20.字数组最大累加和问题
子数组最大累加和问题是一个非常经典的问题,也比较简单
但是 扩展出的问题很多,在笔试、面试中特别常见
扩展出的问题很多非常有趣,解法也比较巧妙
53. 最大子数组和
已解答
中等
给你一个整数数组 nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组
是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4] 输出:6 解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
public static int maxSubArray(int[] nums) { int n = nums.length; int ans = nums[0]; for (int i = 1, pre = nums[0]; i < n; i++) { pre = Math.max(nums[i], pre + nums[i]); ans = Math.max(ans, pre); } return ans; }
198. 打家劫舍
已解答
中等
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1] 输出:4 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。
public static int rob(int[] nums) { int n = nums.length; if (n == 1) { return nums[0]; } if (n == 2) { return Math.max(nums[0], nums[1]); } int prepre = nums[0]; int pre = Math.max(nums[0], nums[1]); for (int i = 2, cur; i < n; i++) { cur = Math.max(pre, Math.max(nums[i], prepre + nums[i])); prepre = pre; pre = cur; } return pre; }
213. 打家劫舍 II
已解答
中等
提示
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
示例 1:
输入:nums = [2,3,2] 输出:3 解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
//成换无非就是 丢弃索引 0 位置的数取索引 n的数 才能成环 //不成环就是取索引 0 的数丢弃索引 n-1的数 public int rob(int[] nums) { int n = nums.length; if(n==1){ return nums[0]; }else if(n==2){ return Math.max(nums[0],nums[1]); } int prepre = nums[0]; int pre = Math.max(nums[0],nums[1]); int max1 = Math.max(pre,prepre); for(int i = 2;i<n-1;i++){ max1 = Math.max(prepre+nums[i],pre); prepre = pre; pre = max1; } prepre = nums[1]; pre = Math.max(nums[2],nums[1]); int max2 = Math.max(pre,prepre); for(int i = 3;i<n;i++){ max2 = Math.max(prepre+nums[i],pre); prepre = pre; pre = max2; } return Math.max(max1,max2); }
2560. 打家劫舍 IV
已解答
中等
提示
沿街有一排连续的房屋。每间房屋内都藏有一定的现金。现在有一位小偷计划从这些房屋中窃取现金。
由于相邻的房屋装有相互连通的防盗系统,所以小偷 不会窃取相邻的房屋 。
小偷的 窃取能力 定义为他在窃取过程中能从单间房屋中窃取的 最大金额 。
给你一个整数数组 nums
表示每间房屋存放的现金金额。形式上,从左起第 i
间房屋中放有 nums[i]
美元。
另给你一个整数 k
,表示窃贼将会窃取的 最少 房屋数。小偷总能窃取至少 k
间房屋。
返回小偷的 最小 窃取能力。
示例 1:
输入:nums = [2,3,5,9], k = 2 输出:5 解释: 小偷窃取至少 2 间房屋,共有 3 种方式: - 窃取下标 0 和 2 处的房屋,窃取能力为 max(nums[0], nums[2]) = 5 。 - 窃取下标 0 和 3 处的房屋,窃取能力为 max(nums[0], nums[3]) = 9 。 - 窃取下标 1 和 3 处的房屋,窃取能力为 max(nums[1], nums[3]) = 9 。 因此,返回 min(5, 9, 9) = 5 。
public static int[] findOrder(int numCourses, int[][] prerequisites) { List<List<Integer>> list = new ArrayList<>(); for (int i=0;i<numCourses;i++){ list.add(new ArrayList<>()); } //入度表 int [] indegree= new int[numCourses]; for (int i=0;i< prerequisites.length;i++){ list.get(prerequisites[i][1]).add(prerequisites[i][0]); indegree[prerequisites[i][0]]++; } Deque<Integer> deque = new LinkedList<>(); int where=0; int [] ans = new int [numCourses]; for (int i=0;i<numCourses;i++){ if (indegree[i]==0){ deque.offer(i); } } while (!deque.isEmpty()){ Integer first = deque.getFirst(); ans[where++] = first; for (int q:list.get(first)){ if (--indegree[q]==0){ deque.addLast(q); } } deque.removeFirst(); } if (where<numCourses){ return new int [0]; } return ans; }
918. 环形子数组的最大和
已解答
中等
提示
给定一个长度为 n
的环形整数数组 nums
,返回 nums
的非空 子数组 的最大可能和 。
环形数组 意味着数组的末端将会与开头相连呈环状。形式上, nums[i]
的下一个元素是 nums[(i + 1) % n]
, nums[i]
的前一个元素是 nums[(i - 1 + n) % n]
。
子数组 最多只能包含固定缓冲区 nums
中的每个元素一次。形式上,对于子数组 nums[i], nums[i + 1], ..., nums[j]
,不存在 i <= k1, k2 <= j
其中 k1 % n == k2 % n
。
示例 1:
输入:nums = [1,-2,3,-2] 输出:3 解释:从子数组 [3] 得到最大和 3
public int maxSubarraySumCircular(int[] nums) { //两种情况 就是最大的值是首尾连接的情况,第二种就是最大值不是首尾连接 int max1 = nums[0]; //记录字数组的最小值,字数组总和减去最小值就是首尾相连的最大值 int max2 = nums[0]; //记录字数组的最大值 //记录总和 int allsum = nums[0]; int pre1= nums[0]; int pre2 = nums[0]; if(nums.length==1){ return nums[0]; } for(int i = 1;i<nums.length;i++){ allsum+=nums[i]; //先更新最大值 if (pre1>0){ pre1 = nums[i]+pre1; }else { pre1 = nums[i]; } max2 = Math.max(pre1,max2); //更新最小值 if (pre2<0){ pre2 += nums[i]; }else { pre2 = nums[i]; } max1 = Math.min(pre2,max1); } //考虑到可能出现全是负数的情况,所以需要判断一下 return allsum==max1 ? max2:Math.max(allsum-max1,max2); }
面试题 17.24. 最大子矩阵
困难
提示
给定一个正整数、负整数和 0 组成的 N × M 矩阵,编写代码找出元素总和最大的子矩阵。
返回一个数组 [r1, c1, r2, c2]
,其中 r1
, c1
分别代表子矩阵左上角的行号和列号,r2
, c2
分别代表右下角的行号和列号。若有多个满足条件的子矩阵,返回任意一个均可。
注意:本题相对书上原题稍作改动
示例:
输入: [ [-1,0], [0,-1] ] 输出:[0,1,0,1] 解释:输入中标粗的元素即为输出所表示的矩阵
说明:
-
1 <= matrix.length, matrix[0].length <= 200
-
public static int[] getMaxMatrix(int[][] matrix) {
int a = 0,b = 0,c = 0,d = 0;
int n = matrix.length;
int m = matrix[0].length;
//大思路 从第一行 开始,考虑1-1,1-2....1-n组成的矩阵中最大的
//再从第二行开始 2-2,2-3...2-n;一直到 n-n,能够包含所有的矩阵//使用arr作为压缩数组
int [] arr = new int[m];
int max = Integer.MIN_VALUE;
for (int up = 0;up<n;up++){
Arrays.fill(arr,0);
for (int down = up;down<n;down++){
for (int i = 0;i<m;i++){
arr[i] += matrix[down][i];
}
//数组压缩完毕
int l = 0;//记录数组的最左边
//找到数组中最大的字数组
int pre = Integer.MIN_VALUE;
for (int i = 0;i<m;i++){
if (pre>0){
pre+=arr[i];
}else {
pre = arr[i];
l = i;
}
if (pre>max){
max = pre;
a = up;
b = l;
c = down;
d = i;
}
}
}
}
return new int[] {a,b,c,d};
}