未排序数组中累加和为给定值的最长子数组系列问题

来源:互联网 发布:服务器编程项目 编辑:程序博客网 时间:2024/05/16 02:03

牛客网左程云第二课第三题,这是一个很重要的算法原型。

问题:给定一个无序数组 arr,其中元素可正、可负、可 0,给定一个整数 k。求 arr 所有的子数组中累加和为 k 的最长子数组长度。 

要求:时间复杂度 O(N)

分析:本题和未排序正数数组中累加和为给定值的最长子数组长度这个问题的区别在于,数组中的数和给定的k值是任意整数,可以为正、负、零。上一个问题只能是整数。那么我们是否能用上一个问题的解法来解决这个问题呢?答案是否定的,因为它不满足left一直在right的左边这个条件,无法使用双指针的解法。

解法:0到位置i的累加和我们用sum[0~i],存在j使得sum[0~j] - sum[0~i] = k,则k = sum[i+1~j]。我们只有找到满足条件的i最早出现的位置,就可以使得i~j有一个最大值,可以得到满足条件最长的子数组。我们用sum累加数组中的元素,这里我们用一个哈希表来保存最早出现sum-k的值的位置,哈希表的key为sum-k的值,value为其最早出现的位置,哈希表初始时包含key为0,value为-1,这一条记录很重要,因为我们找到的位置是从i+1位置开始的,如果没有这条记录,我们就不能得到从0位置开始的子数组,当sum-k为0时代表我们从0位置到当前位置的值为k,所有我们需要这样一条记录。用len保存满足题意最长的子数组长度。在遍历过程中,sum累加当前值,在哈希表中查找sum-k的值是否存在,若不存在,将sum-k作为key保存在哈希表中,value为当前位置i;若存在,判断当前len的值和当前位置i到sum-k的value的值的大小,若len小则更新len的值为i减去sum-k的value。

public int maxLength(int[] arr, int k) {if (arr == null || arr.length == 0) {return 0;}int len = 0;int sum = 0;HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();map.put(0,-1);// importantfor(int i = 0;i < arr.length;i++){sum += arr[i];if(map.containsKey(sum-k)){len = Math.max(len, i - map.get(sum-k));}else {map.put(sum-k, i);}}return len;}

补充题目1:给定一个无序数组 arr,其中元素可正、可负、可 0。求 arr 所有的子数组中正数与负数个数相等的最长子数组长度。 
要求:时间复杂度 O(N)
分析:这是对上面算法原型的一个应用,我们将数组中的整数变成1,负数变成-1,零仍然为零。然后我们求累加和为0的最长子数组,这样我们就将这个问题转化成未排序数组中累加和为给0的最长子数组问题了。把上面的代码修改一下即可。
补充题目2:给定一个无序数组 arr,其中元素只是 1 或 0。求 arr 所有的子数组中 0 和 1 个 数相等的最长子数组长度。 
要求:时间复杂度 O(N)
分析:和上面的思想类似,我们将数组中的0变成-1,1仍然为1,求累加和为0的最长子数组,我们可以求出1和-1个数相同,代表着0和1个数相同。

0 0
原创粉丝点击