算法高频题-动态规划
-
刷题:LeetCode(Top 100-150题,至少刷两遍)。重点:链表、树、二分查找、动态规划、回溯、栈/队列。
-
每一个题型,前10个高频题
算法思考框架参考:算法题思维框架-CSDN博客
高频顺序参考网站:CodeTop 面试题目总结
1.53. 最大子数组和
这道题是动态规划的经典问题,主要有三种方法:
-
Kadane算法(动态规划):最优解,O(n)时间复杂度
-
分治法:O(n log n)时间复杂度
-
暴力法:O(n²)时间复杂度(不推荐)
方法一:Kadane算法(动态规划)
class Solution {
public:int maxSubArray(vector<int>& nums) {if (nums.empty()) return 0;int maxSum = nums[0]; // 全局最大和int currentSum = nums[0]; // 当前子数组和for (int i = 1; i < nums.size(); i++) {// 对于每个元素,选择:要么加入当前子数组,要么重新开始currentSum = max(nums[i], currentSum + nums[i]);// 更新全局最大和maxSum = max(maxSum, currentSum);}return maxSum;}
};
方法二:分治法
class Solution {
public:int maxSubArray(vector<int>& nums) {return divideAndConquer(nums, 0, nums.size() - 1);}private:int divideAndConquer(vector<int>& nums, int left, int right) {if (left == right) return nums[left];int mid = left + (right - left) / 2;// 分别求左右两边的最大子数组和int leftMax = divideAndConquer(nums, left, mid);int rightMax = divideAndConquer(nums, mid + 1, right);// 求跨越中点的最大子数组和int crossMax = maxCrossingSum(nums, left, mid, right);return max({leftMax, rightMax, crossMax});}int maxCrossingSum(vector<int>& nums, int left, int mid, int right) {// 从中点向左的最大和int leftSum = INT_MIN;int sum = 0;for (int i = mid; i >= left; i--) {sum += nums[i];leftSum = max(leftSum, sum);}// 从中点向右的最大和int rightSum = INT_MIN;sum = 0;for (int i = mid + 1; i <= right; i++) {sum += nums[i];rightSum = max(rightSum, sum);}return leftSum + rightSum;}
};
方法三:动态规划(显式DP数组)
class Solution {
public:int maxSubArray(vector<int>& nums) {if (nums.empty()) return 0;int n = nums.size();vector<int> dp(n); // dp[i]表示以nums[i]结尾的最大子数组和dp[0] = nums[0];int maxSum = dp[0];for (int i = 1; i < n; i++) {dp[i] = max(nums[i], dp[i - 1] + nums[i]);maxSum = max(maxSum, dp[i]);}return maxSum;}
};
-
时间复杂度:
-
Kadane算法:O(n)
-
分治法:O(n log n)
-
暴力法:O(n²)
-
-
空间复杂度:
-
Kadane算法:O(1)
-
分治法:O(log n)(递归栈)
-
显式DP:O(n)
-
-
"为什么Kadane算法有效?"
-
基于贪心思想:如果当前子数组和为负数,它对后续子数组没有贡献
-
可以重新开始计算子数组和
-
-
"如何修改算法来返回最大子数组的起止位置?"
class Solution { public:vector<int> maxSubArrayWithIndices(vector<int>& nums) {if (nums.empty()) return {0, 0, 0};int maxSum = nums[0];int currentSum = nums[0];int start = 0, end = 0;int tempStart = 0;for (int i = 1; i < nums.size(); i++) {if (nums[i] > currentSum + nums[i]) {currentSum = nums[i];tempStart = i;} else {currentSum += nums[i];}if (currentSum > maxSum) {maxSum = currentSum;start = tempStart;end = i;}}return {maxSum, start, end};} };
-
"如果要求子数组长度至少为k怎么办?"
需要使用滑动窗口或前缀和等更复杂的方法