53. 最大子数组和 - 力扣(LeetCode)(字节真题)
给你一个整数数组 nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组
是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4] 输出:6 解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1] 输出:1
示例 3:
输入:nums = [5,4,-1,7,8] 输出:23
class Solution {
public int maxSubArray(int[] nums) {
int CurSum = nums[0];
int MaxSum = nums[0];
for (int i = 1; i < nums.length; i++){
//更新当前子数组和CurSum
CurSum = Math.max(CurSum+nums[i], nums[i]);
//更新最大子数组和MaxSum
MaxSum = Math.max(CurSum, MaxSum);
}
return MaxSum;
}
}
Kadane算法:
是一种用于解决最大子数组和问题的动态规划算法。这类问题的目标是在给定整数数组中找到一个连续的子数组,使其元素之和最大(数组含有负数)。
算法的核心思想是通过迭代数组的每个元素,维护两个变量来跟踪局部最优解和全局最优解(其实就是通过两个最值来求解问题)。
它能够在线性时间内找到具有最大和的连续子数组,并且相对于其他可能的暴力解法来说,Kadane算法非常简洁和高效。下面是Kadane算法的详细解释:
Kadane算法步骤:
-
初始化:
- 设两个变量
currentSum
和maxSum
。 currentSum
用于记录当前子数组的和,初始化为数组的第一个元素。maxSum
用于记录迄今为止找到的最大子数组的和,初始化为数组的第一个元素。
- 设两个变量
-
遍历数组:
- 从第二个元素开始遍历数组。
- 对于每一个元素,更新
currentSum
为currentSum + 当前元素
与当前元素
之间的较大值。这一步决定是否继续当前的子数组还是从当前元素开始一个新的子数组。 - 更新
maxSum
为maxSum
与currentSum
之间的较大值。这样可以保证maxSum
始终是最大的子数组和。
121. 买卖股票的最佳时机 - 力扣(LeetCode)(用Kadane算法解决)
给定一个数组 prices
,它的第 i
个元素 prices[i]
表示一支给定股票第 i
天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0
。
示例 1:
输入:[7,1,5,3,6,4] 输出:5 解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
输入:prices = [7,6,4,3,1] 输出:0 解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
class Solution {
public int maxProfit(int[] prices) {
/*就是找到最小值,让其之后的最大值减去该最小值
Kadane算法:
Cur记录当前可以买入的最低点
MaxProfit记录最大的利润
*/
int Cur = prices[0];
int MaxProfit = 0;
for (int i = 1; i < prices.length; i++){
//CurProfit记录当前股票的利润
//若prices[i] < Cur,则从prices[i]处买入
if (prices[i] < Cur){
Cur = prices[i];
}
else{
//更新最大利润
MaxProfit = Math.max(prices[i]-Cur, MaxProfit);
}
}
return MaxProfit;
}
}
其他用到Kadane算法的题:
152. 乘积最大子数组 - 力扣(LeetCode)
class Solution {
public int maxProduct(int[] nums) {
int maxProduct = nums[0];
int minProduct = nums[0];
int result = nums[0];
for (int i = 1; i < nums.length; i++) {
if (nums[i] < 0) {
// 交换maxProduct和minProduct
int temp = maxProduct;
maxProduct = minProduct;
minProduct = temp;
}
maxProduct = Math.max(nums[i], maxProduct * nums[i]);
minProduct = Math.min(nums[i], minProduct * nums[i]);
result = Math.max(result, maxProduct);
}
return result;
}
}
918. 环形子数组的最大和 - 力扣(LeetCode)
class Solution {
public int maxSubarraySumCircular(int[] nums) {
int totalSum = 0;
int maxSum = Integer.MIN_VALUE;
int currentMax = 0;
int minSum = Integer.MAX_VALUE;
int currentMin = 0;
for (int num : nums) {
totalSum += num;
currentMax = Math.max(currentMax + num, num);
maxSum = Math.max(maxSum, currentMax);
currentMin = Math.min(currentMin + num, num);
minSum = Math.min(minSum, currentMin);
}
if (maxSum > 0) {
return Math.max(maxSum, totalSum - minSum);
} else {
return maxSum;
}
}
}
221. 最大正方形 - 力扣(LeetCode)
class Solution {
public int maximalSquare(char[][] matrix) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) return 0;
int rows = matrix.length;
int cols = matrix[0].length;
int maxSide = 0;
int[][] dp = new int[rows + 1][cols + 1];
for (int i = 1; i <= rows; i++) {
for (int j = 1; j <= cols; j++) {
if (matrix[i - 1][j - 1] == '1') {
dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
maxSide = Math.max(maxSide, dp[i][j]);
}
}
}
return maxSide * maxSide;
}
}
———————————————————————————————————————————
56. 合并区间 - 力扣(LeetCode)(不太会,做得磕磕绊绊的)
class Solution {
public int[][] merge(int[][] intervals) {
/*
重叠:数组2的左边界<=数组1的右边届,则数组2可以覆盖数组1
新的数组=[数组1的左边界,数组2的右边届]
*/
//result存储结果,LinkedList方便插入
LinkedList<int[]> result = new LinkedList();
//先排序,将左边界按升序排序
Arrays.sort(intervals, (a,b) -> Integer.compare(a[0],b[0]));
//遍历intervals
//intervals[i][0]-左边界;intervals[i][1]-右边界
for (int i=0; i < intervals.length; i++){
//初始化左右边界
int L = intervals[i][0];
int R = intervals[i][1];
//若满足合并条件,更新右边届
if (result.size()==0 || result.get(result.size()-1)[1] < L){
result.add (new int[]{L,R});
}
else {
result.get(result.size()-1)[1] = Math.max(R, result.get(result.size()-1)[1]);
}
}
//最开始为了插入方便,将result初始化为链表,最后返回时要转为Array
return result.toArray(new int[result.size()][]);
}
}
-
Integer.compare(a[0], b[0])
:Integer.compare
是 Java 中的一个静态方法,用于比较两个整数的大小。a
和b
都是intervals
数组中的元素,每个元素都是一个长度为 2 的整数数组(表示一个区间)。a[0]
和b[0]
分别是两个区间的起始位置。- 如果
a[0]
小于b[0]
,Integer.compare(a[0], b[0])
返回负值; - 如果
a[0]
等于b[0]
,Integer.compare(a[0], b[0])
返回 0; - 如果
a[0]
大于b[0]
,Integer.compare(a[0], b[0])
返回正值。
只需要和result中的最后一个区间比较即可(result中存储的都是合并好的区间,由于intervals是按左边界升序排的,只需要判断result中最后一个区间的右边届是否小于当前的左边界)
result.get(result.size()-1)[1]
的作用是获取result
中最后一个区间的右边界。- 在合并区间时,需要检查当前区间和
result
中最后一个区间是否有重叠,并根据情况更新最后一个区间的右边界。
189. 轮转数组 - 力扣(LeetCode)
给定一个整数数组 nums
,将数组中的元素向右轮转 k
个位置,其中 k
是非负数。
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3 输出:[5,6,7,1,2,3,4]
解释: 向右轮转 1 步:[7,1,2,3,4,5,6]
向右轮转 2 步:[6,7,1,2,3,4,5]
向右轮转 3 步:[5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2 输出:[3,99,-1,-100] 解释: 向右轮转 1 步: [99,-1,-100,3] 向右轮转 2 步: [3,99,-1,-100]
直接在草稿本上代入示例1,推算一下,index = (i+k)%n
class Solution {
public void rotate(int[] nums, int k) {
int n = nums.length;
int[] result = new int[n];
for (int i=0; i<n; i++){
int index = (i+k)%n;
result[index] = nums[i];
}
for (int i = 0; i < n; i++) {
nums[i] = result[i];
}
}
}
238. 除自身以外数组的乘积 - 力扣(LeetCode)(双指针法-分别求前缀积和后缀积)
给你一个整数数组 nums
,返回 数组 answer
,其中 answer[i]
等于 nums
中除 nums[i]
之外其余各元素的乘积 。
题目数据 保证 数组 nums
之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请 不要使用除法,且在 O(n)
时间复杂度内完成此题。
示例 1:
输入: nums =[1,2,3,4]
输出:[24,12,8,6]
示例 2:
输入: nums = [-1,1,0,-3,3] 输出: [0,0,9,0,0]
class Solution {
public int[] productExceptSelf(int[] nums) {
//双指针,分别求出前缀积和后缀积
/*
前缀积是指从数组开始位置到当前元素(不包括当前元素)的所有元素的乘积。
举例来说,对于数组 nums = [1, 2, 3, 4]:
第0个元素的前缀积是1(因为前面没有元素)。
第1个元素的前缀积是 1(即 nums[0])。
第2个元素的前缀积是 1 * 2 = 2(即 nums[0] * nums[1])。
第3个元素的前缀积是 1 * 2 * 3 = 6(即 nums[0] * nums[1] * nums[2])。
后缀积是指从数组末尾到当前元素(不包括当前元素)的所有元素的乘积。
同样地,对于数组 nums = [1, 2, 3, 4]:
第3个元素的后缀积是1(因为后面没有元素)。
第2个元素的后缀积是 4(即 nums[3])。
第1个元素的后缀积是 3 * 4 = 12(即 nums[2] * nums[3])。
第0个元素的后缀积是 2 * 3 * 4 = 24(即 nums[1] * nums[2] * nums[3])。
*/
//前缀积和后缀积初始化
int brefore = 1;
int after = 1;
//answer初始化,内容都是1
int[] answer = new int[nums.length];
Arrays.fill(answer,1);
//求前缀积
for (int i = 0; i<nums.length; i++){
answer[i] *= brefore;
brefore *= nums[i];
}
//求后缀积
for (int i = nums.length-1; i>=0;i--){
answer[i] *= after;
after *= nums[i];
}
return answer;
}
}