算法通关之路:一文吃透Kadane算法(C++实现)
在算法面试和日常刷题中,最大子数组和(Maximum Subarray) 是一道经典必考题。暴力枚举所有子数组的解法时间复杂度高达 O(n²),而今天要讲的 Kadane算法(卡登算法) 能以 O(n) 时间复杂度 + O(1) 空间复杂度 完美解决这个问题,简洁又高效。
今天就用博客的形式,带你从原理到C++代码,彻底掌握Kadane算法!
一、问题背景
先明确问题:给定一个整数数组(包含正数、负数、0),找到一个连续的子数组,使其元素之和最大,返回这个最大和。
示例:
- 输入:
nums = [-2,1,-3,4,-1,2,1,-5,4] - 输出:
6(对应子数组[4,-1,2,1])
暴力思路:遍历所有起点和终点,计算子数组和取最大值。但数组长度一大就会超时,这时候Kadane算法就派上用场了。
二、Kadane算法核心原理
Kadane算法的核心思想只有一句话:****每一步都做出当前最优选择,最终得到全局最优解(贪心思想)。
我们遍历数组时,维护两个关键变量:
- 当前最大和(current_max) :以当前元素为结尾的连续子数组的最大和。
- 全局最大和(global_max):遍历过程中所有子数组的最大和(最终答案)。
核心决策逻辑:
对于数组中的每一个元素 nums[i],我们只有两种选择:
- 把
nums[i]加入前面的子数组 →current_max + nums[i] - 以
nums[i]作为新子数组的起点 →nums[i]
我们取两者的最大值 作为新的 current_max,保证每一步都是当前最优。
同时,每次更新 current_max 后,用它更新 global_max,记录全局最优。
举个例子理解(数组 [-2,1,-3,4]):
- 初始:
current_max=-2,global_max=-2 - 元素1:max(1, -2+1)=1 →
current_max=1,global_max=1 - 元素-3:max(-3, 1+(-3))=-2 →
current_max=-2,global_max=1 - 元素4:max(4, -2+4)=4 →
current_max=4,global_max=4
三、算法步骤(清晰版)
- 初始化
current_max和global_max为数组第一个元素。 - 从数组第二个元素 开始遍历:
- 更新
current_max = max(nums[i], current_max + nums[i]) - 更新
global_max = max(global_max, current_max)
- 更新
- 遍历结束,
global_max就是答案。
四、C++ 代码实现
1. 基础版(最简洁,面试首选)
直接实现核心逻辑,无多余操作,时间O(n),空间O(1):
cpp
#include <iostream>
#include <vector>
#include <algorithm> // 用于max函数
using namespace std;
// Kadane算法:求最大子数组和
int maxSubArray(vector<int>& nums) {
// 边界判断:数组为空返回0(根据题目要求调整)
if (nums.empty()) return 0;
// 初始化当前最大和、全局最大和为第一个元素
int current_max = nums[0];
int global_max = nums[0];
// 从第二个元素开始遍历
for (int i = 1; i < nums.size(); ++i) {
// 核心:当前最优选择
current_max = max(nums[i], current_max + nums[i]);
// 更新全局最优
global_max = max(global_max, current_max);
}
return global_max;
}
// 测试代码
int main() {
vector<int> nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
cout << "最大子数组和:" << maxSubArray(nums) << endl; // 输出6
return 0;
}
2. 进阶版:记录最大子数组的下标
如果题目不仅要求最大和,还要求输出子数组的起始、结束下标,可以在基础版上稍作修改:
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int maxSubArray(vector<int>& nums, int& start, int& end) {
if (nums.empty()) return 0;
int current_max = nums[0];
int global_max = nums[0];
int temp_start = 0; // 临时记录当前子数组的起点
for (int i = 1; i < nums.size(); ++i) {
// 选择重新开始:更新临时起点
if (nums[i] > current_max + nums[i]) {
current_max = nums[i];
temp_start = i;
} else {
// 选择加入前面的子数组
current_max += nums[i];
}
// 更新全局最大和,记录最终起止下标
if (current_max > global_max) {
global_max = current_max;
start = temp_start;
end = i;
}
}
return global_max;
}
// 测试代码
int main() {
vector<int> nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
int start = 0, end = 0;
int max_sum = maxSubArray(nums, start, end);
cout << "最大子数组和:" << max_sum << endl;
cout << "子数组下标:[" << start << ", " << end << "]" << endl;
cout << "子数组:";
for (int i = start; i <= end; ++i) {
cout << nums[i] << " ";
}
return 0;
}
输出结果:
最大子数组和:6
子数组下标:[3, 6]
子数组:4 -1 2 1
五、关键细节说明
- 全负数数组 :Kadane算法依然有效!比如数组
[-5,-3,-2,-1],算法会返回最大的单个元素-1(符合题意,因为子数组至少包含一个元素)。 - 空间复杂度 :基础版只用到两个变量,是最优空间复杂度,无需额外开辟数组。
- 适用场景 :除了经典的最大子数组和,还能解决最大子矩阵和 (二维Kadane)、环形数组最大子数组和等变形题。
六、算法复杂度分析
- 时间复杂度:O(n) ------ 只遍历数组一次,无嵌套循环。
- 空间复杂度:O(1) ------ 仅使用常数个变量,不随数组长度变化。
这也是Kadane算法成为最优解的原因!
七、总结
Kadane算法是贪心思想 的经典应用,核心就是每一步只做最优选择:
- 要么延续前面的子数组,要么重新开始新子数组。
- 时间O(n) + 空间O(1),是最大子数组和问题的最优解法。
- C++实现简洁,面试手写无压力,还能轻松扩展记录子数组下标。
掌握这个算法,再也不怕最大子数组和相关的面试题啦!
核心要点回顾
- 核心思想:贪心,每一步取当前最优(延续/重启子数组)
- 关键变量 :
current_max(当前结尾最大和)、global_max(全局最大和) - 复杂度:O(n)时间,O(1)空间
- 适用场景:最大子数组和及各类变形题