分治法
分治法的本质:
- 递归:利用递归思想
- 分解:大问题分解成小问题
例题:
- 169 多数元素
- 53 最大子序和
- 215 数组中的第K个最大元素
题型一
难度简单
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋
的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:[3,2,3]
输出:3
示例 2:
输入:[2,2,1,1,1,2,2]
输出:2
进阶:
- 尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。
- 方法:
- 题解:
- 视频:https://www.bilibili.com/video/BV1sy4y1q79M?p=62
- 说明:
- 拆分条件:获取每个区域内的高频值
- 边界条件:下标相同,则到底了,改返回上一个栈结果了
- 代码:
/**
* @param {number[]} nums
* @return {number}
*/
var majorityElement = function(nums) {
return getMajority(nums, 0, nums.length -1)
};
function getMajority(nums, left, right) {
// 如果左边等于右边,说明到了最下面,即只有一个元素的时候,不可拆分了,则返回任意一个即可
if (left === right) return nums[left]
let mid = Math.floor(left + (right - left)/2)
const leftMajority = getMajority(nums, left, mid)
const rightMajority = getMajority(nums, mid + 1, right)
// 如果左右两边的高频值都相等,则返回任意一个高频值即可
if (leftMajority === rightMajority) return leftMajority
let leftCount = 0
let rightCount = 0
for(let i = left; i<= right; i++) {
if (nums[i] === leftMajority) {
leftCount ++
} else if (nums[i] === rightMajority) {
rightCount ++
}
}
return Math.max(leftCount, rightCount) === leftCount ? leftMajority : rightMajority
}
难度简单
给定一个整数数组 nums
,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1]
输出:1
示例 3:
输入:nums = [0]
输出:0
示例 4:
输入:nums = [-1]
输出:-1
示例 5:
输入:nums = [-100000]
输出:-100000
提示:
1 <= nums.length <= 3 * 104
-105 <= nums[i] <= 105
**进阶:**如果你已经实现复杂度为 O(n)
的解法,尝试使用更为精妙的 分治法 求解。
- 方法:
- 题解:https://mp.weixin.qq.com/s/3GAD0a-aI_hQJo7IZMQTlQ
- 视频:https://www.bilibili.com/video/BV1sy4y1q79M?p=63
- 代码:
/**
* // 暴力法,
* // 动态规划法
* // 双指针
* 分治法(递归+拆解)
*
* @param {number[]} nums
* @return {number}
*/
var maxSubArray = function(nums) {
if (!nums.length) return
return getMax(nums, 0 , nums.length - 1 )
};
function getMax(nums, left, right) {
if (left == right) return nums[left]
const mid = Math.floor(left + (right - left) / 2)
let leftMax = getMax(nums, left, mid)
let rightMax = getMax(nums, mid + 1, right)
let crossMAx = getCrossMax(nums, left, right)
return Math.max(leftMax, rightMax, crossMAx)
}
function getCrossMax(nums, l, r) {
const mid = Math.floor(l + (r - l) / 2)
let leftSum = nums[mid];
let leftMax = leftSum;
for (let i = mid - 1; i >= l; i--) {
leftSum += nums[i];
leftMax = Math.max(leftMax, leftSum);
}
// from mid+1 to rightmost
let rightSum = nums[mid+1];
let rightMax = rightSum;
// 这里是 mid + 2,是因为前面 let rightSum = nums[mid+1]; 已经加了一次
for (let i = mid + 2; i <= r; i++) {
rightSum += nums[i];
rightMax = Math.max(rightMax, rightSum);
}
return leftMax + rightMax;
}