快速排序 && 归并排序

模板:

//快速排序
#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里面来了。

原文地址:https://www.cnblogs.com/longxue1991/p/12603917.html