原理

  • 将问题拆分为几个子问题,而且这些子问题和原问题相似只是量级上小一些
  • 递归地解决每一个子问题,然后结合这些子问题的解决方案构造出原问题的解决方案

分治法 = 分 + 治

  1. 归并排序
  2. 快速排序

题目

1. 快速指数

计算分治法 - 图1
https://leetcode-cn.com/problems/powx-n/

  1. def fast_power(x, n):
  2. if n == 0:
  3. return 1
  4. elif n < 0:
  5. return 1 / fast_power(x, -n)
  6. elif n % 2:
  7. return fast_power(x * x, n // 2) * x
  8. else:
  9. return fast_power(x * x, n // 2)
  1. class Solution:
  2. def myPow(self, x: float, n: int) -> float:
  3. if n == 0:
  4. return 1
  5. if n < 0:
  6. return 1 / self.myPow(x, -n)
  7. if n == 1:
  8. return x
  9. ans = self.myPow(x, n >> 1)
  10. ans *= ans
  11. if n & 1 == 1:
  12. # 奇数
  13. ans *= x
  14. # print(ans)
  15. return ans

2. 搜索峰值

  • 数组没有重复值,但可能存在多个峰值,返回任意一个峰值的index.
  • 假设num[-1] = num[n] = -∞

https://leetcode-cn.com/problems/find-peak-element/

顺序遍历一遍:nums[i - 1] < num[i] < num[i + 1], 最大时间复杂度O(n)

优化,O(logn)。二分法
image.png

3. 查找中值/查找第k个元素⭐⭐⭐⭐⭐

  • 排序,选择第k个,时间复杂度O(nlogn)
  • O(n)的解法。参考快速排序

4. 两数组交集

  • 给2个大小不一的数组,找出这两个数组的交集
  • 输出中不能有重复
  1. set.intersection(set1, set2 … etc) intersection() 方法用于返回两个或更多集合中都包含的元素,即交集。
  2. 两层循环,O(n^2)
  3. 查找时不用in,用二分查找。(m + n) logn
  4. 两个数组都排序,然后维护两个指针i,j。双指针

5. 两数组交集 II

  • 给2个大小不一的数组,找出这两个数组的交集
  • 输出中能有重复
  • Given nums1=[1, 2, 2, 1],nums2=[2, 2],return [2, 2]
  1. 排序,双指针

暴力破解:两个循环,找出所有的序列对。O(n^2)
进一步优化,考虑O(nlgn)。
分治法

  1. def merge(left, right):
  2. result = list()
  3. i, j = 0, 0
  4. inv_count = 0
  5. while i < len(left) and j < len(right):
  6. if left[i] < right[i]:
  7. result.append(left[i])
  8. i += 1
  9. elif right[j] < left[i]:
  10. result.append(right[j])
  11. j += 1
  12. inv_count += (len(left) - i)
  13. result += left[i:]
  14. result += right[j:]
  15. return result, inv_count
  16. def countInv(array):
  17. if len(array) < 2:
  18. return array, 0
  19. middle = len(array) // 2
  20. left, inv_left = countInv(array[:middle])
  21. right, inv_right = countInv(array[middle:])
  22. merged, count = merge(left, right)
  23. count += (inv_left + inv_right)
  24. return merged, count

7. 在已排序数组中找到多余元素的索引

给定两个排好序的数组。这两个数组只有一个不同的地方:在第一个数组某个位置上多一个元素。
请找到这个元素的索引位置。

  • 暴力,O(n^2)
  • 遍历当前元素 + 在另外一个数组使用二分查找。 O(nlgn)
  • 双指针。 O(n)
  • 思考,如何O(lgn)? 二分查找。

8. 在一个一维数组中找到连续的子序列,且这个子序列的加和值最大。

https://leetcode-cn.com/problems/maximum-subarray/
image.png

  • 暴力,O(n^2)
  • 暴力,对加法进行优化。O(n^2),边找边加
  • DP。思考当前元素可能出现在连续子序列中吗?O(n)

    1. class Solution:
    2. def maxSubArray(self, nums: List[int]) -> int:
    3. localMax, globalMax = nums[0], nums[0]
    4. for num in nums[1:]:
    5. if localMax <= 0:
    6. localMax = num
    7. else:
    8. localMax += num
    9. globalMax = max(globalMax, localMax)
    10. # print(globalMax)
    11. return globalMax
  • 分治法。

分成两半,找出左边最大值,找打右边最大值。当时不意味着找到全局最大值。
image.png

9. 快速整数乘法

https://itimetraveler.github.io/2017/08/22/%E3%80%90%E7%AE%97%E6%B3%95%E3%80%91%E5%A4%A7%E6%95%B0%E7%9B%B8%E4%B9%98%E9%97%AE%E9%A2%98%E5%8F%8A%E5%85%B6%E9%AB%98%E6%95%88%E7%AE%97%E6%B3%95/

10. 对于多项式乘法的快速傅里叶变换

11. 矩阵乘法

12. 练习水槽

image.png

数学的方式思考,列出表达式,然后使用二分查找确定二元一次方程的根

13.奇-偶数换序问题

image.png
image.png

进一步优化,nlog(N),分治分治分治
image.png
image.png

image.png

递归有三种:分别时将递归放在头、尾和中间。头递归、尾递归和中间递归。

14.用最少步数收集所有硬币
image.pngimage.png
找到高度中的最小值minimum,这个最小值意味着当横着拿的时候,拿完minimum个数之后,数组被分成了两个部分;
[L, minimum], [minimum, R]
考虑竖着拿的情况。
image.png

15.
image.png