关于数组的一些题目
1、 数组中和为 0 的三个数
-
描述
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a ,b ,c ,使得 a + b + c = 0 ?请找出所有和为 0 且 不重复 的三元组。
-
解法
暴力来说,我们应该对数组使用三重循环,逐一判断。三重循环我们可以简化为一重循环和双指针。对每一个值使用双指针判断它后面是否存在两个值和它的和等于零。可以先进行排序,排完之后,大于 0 的值就可以直接舍弃了。要注意元素相同的情况,比如(-1,0,1,1,1),题目要求不重复的组合,得把这种情况排除。
-
源码
class Solution { public List<List<Integer>> threeSum(int[] nums) { Arrays.sort(nums); List<List<Integer>> res = new ArrayList<>(); int len = nums.length; for(int i=0; i<len; i++){ //大于 0 就不会存在该值了 if(nums[i] > 0) break; //这里是为了防止重复值的出现,不对它进行判断而是直接下一个 if(i>0 && nums[i]==nums[i-1]) continue; int start = i+1; int end = len-1; while(start < end){ int total = nums[i]+nums[start]+nums[end]; if(total == 0) { res.add(Arrays.asList(nums[i],nums[start],nums[end])); //这里不能直接 break; while(start<end && nums[start]==nums[start+1]) start++; start++; } else if(total < 0) start++; else if(total > 0) end--; } } return res; } }
2、和大于等于 target 的最短子数组
-
描述
给定一个含有 n 个正整数的数组和一个正整数 target 。找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
-
解法
寻找数组中满足条件的数据本质还是对数组的遍历,如何遍历满足条件而且更加高效,不同的题目有不同的技巧。对于这个题,对每一个后边界来寻找前边界。我们可以先让前边界不动,移动后边界,满足 > 的条件后,说明对于这个 end 边界存在 start 边界是条件成立,由于要最小值,所以 sum-=nums[start] , start++。
-
源码
class Solution { public int minSubArrayLen(int target, int[] nums) { int start = 0; int end = 0; int res = Integer.MAX_VALUE; int len = nums.length; int sum = 0; while(end<len){ //对于每一个后边界,来寻找合适的前边界,以保证数据遍历完整 sum += nums[end]; while(sum>=target){ sum -= nums[start]; res = Math.min(res,end-start+1); start++; } end++; } return res==Integer.MAX_VALUE ? 0 : res; } }
3、乘积小于 K 的子数组
-
描述
给定一个正整数数组
nums和整数k,请找出该数组内乘积小于k的连续的子数组的个数。 -
解法
这也是一个求区间的问题,寻找还是要遍历。先按照上题的方法来寻找满足条件的区间,即里层的 while 循环,仍是对于 end 边界来寻找 start 边界。找到之后,按理说这个区间里面的所有组合都只满足的,res 应该 += (j-i+1)!,但是区间之间肯定会有重复部分,肯定是小于该值的。 (j-i+1)只是区间内元素的个数.... 不太理解这里
-
源码
class Solution { public int numSubarrayProductLessThanK(int[] nums, int k) { int res = 0; int start = 0; int end = 0; int add = 1; int len = nums.length; while(end < len){ add *= nums[end]; while(start<=end && add>=k){ add /= nums[start]; start++; } res += end-start+1; end++; } return res; } }
4、和为 K 的子数组
-
描述
给定一个整数数组和一个整数
k,请找到该数组中和为k的连续子数组的个数。 -
解法
连续区间的问题很多可以使用前缀和,上面的问题感觉也可以使用。前缀和和 Map 结合有时很有效果。子数组和为 K 就是 prefix[j]-prefix[i] == k 。本解法借助map 来快速寻找 prefix[i] 的出现的次数
-
源码
class Solution { public int subarraySum(int[] nums, int k) { int len = nums.length; int res = 0; int [] prefix = new int[len+1]; Map<Integer,Integer> map = new HashMap<>(); map.put(0,1); for(int i=1; i<=len; i++){ //算出该点的前缀和,并判断 prefix[i]-k 是否在 map 中存储,如果有的话 prefix[i] = prefix[i-1]+nums[i-1]; if(map.containsKey(prefix[i]-k)) res+=map.get(prefix[i]-k); //可能存在 0 ,使得同值的前缀和存在 map.put(prefix[i],map.getOrDefault(prefix[i],0)+1); } return res; } }
5、相同数量的 0和 1 的最长连续子数组
-
描述
给定一个二进制数组
nums, 找到含有相同数量的0和1的最长连续子数组,并返回该子数组的长度。 -
解法
任然是区间,还是可以使用前缀和和Map的组合。但是 0 和 1 的前缀一直加没啥规律,这里用点技巧。遇到 0 时给前缀和 -1,那么当两个下标对应的前缀和相同时,就说明这个区间内的 0 和 1 个数时相同的。要注意的是,当该值在前面出现的时候,为了保证取得的是最长区间,不要将该值加入 map 里面。
-
源码
class Solution { public int findMaxLength(int[] nums) { int len = nums.length; int[] preSum = new int[len+1]; Map<Integer,Integer> map = new HashMap<>(); map.put(0,0); int max = 0; for(int i=1; i<=len; i++){ preSum[i] = preSum[i-1] + (nums[i-1]==0 ? -1 : 1); if(map.containsKey(preSum[i])){ max = Math.max(max,i-map.get(preSum[i])); } //前面没出现才加入map 里面,保证 map 里面存储的下标都是第一次出现的地方 else map.put(preSum[i],i); } return max; } }
6、左右两边的数组值相等
-
描述
给你一个整数数组 nums ,请计算数组的 中心下标 。数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。
-
解法
左右两边仍然是区间,还是可以使用前缀和。只不过这里根据题目要求, preSum*2 + nums[i] == total,来计算判断。
-
源码
class Solution { public int pivotIndex(int[] nums) { int total = Arrays.stream(nums).sum(); int sum = 0; for(int i=0; i<nums.length; i++){ if(2*sum + nums[i] == total){ return i; } //累计前缀和 sum += nums[i]; } return -1; } }
7、二维子矩阵的和
-
描述
给定一个二维矩阵 matrix,以下类型的多个请求:
计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1) ,右下角为 (row2, col2) 。
实现 NumMatrix 类: NumMatrix(int[][] matrix) 给定整数矩阵 matrix 进行初始化int sumRegion(int row1, int col1, int row2, int col2) 返回左上角 (row1, col1) 、右下角 (row2, col2) 的子矩阵的元素总和。
-
解法
关于矩阵的一个题目,要注意的是前缀和
preSum[i+1][j+1]是加到矩阵的matrix[i][j]。因此求sumRegion(int row1, int col1, int row2, int col2)时是用preSum[col2+1][rol+1]的 -
源码
class NumMatrix { private int[][] preSum; public NumMatrix(int[][] matrix) { int m = matrix.length; if(m > 0 ){ this.preSum = new int[m+1][matrix[0].length+1]; for(int i=0; i<m; i++){ for(int j=0; j<matrix[0].length; j++){ preSum[i+1][j+1] = preSum[i][j+1]+preSum[i+1][j]-preSum[i][j]+matrix[i][j]; } } } } public int sumRegion(int row1, int col1, int row2, int col2) { return preSum[row2+1][col2+1] + preSum[row1][col1] - preSum[row1][col2+1]-preSum[row2+1][col1]; } }

浙公网安备 33010602011771号