天天看点

【算法】解题总结:剑指Offer 30 连续子数组的最大和、剑指Offer 4 重建二叉树

JZ30 连续子数组的最大和

(简单)

题目

描述

输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为 O(n)。

示例

输入:

[1,-2,3,10,-4,7,2,-5]

返回值:

18

说明:

输入的数组为{1,-2,3,10,—4,7,2,一5},和最大的子数组为{3,10,一4,7,2},因此输出为该子数组的和 18。

思路

使用动态规划求解问题,最重要的就是确定从前一个阶段转化到后一个阶段之间的递推关系, 递推关系必须是从次小的问题开始到较大的问题之间的转化,从这个角度来说,动态规划往往可以用递归程序来实现,不过因为递推可以充分利用前面保存的子问题的解来减少重复计算,所以对于大规模问题来说,有递归不可比拟的优势,这也是动态规划算法的核心之处。

因此,此题已明确限定时间复杂度为 O(n),因此我们需要的首要任务是找清楚递推关系公式,同时,因为我们的时间复杂度只允许我们遍历一次数组,因此还需要用一个变量来记录最大的子数组和,而且,由于该方法传入的是引用型的数组变量,因此我们也要拷贝一份此数组,操作拷贝的那份,而不是直接操作传入的那份,如果不这样,那可能会在项目种产生十分严重的故障。而且我们也要考虑数组参数为 null 的情况,这时直接返回和为 0 即可,使程序有较好的健壮性。

我们令拷贝的那份数组的名为 arr ,arr[i](即数组中的第 i 个元素值) 的结果,根据 arr[i - 1] 推出,当 arr[i - 1] 为正数的时候,是可以促进待选子数组和变大的,因此要加上,即 arr[i] += arr[i - 1],若 arr[i - 1] 为负数,则将使得待选子数组和变小,因此不应该加上,应舍弃 [0,i-1] 下标范围内的数组元素,令 arr[i] += 0 即可,也就是从当前数组元素再次作为起始元素开始,并在每次进行上述求 arr[i] 的操作之后,用一个变量 max 来记录下 arr[i] 的最大值即可,这样就可以巧妙高效地选取出,最后变量 max 就正是和最大的子数组元素值的和。

实现

public class JZ30连续子数组的最大和 {
    public int FindGreatestSumOfSubArray(int[] array) {
        if (array == null) {
            return 0;
        }

        int[] arr = new int[array.length];
        System.arraycopy(array, 0, arr, 0, arr.length);

        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            arr[i] += arr[i - 1] > 0 ? arr[i - 1] : 0;
            max = Math.max(max, arr[i]);
        }

        return max;
    }
}      
【算法】解题总结:剑指Offer 30 连续子数组的最大和、剑指Offer 4 重建二叉树

JZ4 重建二叉树

(中等)

题目

描述 给定某二叉树的前序遍历和中序遍历,请重建出该二叉树并返回它的头结点。 例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下图所示。
【算法】解题总结:剑指Offer 30 连续子数组的最大和、剑指Offer 4 重建二叉树
提示: 1.0 <= pre.length <= 2000 2.vin.length == pre.length 3.-10000 <= pre[i], vin[i] <= 10000 4.pre 和 vin 均无重复元素 5.vin出现的元素均出现在 pre里

6.只需要返回根结点,系统会自动输出整颗树做答案对比

示例1

输入:

[1,2,4,7,3,5,6,8],[4,7,2,1,5,3,8,6]

返回值:

{1,2,3,4,#,5,6,#,7,#,#,8}

说明:

返回根节点,系统会输出整颗二叉树对比结果

示例2

输入:

[1],[1]

返回值:

{1}

示例3

输入:

[1,2,3,4,5,6,7],[3,2,4,1,6,5,7]

返回值:

{1,2,5,3,4,6,7}

思路

这次就偷个懒吧,只想写个代码实现,但不想自己再写思路了,碰巧有位大佬写的思路也非常清晰易懂,我对此题也没想到更巧妙的方法。

(​​下图来源​​)

实现

public class JZ4重建二叉树 {
    public TreeNode reConstructBinaryTree(int[] pre,int[] in) {
        if (pre == null || in == null
                        || pre.length == 0 || in.length == 0) {
            return null;
        }

        TreeNode root = new TreeNode(pre[0]);
        for (int i = 0; i < in.length; i++) {
            if (in[i] == pre[0]) {
                root.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i + 1), Arrays.copyOfRange(in, 0, i));
                root.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i + 1, pre.length), Arrays.copyOfRange(in, i + 1, in.length));
                break;
            }
        }

        return root;
    }
}