「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战」
“凡治众如治寡,分数是也” —— 《孙子兵法》
分治算法求解(分而治之的策略)
分解:大规模问题(原问题) --> 分解 --> 小规模问题(子问题 —— 相同,只是规模较原问题小得多)治理:小规模问题之间是相互独立的,互不干扰合并:小规模问题的解(子问题) --> 合并 --> 大规模问题的解(原问题)
分治策略常用场景
合并排序
- 分治算法的一个典型应用和完美体现
- 平衡 + 简单的二分分治策略
- 步骤
- 分解:带排序元素 -> 分解 -> 两个子序列(大小大致相同)-> ... -> 分解至 -> 子序列中只有一个元素(单个元素的子序列是有序的)
- 治理:子序列 -> 合并+排序(递归排序)
- 合并:子序列 -> 合并 -> 有序序列
- 异地排序(合并操作需要通过辅助合并函数完成)—— 先易后难
- 时间复杂度:O(nlogn)
- 空间复杂度:O(n)
- 递归占用的栈空间 === 递归树的深度(logn)
快速排序
- 选取基准元素的方法
- 第一个元素
- 最后一个元素
- 中间位置的元素
- 第一个元素 + 最后一个元素 + 中间位置的元素 => 三者的中位数
- 随机位置的元素
- 步骤
- 分解:小于/等于基准元素的子序列 + 基准元素 + 大于基准元素的子序列
- 治理:对子序列递归快速排序
- 合并:排序后的子序列进行合并 -> 原问题的解
- 原地排序 —— 先难后易
- 最好情况
- (n/2)个元素 — + — 基准元素 — + — (n/2)个元素
- 时间复杂度:O(nlgon)
- 空间复杂度:O(logn)
- 最坏情况
- 划分:基准元素 — + — (n-1)个元素
- 时间复杂度:O(n*n)
- 空间复杂度:O(n)
- 平均情况
- 划分:(k-1)个元素 — + — 基准元素 — + — (n-k)个元素
- 时间复杂度:O(nlogn)
- 空间复杂度:O(nlogn)
- 优化方案
- 从右向左找小于基准元素的数
r[j] - 从左向右找大于基准元素的数
r[i] - 将找到的两个元素交换位置 -> 至 -> i === j
- 基准元素于较大的元素交换位置
int point(int r[], int min, int max) { // 优化划分方案 int i = min, j = max, mid = r[min]; // 找基准元素 while (i < j) { while(i < j && r[j] > mid) j--; // 从右向左 while(i < j && r[i] < mid) i++; // 从左向右 if (i < j) swap(r[i++], r[j--]); // 交换位置 } if (r[i] > mid) { swap(r[i-1], r[min]); // 交换位置 return i-1; // 返回新的基准值的位置 } swap(r[i], r[min]); // 交换位置 return i // 返回新基准元素的位置 } - 从右向左找小于基准元素的数