快速排序
例题:https://www.acwing.com/problem/content/787/
快排:
①确定分界点
②调整区间,使得小于等于x的在左边,大于等于x的在右边
③递归处理左右两段
两个指针 i,j 同时向中间移动,左指针 i ,右指针 j 。
如果左指针指向的数,小于等于 x ,那就继续右移,如果大于 x ,就先停止。
如果右指针指向的数,大于等于 x ,那就继续左移,如果小于 x ,就先停止。
如果两个指针都停止了,那么交换这两个指针指向的数。
直到这两指针相遇,说明遍历完成。
模板:
从小到大排序:
void quick_sort(int q[], int l, int r)
{
if(l >= r) return;
int i = l - 1, j = r + 1, x = q[l + r + 1 >> 1];//注意是向上取整,因为向下取整可能使得x取到q[l]
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);
}
从大到小排序:
void quick_sort(int q[], int l, int r)
{
if(l >= r) return;
int i = l - 1, j = r + 1, x = q[l + 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);
}
快速选择
例题:https://www.acwing.com/problem/content/description/788/
快排回顾:
①找到分界点 x ,q[L],q[(L + R) / 2],q[R]
②左边所有数Left <= x,右边所有数Right >= x
③递归排序Left,递归排序Right
假设左边所有数的个数为SL,右边所有数的个数为SR
①k ≤ SL 那么第 k 小的数一定在左边,递归左边
②k > SR 那么第 k 小的数一定在右边,递归右边,k - SL
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int n, k;
int q[N];
int quick_sort(int l, int r, int k)
{
if(l == r) return q[l];
int x = q[l], i = l - 1, j = r + 1;
while(i < j)
{
while(q[++i] < x);
while(q[--j] > x);
if(i < j) swap(q[i], q[j]);
}
int s1 = j - l + 1;
if(k <= s1) quick_sort(l, j, k);
else quick_sort(j + 1, r, k - s1);
}
int main()
{
cin >> n >> k;
for(int i = 0; i < n; i++)
scanf("%d", &q[i]);
cout << quick_sort(0, n - 1, k) << endl;
return 0;
}
归并排序
例题:https://www.acwing.com/problem/content/789/
①确定分界点:mid=(l + r) / 2
②递归排序左右两边
③归并,合二为一
#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
int n;
int q[N], tmp[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]) tmp[k++] = q[i++];
else tmp[k++] = q[j++];
while(i <= mid)
tmp[k++] = q[i++];
while(j <= r)
tmp[k++] = q[j++];
for(i = l, j = 0; i <= r; i++, j++)
q[i] = tmp[j];
}
int main()
{
int n;
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]);
printf("\n");
}
求逆序对的数量
例题:https://www.acwing.com/problem/content/790/
归并排序:
①[L, R] => [L, mid], [mid + 1, R]
②递归排序[L, mid]和[mid + 1, R]
③归并,将左右两个有序序列合并成一个有序序列
①左半边内部的逆序对数量:merge_sort(L, mid)
②右半边内部的逆序对数量:merge_sort(mid + 1, R)
③
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100010;
int q[N], tmp[N];
ll merge_sort(int l, int r)
{
if(l >= r) return 0;
int mid = l + r >> 1;
ll res = merge_sort(l, mid) + merge_sort(mid + 1, r);
//归并的过程
int k = 0, i = l, j = mid + 1;
while(i <= mid && j <= r)
if(q[i] <= q[j]) tmp[k++] = q[i++];
else
{
tmp[k++] = q[j++];
res += mid - i + 1;
}
//扫尾
while(i <= mid)
tmp[k++] = q[i++];
while(j <= r)
tmp[k++] = q[j++];
//物归原主
for(int i = l, j = 0; i <= r; i++, j++)
q[i] = tmp[j];
return res;
}
int main()
{
int n;
cin >> n;
for(int i = 0; i < n; i++)
cin >> q[i];
cout << merge_sort(0, n - 1) << endl;
return 0;
}