排序算法


快速排序


一、基本思路

1
2
3
1. 确定分界点x
2. 调整区间:使左边区间都是小于等于x的数,右边都是大于等于x的数
3. 使用递归处理左右两段

二、快排模板

1
2
3
4
5
6
7
8
9
10
11
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和+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);//记住就行,不然要考虑边界问题
}

三、例题

1.快速排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//给定你一个长度为 n 的整数数列。
//请你使用快速排序对这个数列按照从小到大进行排序。
//并将排好序的数列按顺序输出。
//输入格式
//输入共两行,第一行包含整数 n。
//第二行包含 n 个整数(所有整数均在 1∼1e9 范围内),表示整个数列。
//输出格式
//输出共一行,包含 n 个整数,表示排好序的数列。
//数据范围
//1≤n≤100000
#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 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);
}

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;
}

2.第k个数

(题目转载自acwing)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*给定一个长度为 n 的整数数列,以及一个整数 k,请用快速选择算法求出数列从小到大排序后的第 k 个数。

输入格式
第一行包含两个整数 n 和 k。

第二行包含 n 个整数(所有整数均在 1∼1e9 范围内),表示整数数列。

输出格式
输出一个整数,表示数列的第 k 小数。

数据范围
1≤n≤100000,
1≤k≤n*/


//快速选择算法需要对快排进行优化
#include<iostream>
using namespace std;

const int N = 1e5 + 10;
int q[N];
int n,k;

int qsort(int l , int r, int k){
if(l == r) return q[l];

int x = q[l + r >> 1];
int 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]);
}
int sl = j - l + 1;
if(k <= sl) return qsort(l, j, k);//递归左区间
else return qsort(j + 1, r, k - sl);//递归右区间,下标变成求k - sl
}

int main(){
scanf("%d%d", &n, &k);
for(int i = 0; i < n; i++) scanf("%d", &q[i]);

cout<<qsort(0, n - 1, k)<<endl;
return 0;
}

3.求第k小的数

(题目转载自洛谷)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//题目描述
//输入 n(1 n <= 5000000且 n 为奇数)个数字 ,输出这些数字的第 k 小的数。最小的数是第 0 小。
//请尽量不要使用 nth_element 来写本题,因为本题的重点在于练习分治算法。
#include<iostream>
using namespace std;

int q[5000010];
int n, k;

void quick_sort(int q[], int l, int r) {

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]);
}
if (k <= j) quick_sort(q, l, j);
else if (k >= i) quick_sort(q, i, r);
else {
printf("%d", q[j + 1]);
exit(0);
}

}

int main() {
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i++) scanf("%d", &q[i]);

quick_sort(q, 0, n - 1);
}

归并排序

一、基本思路

1
2
3
4
1.确定分界点:(l + r)/2  x下标的中间值
2.递归排序左右两边
3.归并——合二为一
(与快排不同,归并是先递归)

二、归排模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//来自yxc dalao
void merge_sort(int q[], int l, int r) {
if (l >= r) return;

int mid = 1 + 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];
}

三、例题

1.归并排序

(题目转载自acwing)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//给定你一个长度为 n 的整数数列。
//
//请你使用归并排序对这个数列按照从小到大进行排序。
//
//并将排好序的数列按顺序输出。
//
//输入格式
//输入共两行,第一行包含整数 n。
//
//第二行包含 n 个整数(所有整数均在 1∼1e9 范围内),表示整个数列。
//
//输出格式
//输出共一行,包含 n 个整数,表示排好序的数列。
//
//数据范围
//1≤n≤100000
#include<iostream>
using namespace std;

const int N = 1e6 + 10;
int q[N],tmp[N];
int 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 i = l, j = mid + 1, k = 0;
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(){
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;

}

2.逆序对的数量

(题目转载自acwing)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/*给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量。

逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i<j 且 a[i]>a[j],则其为一个逆序对;否则不是。

输入格式
第一行包含整数 n,表示数列的长度。

第二行包含 n 个整数,表示整个数列。

输出格式
输出一个整数,表示逆序对的个数。

数据范围
1≤n≤100000,
数列中的元素的取值范围 [1,1e9]。*/

#include<iostream>
using namespace std;
//爆int了,用 long long
typedef long long LL;

const int N = 1e6 + 10;
int n;
int q[N];
int tmp[N];

LL merge_sort(int q[], int l, int r){

if(l >= r) return 0;

int mid = l + r >> 1;

LL res = merge_sort(q, l, mid) + merge_sort(q, mid + 1, r);

int i = l, j = mid + 1, k = 0;
while(i <= mid && j <= r){
if(q[i] <= q[j]) tmp[k++] = q[i++];
if(q[i] > q[j]){
tmp[k++] = q[j++];
res += mid - i + 1;
}
}
//扫尾
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];
return res;
}

int main(){

scanf("%d", &n);
for(int i = 0; i < n; i++) scanf("%d", &q[i]);

cout<<merge_sort(q, 0, n - 1);
return 0;
}

其他排序

例题

1.明明的随机数

(题目转载自洛谷)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*题目描述
明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了N个1到1000之间的随机整数(N≤100),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的学生的学号。然后再把这些数从小到大排序,按照排好的顺序去找同学做调查。请你协助明明完成“去重”与“排序”的工作。

输入格式
输入有两行,第1行为1个正整数,表示所生成的随机数的个数N

第2行有N个用空格隔开的正整数,为所产生的随机数。

输出格式
输出也是两行,第1行为1个正整数M,表示不相同的随机数的个数。

第2行为M个用空格隔开的正整数,为从小到大排好序的不相同的随机数。*/

#include<iostream>
using namespace std;
//用桶排序
int main(){
int n,t, cont = 0;
int q[1010] = {0};
scanf("%d", &n);

for(int i = 0; i < n; i++){
scanf("%d", &t);
q[t] = 1;//去重
}

for(int i = 0; i < 1010; i++){
if(q[i] == 1){
cont++;
}
}
printf("%d\n", cont);
for(int i = 0; i <1010; i++){
if(q[i] == 1)
printf("%d ", i);
}
return 0;
}