Bootstrap

(十八)排序

一、前文

在生活中,我们有许许多多排序的地方,比如定学号。Sam, Amy, Lingling和Daming四人的排序可能是按照首字母排。也就是:

学号学生首字母
1AmyA
2DamingD
3LinglingL
4SamS

C++中,我们应该如何排序呢?这次也是一种算法,叫做排序

二、正文

(一)冒泡排序

1. 如何冒泡?

假如定义一个数组A,我们要从小往大排序,我们可以用i遍历数组A的0到项数-2。如果 A i < A i + 1 A_{i}<A_{i+1} Ai<Ai+1那么交换A和B的值。最终末尾排的值就是最大的值。最终就能成功排序了。在stdlib.h(或cstdlib)库中有一个函数swap(),里面要填入两个变量。下面有三种排序方法

(1)函数交换法
swap(a, b); //交换a和b的值
(2)临时存储法
int t = a; 
a = b; 
b = t; 
(3)位运算法

这种方法意思现在不管,以后再说

a = a ^ b; 
b = a ^ b; 
a = a ^ b; 

三种交换的时间复杂度比较:
函数交换法 < 位运算法 < 临时存储法

看下面动图
冒泡排序动图

2. 如何实现?

会先输入一个数字,表示 A A A的总项数。再依次输入 A i A_{i} Ai。最终会分别输出被排序的 A i A_{i} Ai

int main() {
	int n; 
	cin >> n; 
	int a[n]; 
	for(int i=0; i<n; i++) //输入
		cin >> a[i]; 
	for(int i=n-1; i>=0; i--) 
		for(int j=0; j<i; j++) 
			if(a[j]>a[j+1]) //交换
				swap(a[j], a[j+1]); 
	for(int i=0; i<n; i++) //输出
		cout << a[i] << ' '; 
}

(二)选择排序

1. 如何选择?

说白了,就是在数组中找最小值,随后放在前面。具体动图
选择排序动图

2. 如何实现?

#include <bits/stdc++.h>
using namespace std;
int main() {
	int n; 
	cin >> n; 
	int a[n]; 
	for(int i=0; i<n; i++) cin >> a[i]; //输入
	for(int i=0; i<n; i++) { 
		int minx=INT_MAX, id=0; //INT_MAX表示int类型存储的极限
		for(int j=i; j<n; j++) 
			if(minx>a[j]) minx=a[j], id=j; 
		swap(a[i], a[id]); //交换
	}
	for(int i=0; i<n; i++) cout << a[i] << ' '; //输出
	return 0;
}

(三)插入排序

1. 如何插入?

首先,依次用i遍历 A A A中的值索引,然后在往前面翻,如果 A i < A j A_{i}<A_{j} Ai<Aj,那么插入在 A j A_{j} Aj前,否则继续遍历。看下面的动图
插入排序动图

2. 如何实现?

#include <iostream>
using namespace std; 
int main() {
	int n; 
	cin >> n; 
	int a[n]; 
	for(int i=0; i<n; i++) 
		cin >> a[i]; 
	for(int i=0; i<n-1; i++) {
		int end=i, tmp=a[i+1]; 
		while(end>=0) {
			if(tmp<a[end]) 
				a[end+1] = a[end--]; 
			else 
				break; 
		}
		a[end+1] = tmp; 
	}
	for(int i=0; i<n; i++) 
		cout << a[i] << ' '; 
}

(四)桶排序

1. 如何排?

超市中的收银机见过吧。每回我们支付现钱的时候,收银员会将一样的钱放在一样的盒子里,这就是一个桶。在我们输入的时候,就可以把相应的数字放入一个数组中,到时候输出就直接按照出现的次数了

1. 如何实现?

(1)桶排序
#include <bits/stdc++.h>
using namespace std;
int main() {
	int n, a[1000]={}, b[101000]={}; 
	cin >> n; 
	for(int i=0; i<n; i++) {
		cin >> a[i]; 
		b[a[i]] ++; 
	}
	for(int i=0; i<101000; i++) 
		for(int j=1; j<=b[i]; j++) 
			cout<<i<<' '; 
	return 0; 
}
(2)桶排序去重

另外,使用桶排序我们还可以进行去重操作。就像这样

#include <bits/stdc++.h>
using namespace std;
int main() {
	int n, a[1000]={}; 
	bool b[1010000]={}; 
	cin >> n; 
	for(int i=0; i<n; i++) {
		cin >> a[i]; 
		b[a[i]] = true; 
	}
	for(int i=0; i<1010000; i++) 
		if(b[i]) cout << i << ' '; 
	return 0; 
}

桶排序的缺点是会占用大量空间

(五)sort排序

1. 如何用?

sort()函数位于头文件algorithm,使用方法如下:

#include <algorithm>
sort(Begit, Endit); //从Begit到Endit使用从小到大的方式进行排序
sort(Begit, Endit, Comp); //从Begit到Endit使用Comp规则进行排序

BegitEndit中填的都是指针或迭代器

(1)指针如何表示?

如果这个是STL(Standard Template Library)容器:

T.begin() //T的起始位置的迭代器
T.begin()+n //T[n]的迭代器
T.end() //T的终止符的迭代器
T.end()-1 //T的最后一项的迭代器

如果这是一个静态数组:

a //a的起始位置的迭代器
a + n //a[n]的迭代器
(2)Comp怎么填

这是一个自定义排序的变量,因此要填入的是一个函数

a. 普通函数填法
...
inline bool cmp (Typ a, Typ b) {
	return Compare; 
}
int main () {
	......
	int a[n]; 
	......
	sort(a, a+n, cmp); 
}

这里的inline是一个函数修饰词,表示这是一个内联函数,内联函数不能自己调用自己,但可以节省空间。Typ是数组的类型,ab代表相邻的两项。这样排序后相邻两项会遵守Compare中的规则(两值相同情况除外,Compare是布尔类型)

b. 匿名(lambda)函数填法
......
int main () {
	......
	int a[n]; 
	......
	sort(a, a+n, [](Typ a, Typ b){return Compare; }); 
}

一样的,Typ填数组的类型,ab表示相邻的两项,最终相邻两项会遵守Compare的规则(两数相同情况除外,Compare是布尔类型)

2. 如何写?

#include <bits/stdc++.h>
using namespace std;
int main() {
	int n; 
	cin >> n; 
	int a[n]; 
	for(int i=0; i<n; i++) 
		cin >> a[i]; 
	sort(a, a+n); //排序
	for(int i=0; i<n; i++) 
		cout << a[i] << ' '; 
	return 0; 
}

Alt

;