分治法
许多算法在结构上是递归的, 为了解决问题,往往一次或多次调用其自身(递归)解决相关的子问题
分治法的思想
讲原问题分为几个规模较小但类似于原问题的子问题, 再递归地求解这些问题, 然后再合并这些子问题的解来建立原问题的解(最优子结构)
分治模每层递归的三个步骤
分解原问题为若干子问题, 这些子问题是原问题的较小的实例
解决这些子问题, 递归地求解各子问题, 若子问题规模足够小, 则直接求解(递归结束)
合并这些子问题的解成原问题的解
基于分治的排序算法
快速排序
算法流程
1) 随便找一个数组中的"值" $x$ 作为分界点, 如 $a[l], a[r], a[l + r >> 1]$ (值越靠近中位数越好)
2) 根据 $x$ 的值来调整区间,最终使得 $x$ 左边的数都 $\leq x$, 而在 $x$ 右边的数都 $\geq x$
3) 递归得处理左右两端(经过步骤 $2$ 后数组已经相对有序, 左半边的最大值 $\leq$ 右半边的最小值)
void quick_sort(int l, int r) { //对l~r进行排序 if (l >= r) return; int x = a[l + r >> 1]; //确定分界点 int i = l - 1, j = r + 1; //确定双指针处理每轮的调整区间 while (i < j) { do i++; while (a[i] < x); //找到第一个大于等于分界点的值 do j--; while (a[j] > x); //找到第一个小于等于分界点的值 if (i < j) swap(a[i], a[j]); //交换刚刚找出的两个不符合顺序的值, 一轮结束左半边数都小于x, 右半边数都大于等于x } quick_sort(l, j), quick_sort(j + 1, r); //递归处理左右两边 }
寻找第k大的数
基于快速排序的思想, 但只要每次递归寻找一边即可, O(n)
void quick_search(int l, int r) {
if (l >= r) return;
int x = a[l + r >> 1]; //分界值
int i = l - 1, j = r + 1;
while (i < j) {
do i++; while (a[i] < x);
do j--; while (a[j] > x);
if (i < j) swap(a[i], a[j]);
}
if (j >= k - 1) quick_search(l, j); //要找的数在j的左边
else quick_search(j + 1, r); //右边
}
归并排序
算法流程
1) 递归 以下标的中心点来划分区间成左右两边, 然后先递归排序左右两边
2) 合并 由于返回后左右两端各自有序, 这步需要将两个各自有序的序列合二为一
void merge_sort(int l, int r) {
if (l >= r) return;
int mid = l + r >> 1;
merge_sort(l, mid), merge_sort(mid + 1, r); //先进行递归, 划分出小区间
int i = l, j = mid + 1, k = 0; //l~mid 和 mid+1~r 两个区间各自有序(元素可能只有1个)
//另开一个辅助数组来归并两个数组
while (i <= mid && j <= r) { //分别扫描比较两个数组的头, 拿出较小的
if (a[i] <= a[j]) fz[k++] = a[i++];
else fz[k++] = a[j++];
}
//一个数组用完而另一个数组还有
while (i <= mid) fz[k++] = a[i++];
while (j <= r) fz[k++] = a[j++];
//最后一步将辅助数组中存储的a[l~r]一段的有序子数组回填
for (int i = l, j = 0; i <= r; i++, j++) a[i] = fz[j];
}
求逆序对的数量
在进行归并排序的时候可以顺便求出序列的逆序对数量
void merge_sort(int l, int r) {
if (l >= r) return;
int mid = l + r >> 1;
merge_sort(l, mid), merge_sort(mid + 1, r);
int i = l, j = mid + 1, k = 0;
while (i <= mid && j <= r) {
if (a[i] <= a[j]) fz[k++] = a[i++];
else { //只有这种情况会出现逆序对
ans += mid - i + 1; //j 和 i~n 都会产生一个逆序对
fz[k++] = a[j++];
}
}
while (i <= mid) fz[k++] = a[i++];
while (j <= r) fz[k++] = a[j++];
for (int i = l, j = 0; i <= r; i++, j++) a[i] = fz[j];
}

浙公网安备 33010602011771号