快速排序 && 归并排序
模板:
//快速排序 #include <iostream> using namespace std; const int N = 100010; int a[N]; void qsort(int l, int r) { if(l >= r) return; int i = l - 1, j = r + 1, x = a[(l + r + 1) >> 1]; while (i < j) { do i++;while(a[i] < x); do j--;while(a[j] > x); if(i < j) swap(a[i], a[j]); } qsort(l,i - 1);qsort(i,r); } int main() { int n; cin>>n; for (int i = 0; i<n; i++) { cin>>a[i]; } qsort(0,n-1); for (int i = 0; i<n; i++) { cout<<a[i]<<" "; } }
do i++;while(a[i] < x);
do j--;while(a[j] > x);
这里也可以写成while(a[++i] < x);while(a[--j] > x);
每次选择的数在快排之后都是左边小于等于它,右边大于等于它,所以递归循环的时候要根据所选的x来相应的变化:
1、若qsort(l,i -1);qsort(i,r), 那么所选择的中间变量就不能是x = a[l]或x = a[l + r >> 1],而应该是x = a[l + r + 1 >> 1]或x = a[r];
2、若qsort(l,j);qsort(j + 1,r);那么所选择的中间变量就不能是x = a[r]或 x = a[l + r + 1 >> 1], 而应该是x = a[l + r >> 1]或 x = a[l];
可以用比如3 5 1 4 2来模拟举例,可以选择第一个数a[l]、中间的数a[l + r >> 1]、右边的数a[r]来模拟,可以确定正确的策略都是在每次外层while循环结束后,qsort(l,j)都是小于等于x的数,qsort(j+1,r)都是大于等于x的数。
然后递归排序即可。
然后可以再选在只有两个数的数列:1 2来进行模拟,确定上面qsort和中间比较数x的选择策略。
第k个数:
//快速排序 #include <iostream> using namespace std; const int N = 100010; int a[N]; int qsort(int l, int r, int k) { if(l >= r) return a[l]; int i = l - 1, j = r + 1, x = a[l]; while (i < j) { do i++;while(a[i] < x); do j--;while(a[j] > x); if(i < j) swap(a[i], a[j]); } int t = j - l + 1; if(k <= t) return qsort(l,j,k); else return qsort(j + 1, r, k-t); } int main() { int n,k; cin>>n>>k; for (int i = 0; i<n; i++) cin>>a[i]; cout<<qsort(0,n-1,k)<<endl; }
归并排序(求逆序对的个数):
#include <iostream> using namespace std; const int N = 100000; #define LL long long int a[N],tmp[N]; LL qsort(int l, int r) { if(l >= r) return 0; int mid = (l + r) >> 1; LL x = qsort(l,mid), y = qsort(mid+1,r); LL res = x + y; int i = l, j = mid + 1, k = 0; while(i <= mid && j <= r) { if(a[i] <= a[j]) tmp[k++] = a[i++]; else { tmp[k++] = a[j++]; int t = mid - i + 1; res += t; } } while(i <= mid) tmp[k++] = a[i++]; while(j <= r) tmp[k++] = a[j++]; for(int i = l,j = 0;i <= r;i++,j++) a[i] = tmp[j]; return res; } int main() { int n; cin>>n; for(int i = 0;i<n;i++) cin>>a[i]; cout<<qsort(0,n-1)<<endl; }
最后答案res = qsort(l,mid) + qsort(mid + 1, r);
res += mid - i + 1;
这里虽然看起来是加了两次,但实际上最后都会转化成下面的部分。
因为最后数列被递归划分都是成为个数为一个的序列,然后当i == j的时候return 0递归栈返回,进行归并的时候,就进入到while循环里面进行判断,无论一开始的左半部分和右半部分,最后归并的时候都是从1、2、4、8来归并起来的,所以虽然划分了三部分:逆序对在左半部分、在右半部分、分散在左右半边,但归并的时候都是涵盖在res += mid - i + 1里面来了。