📌 1. 插入排序(斗地主摸牌)
-
这个算法模拟的是“打扑克摸牌”的过程:
- 左边的牌是有序的(已经排好)
- 右边的牌是待插入的(还没摸)
- 每次“摸到”一张牌(key),就往左边插入到合适的位置。
void insert_sort(int a[],int n){
for(int i=2;i<=n;i++){
int key=a[i]; //key 表示“当前摸到的这张牌”
int j=i-1; // j 指向手里排好序的那一部分的最后一张牌
//通过他的位置来算,而不是通过值去比较
//1号-j号 是已经排好的
//j+1号 - n号是需要排的
// 向左寻找合适的位置插入key
// 只要 j 没越界,并且手里的牌比 key 大,就把牌往后挪
while(j>=1 && a[j]>key){
a[j+1]=a[j];// 把较大的牌往右移,为 key 腾出位置
j--; // 继续向左看下一张牌
}
// 当找到不大于 key 的牌后,把 key 插入到它右边(j+1 位置)
a[j+1]=key;
}
}
📌 举个例子:
假设你现在要排序这个数组:
ini
复制编辑
a = [3, 5, 2]
执行到第2轮(i=2)时:
- key = 2
- j = 1,a[j] = 5,比2大 → 往后移动
- j = 0,a[j] = 3,比2大 → 再往后移动
- j = -1 ❗️❗️此时不能再访问 a[j] 了!
所以 j >= 0 的存在就是为了防止访问越界。
📌 2. 选择排序(找到最小的元素,将其放入已经排序的区间)
//选择排序
void select_sort(int a[],int n){
for(int i=1;i<=n;i++){
int min_index=i;
for(int j=i+1;j<=n;j++){
if(a[min_index]>a[j]){
min_index=j;
}
}
//交换
swap(a[min_index],a[i]);
}
}
📌 3. 冒泡排序(两两相互比较,谁大谁往后滚)
// 冒泡排序
void bubble_sort(int a[],int n){
for(int i=1;i<=n-1;i++){
for(int j=1;j<=n-i;j++){
if(a[j]>a[j+1]){
swap(a[j],a[j+1]);
}
}
}
}
- 最好情况下O(n) 最坏情况O(n平方)
- 适合有序度>逆序度的方法
- 借助一个变量,flag ,检测有没有发生交换
- 稳定性(稳定)
📌 4. 计数排序(单独开一个数组)
- 典型的空间换时间
- 先得到这个数组中数据的最大值是多少,这样可以决定要开多大的计数数组
- 存的这个数字当做新计数数组的编号
- 如何保证计数排序的稳定性
- 举个例子,这次考试后,老师要对大家的成绩进行排名。
- 如果想实现想通数字的 前后排名的话,需要结合前缀和,每次拿一个数,对应位置的前缀和要-1,方便获取另一个数据的顺序
📌 5. 快速排序(比张三高的站右边,比张三矮的站左边)
- 排序的本质是降低逆序度,提升有序度,直到达到满有序度的过程
· 比张三高的站右边,比张三矮的站左边 一直重复就可以排好,分为左部分右半部分
📌 6. 归并排序(回家做作业,把作业分3类,语文数学英语)
- 分治的思想
- 将数列划分为2部分,找到最中间位置
- 递归地分别对两个子序列进行归并排序
- 合并两个子序列
- 归并排序实例演示