插入类
·1、直接插入排序
·算法思想:
通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫
描,找到相应位置并插入。
·算法描述:
1.从第一个元素开始,该元素可以认为已经被排序;
2.取出下一个元素,在已经排序的元素序列中从后向前扫描;
3.如果该元素 (已排序) 大于新元素,将该元素移到下一位置;
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
5.将新元素插入到该位置后;
重复步骤2~5。
例如:将
7 6 9 3 1 5 2 4
从小到大排序,演示前
4
趟排序结果:

第一趟:排序
6
,与
7
比较,
7
比
6
大,将
7
向右移动,空出位置插入
6
;

第二趟:排序
9
,与
7
比较,
7
比
9
小,不移动元素;

第三趟:排序
3
,与
9
比较,
9
比
3
大,将
9
向右移动;
3
继续与
7
比较,
7
比
3
大,将
7
向右移动;
3
继续与
6
比较,
6
比
3
大,将
6
向右移动;
空出第一个位置,插入元素
3
;

第四趟:排序
1
,与
9
比较,
9
比
1
大,将
9
向右移动;
1
继续与
7
比较,
7
比
1
大,将
7
向右移动;
1
继续与
6
比较,
6
比
1
大,将
6
向右移动;
1
继续与
3
比较,
3
比
1
大,将
3
向右移动;
空出第一个位置,插入元素
1
;
直接插入排序(从小到大):
void Insertion_Sort(int a[],int n){
for(int i=1;i<=n;i++){
int temp=a[i],j;
for(j=i-1;a[j]>temp&&j>=0;j--)
a[j+1]=a[j];
a[j+1]=temp;
}
}
折半插入排序(从小到大):

● 2
、希尔排序
● 算法思想:
1959年Shell
发明,第一个突破
O(n
2
)
的排序算法,是简单插入排序的改
进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。
希尔排序又叫缩小增量排序。
● 算法描述:
将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序:
①选择一个增量序列t1,
t2
,
…
,
tk
,其中
ti>tj
,
tk=1
;
②按增量序列个数k,对序列进行
k
趟排序;
③每趟排序,根据对应的增量ti,将待排序列分割成若干长度为
m
的子
序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列
作为一个表来处理,表长度即为整个序列的长度。



选择类
● 1
、简单选择排序
● 算法思想:
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,
然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序
序列的末尾。以此类推,直到所有元素均排序完毕。
● 算法描述:
①初始状态:无序区为R[1..n],有序区为空;
②第i趟排序
(i=1,2,3…n-1)
开始时,当前有序区和无序区分别为
R[1..i-1]
和R(i..n)。该趟排序从当前无序区中
-
选出关键字最小的记录
R[k]
,将它
与无序区的第1个记录
R
交换,使
R[1..i]
和
R[i+1..n)
分别变为记录个数增加
1个的新有序区和记录个数减少1
个的新无序区;
③n-1趟结束,数组有序化了。
选择排序(从小到大):
交换类
● 1
、冒泡(起泡)排序
●
算法思想:
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比
较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是
重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算
法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
●
算法描述:
①比较相邻的元素。如果第一个比第二个大,就交换它们两个;
②对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这
样在最后的元素应该会是最大的数;
③针对所有的元素重复以上的步骤,除了最后一个;
④重复步骤
1~3
,直到排序完成。
冒泡排序(从小到大):

● 2
、快速排序
●
算法思想:
通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字
均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达
到整个序列有序。
●
算法描述如下:
①从数列中挑出一个元素,称为 “基准”(
pivot
);
②重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基
准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之
后,该基准就处于数列的中间位置。这个称为分区(
partition
)操作;
③递归地(
recursive
)把小于基准值元素的子数列和大于基准值元素的子
数列排序。
举个栗子
序列:
6 1 2 7 9 3 4 5 10 8
,基准值为
6

归并排序
●
算法思想:
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采
用分治法(
Divide and Conquer
)的一个非常典型的应用。将已有
序的子序列合并,得到完全有序的序列;即先使每个子序列有序,
再使子序列段间有序。若将两个有序表合并成一个有序表,称为
2
路归并。
●
算法描述:
①把长度为
n
的输入序列分成两个长度为
n/2
的子序列;
②对这两个子序列分别采用归并排序;
③将两个排序好的子序列合并成一个有序的排序序列。
选择排序:
#include <iostream>
using namespace std;
int main() {
int n, a[10005];
cin >> n;
for (int i = 0; i < n ; i ++) //输入
cin >> a[i];
/*选择排序(从小到大)*/
for (int i = n - 1; i > 0; i --) { //i表示未排序数组的最后一个元素下标
int maxn = 0; //maxn表示最大值下标
for (int j = 0; j <= i; j ++) //从0 ~ i的范围找最大值
if (a[maxn] < a[j])
maxn = j;
swap(a[maxn], a[i]); //交换
}
for (int i = 0; i < n ; i ++) //输出
cout << a[i] << " ";
return 0;
}
插入排序:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 10005;
int a[N];
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i ++)
cin >> a[i];
//插入排序(从小到大)
for (int i = 1; i < n; i ++) {
//1、存储待排序元素a[i]
int j, tmp = a[i];
//2、找位置:所有比tmp大的元素往后移
for (j = i - 1; j >= 0 && a[j] > tmp; j --)
a[j + 1] = a[j];
//3、插入
a[j + 1] = tmp;
}
for (int i = 0; i < n; i ++)
cout << a[i] << " ";
return 0;
}
冒泡排序:
#include <iostream>
using namespace std;
int main() {
int n, a[10005];
cin >> n;
for (int i = 0; i < n ; i ++) //输入
cin >> a[i];
/*冒泡排序(从小到大)*/
for (int i = n - 1; i > 0; i --) { //i表示未排序数组的最后一个元素下标
bool flag = true; //每趟排序开始前将flag置为true
for (int j = 0; j < i; j ++) {
if (a[j] > a[j + 1]) { //相邻元素两两比较,如果逆序(这里的逆序是大的在前小的在后)则交换
swap(a[j], a[j + 1]);
flag = false; //只要有逆序,说明排序未结束,flag置为false
}
}
if (flag == true) //如果某一趟排序后一个逆序对也没有,则说明排序结束
break;
}
for (int i = 0; i < n ; i ++) //输出
cout << a[i] << " ";
return 0;
}
桶排序:
#include <iostream>
#include <algorithm>
using namespace std;
/*
"桶"排序思想: 利用辅助空间(数组)记录元素出现的次数,实现排序、去重等功能
例如:元素 4 3 8 5 4 2 3
1、开辟数组用于记录数字出现次数:
元素 0 1 2 3 4 5 6 7 8
次数 0 0 1 2 2 1 0 0 1
2、遍历数组,将i输出cnt[i]次
0: 没有,不输出
1: 没有,不输出
2: 出现1次,输出一个2
3: 出现2次,输出两个3
……
最终结果: 2 3 3 4 4 5 8
若将上述过程中,将每个出现过的数只输出1次,则实现了去重+排序的操作
*/
//输入n个元素(0 <= a[i] <= 5000), 将n个元素从小到大排序
int cnt[5005];
int main(){
int n, x,maxn = 0, minn = 5000;
cin >> n;
for(int i = 0; i < n; i ++){
cin >> x;
cnt[x] ++;
maxn = max(maxn, x),minn = min(minn, x);
}
for(int i = minn; i <= maxn; i ++){
while(cnt[i] != 0){
cout << i << " ";
cnt[i]--;
}
}
return 0;
}
归并排序:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5+5;
int a[N], n;
//先合并至临时数组, 再赋值回原数组
void merge1(int a[], int l1, int r1, int l2, int r2){
int tmp[N], k = 0;
int i = l1, j = l2;
while(i <= r1 && j <= r2){
if(a[i] <= a[j]) tmp[k ++] = a[i ++];
else tmp[k ++] = a[j ++];
}
while(i <= r1) tmp[k ++] = a[i ++];
while(j <= r2) tmp[k ++] = a[j ++];
for(int i = 0; i < k; i ++)
a[l1 + i] = tmp[i];
}
//先拆分元素组, 再合并会原数组
void merge2(int a[], int l1, int r1, int l2, int r2){
int b[N], c[N], lb = r1 - l1 + 1, lc = r2 - l2 + 1;
for(int i = 0; i < lb; i ++)
b[i] = a[l1 + i];
for(int i = 0; i < lc; i ++)
c[i] = a[l2 + i];
int i = 0, j = 0, k = l1;
while(i < lb && j < lc){
if(b[i] <= c[j]) a[k ++] = b[i ++];
else a[k ++] = c[j ++];
}
while(i < lb) a[k ++] = b[i ++];
while(j < lc) a[k ++] = c[j ++];
}
//递归
void merge_sort_rec(int a[], int left, int right){
if(left == right) return;
int mid = (left + right) / 2;
merge_sort_rec(a, left, mid);
merge_sort_rec(a, mid + 1, right);
merge1(a, left, mid, mid + 1, right);
}
//迭代
void merge_sort_iter(int a[], int n){
//每趟将数组分成若干个长度为len的小组, 进行合并。
for(int len = 1; len < n; len *= 2){
for(int l1 = 1; l1 <= n; l1 += 2 * len){
//这里取min是为了特殊处理最后一组长度不足len的情况, 要注意
int r1 = min(l1 + len - 1, n), l2 = r1 + 1, r2 = min(l2 + len - 1, n);
merge1(a, l1, r1, l2, r2);
}
}
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
merge_sort_iter(a, n);
//merge_sort_rec(a, 1, n);
for(int i = 1; i <= n; i ++) printf("%d ", a[i]);
return 0;
}
快速排序:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5+5;
int a[N], n;
void quick_sort1(int a[], int left, int right) {
if(left >= right) return;
//1、取基准值
swap(a[left], a[left + right >> 1]);
int x = a[left];
//2、划分数组(左右指针)
int i = left, j = right;
while(i < j) {
while(i < j && a[j] >= x) j --;
a[i] = a[j];
while(i < j && a[i] <= x) i ++;
a[j] = a[i];
}
a[i] = x;
//3、递归左区间和右区间
quick_sort1(a, left, i - 1);
quick_sort1(a, i + 1, right);
}
void quick_sort2(int arr[], int left, int right) {
if (left >= right) return;
//1、取基准值
swap(a[left], a[left + right >> 1]);
int x = a[left];
//2、划分数组(快慢指针)
int i = left, j = left + 1;
while (j <= right) {
if (a[j] < x) {
++i;
swap(a[i], a[j]);
}
++j;
}
swap(a[i], a[left]);
//3、递归左区间和右区间
quick_sort2(a, left, i - 1);
quick_sort2(a, i + 1, right);
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
quick_sort2(a, 1, n);
for(int i = 1; i <= n; i ++) printf("%d ", a[i]);
return 0;
}
计数排序:
#include <stdio.h>
#include <cstring>
#define N 10005
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
/*
计数排序
对于数组 a[]:
0 1 2 3 4 5 6 7 8 9
9 1 5 1 3 4 2 6 3 5
设置计数数组 cnt[]:
0 1 2 3 4 5 6 7 8 9
0 2 1 2 1 2 1 0 0 1
对cnt[]数组求前缀和:
0 1 2 3 4 5 6 7 8 9
0 2 3 5 6 8 9 9 9 10
**此时, cnt[i]表示原数组中最后一个元素i在排序之后的排名**
例如上面的 cnt[3] = 5, 意味着原数组中最后一个3排名第5, 所以排序后应该在4号位置(因为数组从0开始)
接下来**倒序**遍历原数组, 按每个元素的排名, 将所有元素放到数组b:
a[9] = 5, cnt[5] = 8, 将5放在b[7], cnt[5]变成7(下一个5的排名为7)
a[8] = 3, cnt[3] = 5, 将3放在b[4], cnt[3]变成4
a[7] = 6, cnt[6] = 9, 将6放在b[8], cnt[6]变成8
a[6] = 2, cnt[2] = 3, 将2放在b[2], cnt[2]变成2
……
倒序是为了保证排序的稳定性
最终数组 b[]:
0 1 2 3 4 5 6 7 8 9
1 1 2 3 3 4 5 5 6 9
将b[]放回a[]即可完成排序
*/
int a[N], b[N], cnt[1005], n, maxn, minn = 1000;
int main(){
scanf("%d", &n);
for(int i = 0; i < n; i ++){
scanf("%d", &a[i]);
cnt[a[i]] ++;
maxn = max(maxn, a[i]);
minn = min(minn, a[i]);
}
for(int i = 1; i <= maxn; i ++)
cnt[i] += cnt[i - 1];
for(int i = n - 1; i >= 0; i --)
b[--cnt[a[i]]] = a[i];
memcpy(a, b, sizeof b);
for(int i = 0; i < n; i ++)
printf("%d ", a[i]);
return 0;
}
基数排序:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
/*
基数排序: 对于多位数,从低位到高位,依次执行若干次计数排序(或者是其他**稳定**排序)
例如原数组 a[]:
0 1 2 3 4
121 421 321 125 530
第一趟对个位执行计数排序:
0 1 2 3 4
530 121 421 321 125
第二趟对十位执行计数排序:
0 1 2 3 4
121 421 321 125 530
第三趟对百位执行计数排序:
0 1 2 3 4
121 125 321 421 530
排序完成
*/
const int N = 100005;
void count_sort(int a[], int n, int w){
int cnt[10] = {0}, b[N];
for(int i = 0; i < n; i ++)
cnt[a[i] / w % 10] ++;
for(int i = 1; i < 10; i ++)
cnt[i] += cnt[i - 1];
for(int i = n - 1; i >= 0; i --)
b[--cnt[a[i] / w % 10]] = a[i];
memcpy(a, b, n*sizeof(int));
}
void radix_sort(int a[], int n){
int w = 0;
for(int i = 0; i < n; i ++)
w = max(w, (int)log10(a[i]) + 1);
for(int i = 1; w --; i *= 10){
count_sort(a, n, i);
}
}
int a[N], n;
int main(){
scanf("%d", &n);
for(int i = 0; i < n; i ++)
scanf("%d", &a[i]);
radix_sort(a, n);
for(int i = 0; i < n; i ++)
printf("%d ", a[i]);
return 0;
}
基数排序(二进制):
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int N = 100005;
void count_sort(int a[], int n, int w){
int cnt[2] = {0}, b[N];
for(int i = 0; i < n; i ++)
cnt[(a[i] >> w) & 1] ++;
cnt[1] += cnt[0];
for(int i = n - 1; i >= 0; i --)
b[--cnt[(a[i] >> w) & 1]] = a[i];
memcpy(a, b, n*sizeof(int));
}
void radix_sort(int a[], int n){
for(int i = 0; i < 32; i ++){
count_sort(a, n, i);
// for(int i = 0; i < n; i ++)
// printf("%d ", a[i]);
// puts("");
}
}
int a[N], n;
int main(){
scanf("%d", &n);
for(int i = 0; i < n; i ++)
scanf("%d", &a[i]);
radix_sort(a, n);
for(int i = 0; i < n; i ++)
printf("%d ", a[i]);
return 0;
}
希尔排序:
#include <iostream>
using namespace std;
/*
希尔排序: 又称缩小增量排序
利用插入排序在数据量少时效率高的特性, 将原数组先分成若干个独立部分, 独立的部分内部分别进行插入排序
例如原数组:
0 1 2 3 4 5 6 7 8 9
5 1 9 7 6 3 4 8 2 0
原数组长度为10
第一趟: 设置步长(dk) = 10 / 2 = 5, 即下标差为5的元素为一组:
0 1 2 3 4 5 6 7 8 9 组内排序 0 1 2 3 4 5 6 7 8 9
第一组 5 3 -----> 3 5
第二组 1 4 -----> 1 4
第三组 9 8 -----> 8 9
第四组 7 2 -----> 2 7
第五组 6 0 -----> 0 6
第一趟排序得到:
0 1 2 3 4 5 6 7 8 9
3 1 8 2 0 5 4 9 7 6
第二趟: 设置步长(dk) = 5 / 2 = 2, 即下标差为2的元素为一组:
0 1 2 3 4 5 6 7 8 9 组内排序 0 1 2 3 4 5 6 7 8 9
第一组 3 8 0 4 7 -----> 0 3 4 7 8
第二组 1 2 5 9 6 -----> 1 2 5 6 9
第二趟排序得到:
0 1 2 3 4 5 6 7 8 9
0 1 3 2 4 5 7 6 8 9
第三趟: 设置步长(dk) = 2 / 2 = 1, 即下标差为1的元素为一组:
0 1 2 3 4 5 6 7 8 9 组内排序 0 1 2 3 4 5 6 7 8 9
第一组 0 1 3 2 4 5 7 6 8 9 -----> 0 1 2 3 4 5 6 7 8 9
最终得到:
0 1 2 3 4 5 6 7 8 9
0 1 3 2 4 5 7 6 8 9
*/
void Shell_Sort(int a[], int n) {
for (int dk = n / 2; dk >= 1; dk /= 2) { //设置增量, 从n/2开始不断缩小2倍
for (int i = dk; i < n; i ++) { //内层执行插入排序, 控制好同组的元素: a[i]应分别与a[i-dk]、a[i-2*dk]……比较
int temp = a[i], j;
for (j = i - dk; j >= 0 && a[j] > temp; j -= dk)
a[j + dk] = a[j];
a[j + dk] = temp;
}
}
}
const int maxn = 100005;
int a[maxn], n;
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i ++)
scanf("%d", &a[i]);
Shell_Sort(a, n);
for (int i = 0; i < n; i ++)
printf("%d ", a[i]);
return 0;
}
文章的最后——
性能分析:
