什么是分支法
求解一个大问题时,将它分成若干个更易求解 k 个的小问题。小问题相互独立且与原问题形式相同。递归求解小问题,将所有小问题的解合并得到原问题的解。
二分法
分解问题时,k 取2,称二分法。
这是一种平衡子问题的思想。k 取2使子问题规模大致相等。

算法框架
divide-and-conquer(P){ //P 为原问题
if |P| <= n{ //如果问题 P 的规模小于 n,则求解它
return adhoc(P);
}
else { //将 P 分解为 k 个更小的子问题
for(i = 1; i <= k; i++) //循环 k 次
yi = divde-and-conquer(Pi); //递归解 Pi
return merge(y1, y2, ···,yk); //合并子问题
}
}
分治法应用
快速排序
思想
一组待排序列,选择一个元素作为基准。基准左侧是小于它的元素,基准右侧是大于它的元素。
对这两个子序列分别重复上述过程,直至每个序列只有一个元素或序列为空。
分治策略
- 分解
原序列 a[s,···,t] 分解为 a[s,···,i-1] 和 a[i,···,t] 。i 为基准位置。 - 解子问题
若子序列元素长度为 0 或 1,认为它有序,直接返回;否则递归各个子问题。 - 合并
排序是就地进行的,合并步骤不需其它操作。
快速排序算法
public class QuickSort{
public static void quickSort(int nums[], int begin, int end){
if(begin < end){
int i = partition(nums, begin, end);
quickSort(nums, begin, i - 1);
quickSort(nums, i + 1, end);
}
}
private static int partition(int nums[],int low, int high){
int i = low;
int j = high;
int baseNum = nums[low];
while(i < j){
//先从后向前寻找第一个小于等于 baseNum 的数
while(j > i && nums[j] >= baseNum)
j--;
//把从后面找到的数赋给 nums[i],
nums[i] = nums[j];
//接着从前往后寻找第一个大于 baseNum 的数
while(i < j && nums[i] < baseNum)
i++;
//把从前面找到的数赋给 num[j]
nums[j] = nums[i];
}
//循环结束:i == j
nums[i] = baseNum;
return i;
}
}
复杂度分析
快速排序的时间主要耗费在划分操作上,对长度为 n 的区间进行划分,共需 n-1 次关键字的比较,时间复杂度为 O(n)。
对n个记录进行快速排序的过程构成一棵递归树,在这样的递归树中,每一层至多对n个记录进行划分,所花时间为 O(n)。
当初始排序数据正序或反序时,此时的递归树高度为n,快速排序呈现最坏情况,即最坏情况下的时间复杂度为 O(n^2);当初始排序数据随机分布,使每次分成的两个子区间中的记录个数大致相等,此时的递归树高度为log2n, 快速排序呈现最好情况,即最好情况下的时间复杂度为 O(n * log2n)。 快速排序算法的平均时间复杂度也是 O(n * log2n)。
归并排序
基本思路
一组长度为 n 的待排序列,将其分成 k (k >= 2) 个长度为 n / k 的有序子表成对归并;将这些有序表接着归并,获 n / k^2 个长为 k^2 的有序子表。反复进行,最后得一个长度为 n 的有序表。
二路归并排序
k = 2(归并在两个相邻有序子表间进行),称为二路归并排序
分治策略

自底向上二路归并排序算法
public class MergeSort {
/**
* 选择排序(从小到大)
*
* @param nums int[] 待排数组
*/
public static void mergeSort(int[] nums, int first, int last) {
if (first < last) {
int mid = (first + last) / 2;
mergeSort(nums, first, mid);
mergeSort(nums, mid + 1, last);
merge(nums, first, last);
}
}
public static void merge(int[] nums, int first, int last) {
// 拷贝数组
int[] numsCopy = new int[nums.length];
System.arraycopy(nums, first, numsCopy, first, last - first + 1);
// 指针准备
int mid = (first + last) / 2;
int i = first;
int iLeft = first;
int iRight = mid + 1;
// 开始比较
while (iLeft <= mid && iRight <= last) {
while(iLeft <= mid && iRight <= last && numsCopy[iLeft] <= numsCopy[iRight]){
nums[i++] = numsCopy[iLeft++];
}
if(iLeft > mid || iRight > last) break;
while(iLeft <= mid && iRight <= last && numsCopy[iLeft] >= numsCopy[iRight]){
nums[i++] = numsCopy[iRight++];
}
}
// 结尾处理
if(iLeft > mid) while(iRight <= last) nums[i++] = numsCopy[iRight++];
else while(iLeft <= mid) nums[i++] = numsCopy[iLeft++];
}
}
此处借用该博客的一张图

本文深入解析了分治法的基本概念,包括分支法和二分法的定义与应用。重点介绍了快速排序和归并排序两种典型分治算法的实现原理与过程,通过代码示例详细解释了算法的具体操作。同时,分析了算法的时间复杂度,讨论了不同情况下算法的效率表现。

&spm=1001.2101.3001.5002&articleId=106158976&d=1&t=3&u=ab6a047bf9514500ac736a084c3ef89f)
537

被折叠的 条评论
为什么被折叠?



