分治算法,可以认为是一种算法思想,通过将原问题分解成小规模的子问题,然后根据子问题的结果构造出原问题的答案。这里有点类似动态规划,所以说运用分治算法也需要满足一些条件,你的原问题结果应该可以通过合并子问题结果来计算。
贪心算法可以理解为一种特殊的动态规划问题,拥有一些特殊的性质,可以进一步降低动态规划的时间复杂度。
贪心算法
贪心算法比动态规划多了一个性质:贪心选择性质。我们不需要递归地计算出所有选择的具体结果然后比较求最值,而只需要做出那个最有潜力,看起来最优的选择即可。
贪心算法举例:334. 递增的三元子序列
可以使用谈心的方法将空间复杂度降到O(1),从左到右遍历数组,遍历过程中维护两个变量first和second,分别表示递增的三元子序列中的第一个数和第二个数,任何时候都有first < second 初始时,first = nums[0], second = 正无穷。对于 1<=i<n,当遍历到下标i时,令num = nums[i],进行如下操作: 1,如果num > second,则找到了一个递增的三元子序列,返回true 2,否则如果num > first,则将second的值更新为num 3,否则,将first的值更新为num 如果遍历结束时没有找到递增的三元子序列,返回false。 上述做法的贪心思想是:为了找到递增的三元子序列,first\textit{first}first 和 second\textit{second}second 应该尽可能地小,此时找到递增的三元子序列的可能性更大。
class Solution {
func increasingTriplet(_ nums: [Int]) -> Bool {
let n = nums.count
if n < 3 {
return false
}
var first = nums[0]
var second = Int.max
for i in 1..<n {
let num = nums[i]
if num > second {
return true
}else if num > first {
second = num
}else {
first = num
}
}
return false
}
}
其他经典贪心算法题:55. 跳跃游戏、45. 跳跃游戏 II、134. 加油站、1024. 视频拼接
分治算法
最典型的分治算法就是归并排序了,核心逻辑如下:
void sort(int[] nums, int lo, int hi) {
int mid = (lo + hi) / 2;
/****** 分 ******/
// 对数组的两部分分别排序
sort(nums, lo, mid);
sort(nums, mid + 1, hi);
/****** 治 ******/
// 合并两个排好序的子数组
merge(nums, lo, mid, hi);
}
核心思想就是只要我先把数组的左半部分排序,再把右半部分排序,最后把两部分合并,不就是对整个数组排序了么。先分,然后再治。 其他经典分治算法题:241. 为运算表达式设计优先级、105. 从前序与中序遍历序列构造二叉树