1.快速排序—分治
- 确定分界点:一般是取中间的点。
- 调整区间:左区间所有的数都小于等于x,右区间所有的数都大于等于x。
- 递归处理左右两段。
解法:
#include <iostream>
using namespace std;
const int N = 1e6+10;
int n;
int q[N];
void quick_sort(int q[], int l, int r) {
if (l >= r) return;
int x = q[l+((r-l)>>1)], i = l - 1, j = r + 1;
while (i < j) {
do i++; while (q[i] < x);
do j--; while (q[j] > x);
if (i < j) {
swap(q[i], q[j]);
}
}
quick_sort(q, l, j);
quick_sort(q, j+1, r);
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d", &q[i]);
quick_sort(q, 0, n - 1);
for (int i = 0; i < n; i++) printf("%d ", q[i]);
return 0;
}
//另一种写法
#include <iostream>
using namespace std;
const int N = 1e6+10;
int n;
int q[N];
void quick_sort(int q[], int l, int r) {
if (l >= r) return;
int x = q[l+r+1>>1)], i = l - 1, j = r + 1;
while (i < j) {
do i++; while (q[i] < x);
do j--; while (q[j] > x);
if (i < j) {
swap(q[i], q[j]);
}
}
quick_sort(q, l, i-1);
quick_sort(q, i, r);
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d", &q[i]);
quick_sort(q, 0, n - 1);
for (int i = 0; i < n; i++) printf("%d ", q[i]);
return 0;
}
平均时间复杂度:nlog(n);
边界情况分析:快速排序为分治排序,若分治后还出现(l,r)的情况,会无限递归
分析一: 以j为划分时,x不能选q[r]
以i为划分时,x不能选q[l]
例:若以j为划分,x选q[r], 当q[l ~r-1]<x时,j会取到r,此时quick_sort(q,l,j)即quick_sort(q,l,r)会造成无限递归。所以选x的时候,x=q[l+r<<1],x不能取q[l+r+1<<1],因为l+r+1为向上取整,会取到q[r]
若以i为划分,x选q[l],当q[l+1~r]>x时,i会取到l,此时quick_sort(q,i,r)即为quick_sort(q,l,r)会造成无限递归。所以选x的时候,x=q[l+r+1<<1],x不能取q[l+r<<1],因为l+r为向下取整,会取到q[l]
分析二:结束一次循环时,q[j]<=x,q[i]>=x
因为j和i越来越逼近,每一次排好序之后从i++,j++开始排序,他们一定会在同一个数相遇,若这个数正好为x,则i和j移动到此处停止,i等于j;若这个数不为x这个数肯定不可能既满足x大于x又满足小于x,i和j肯定会移动其中一个,从而结果j肯定会小于i,而不会等于i,i对应的肯定是大于等于x的,j对应的值肯定是小于等于x的。又因为j(不包括j)右边所有的数都满足大于等于j,而i(不包括i)左边所有的数都满足小于等于x,所以在传参时,传入(l,j),(j+1)或者(l,i-1),(i,r)。
分析三:不能为while(q[i]<=x)和while(q[j]>=x)
若q[l~r]全部相同,则循环的过程中,若q[i]>=x,i会一直自增,数组会越界,若越界后的数组仍然满足q[i]>=x的话,对于一个大数组肯定会TLE。
2.归并排序—分治
- 确定分界点,取中间点
- 递归排序
- 归并—合二为一
解法:
#include <iostream>
using namespace std;
const int N = 1000000;
int n;
int q[N], temp[N];
void merge_sort(int q[], int l, int r) {
if (l >= r) return;
int mid = l+r>>1;
merge_sort(q, l, mid);
merge_sort(q, mid + 1, r);
int k = 0, i = l, j = mid + 1;
while (i <=mid && j <= r)
if (q[i] <= q[j]) temp[k++] = q[i++];
else temp[k++] = q[j++];
while (i <= mid) temp[k++] = q[i++];
while (j <= r) temp[k++] = q[j++];
for (i = l, j = 0; i <= r; i++, j++) q[i] = temp[j];
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d", &q[i]);
merge_sort(q, 0, n - 1);
for (int i = 0; i < n; i++) printf("%d ", q[i]);
return 0;
}
平均时间复杂度:log(n);
归并算法就是取数组的中间下标,定义两个指针分别指向左区间和右区间的第一个元素,然后将左区间和右区间的数一个一个地进行比较,将较小值存入临时数组,并将该指针右移一位,另一指针不移动。若一个区间遍历完毕,另一个区间还剩下数,则将剩下的数直接放入临时数组即可。这里递归的作用其实就是将数组的数由少至多排好序,因为栈顶是两个数的排序,这两个数在经过归并之后出栈,此时两个数的序列就排好了,接下来可能就轮到另外两个数或者三个或者四个数进行排序并归,那么越来越多的数就被排列好了,随着merge-sort方法的栈顶一个一个出栈,数组就逐渐了完成排列。