选择排序
简单选择排序
基本思想: 在待排序的数据中选出最大(小)的元素放在其最终位置。
基本操作:
- 首先通过n-1次关键字比较,从n个记录中找出关键字最小的记录,将它与第一个记录交换
- 再通过n-2次比较,从剩余的n-1个记录中找出关键字次小的记录,将它与第二个记录交换
- 重复上述操作,共进行n-1趟排序后,排序结束
简单选择排序算法:
void SelectSort(SqList &K){
for(i = 1; i< L.length; ++i){
k = i;
for( j = i + 1; j <= L.length; j++)
if(L.r[j].key < L.r[k].key)
k = j; //记录最小值位置
if(k != i)
L.r[i]⬅➡L.r[k]; //交换
}
}
时间复杂度:
- 记录移动次数
最好情况:0
最坏情况:3(n-1) - 比较次数:无论待排序列处于什么状态,选择排序所需进行的“比较”次数都相同
∑ i = 1 n − 1 ( n − i ) = n 2 ( n − 1 ) \sum_{i=1}^{n-1} (n-i)= \frac{n}{2}(n-1) i=1∑n−1(n−i)=2n(n−1)
算法稳定性:简单选择排序是不稳定排序
堆排序
堆的定义:
堆实质是满足如下性质的完全二叉树:
二叉树中任一非叶子结点均小于(大于)它的孩子结点。
堆排序:
若在输出堆顶的最小值(最大值)后,使得剩余n-1个元素的序列重新建成一个堆,则得到n个元素的次小值(次大值)…如此反复,便能得到一个有序序列,这个过程称之为堆排序。
实现堆排序需解决两个问题:
- 如何由一个无序序列建成一个堆?
- 如何在输出堆顶元素后,调整剩余元素为一个新的堆?
算法思路:https://www.bilibili.com/video/BV1nJ411V7bd?p=168(3:43)
筛选过程的算法描述:
/*一直R[s..m]中记录的关键字除R[s]之外均满足堆定义,
本函数调整R[s]的关键字,使R[s..m]成为一个大根堆*/
void HeapAdjust(elem R[], int s, int m){
re = R[s];
for(j = 2*s; j <= m; j *= 2){ //沿key较大的孩子结点向下筛选
if(j < m && R[j] < R[j + 1]) //j为key较大的记录下标
++j;
if(rc >= R[j])
break;
R[s] = R[j];
s = j; //rc应插在位置s上
}//for
R[s] = rc; //插入
}// HeapAdjust
堆的建立:
从无序序列建立堆
例题讲解:https://www.bilibili.com/video/BV1nJ411V7bd?p=169(4:53)
实质上,堆排序就是利用完全二叉树中父结点与孩子结点之间的内在关系来排序的。
堆排序的算法:
void HeapSort(elem R[]){ //对R[1]到R[n]进行堆排序
int i;
for(i = n/2; i >= 1; i--)
HeapAdjust(R, i, n); //建立初始堆
for(i = n; i > 1; i--){ //进行n - 1趟排序
Swap(R[1], R[i]); //根与最后一个元素交换
HeapAdjust(R, 1, i - 1); //对R[1]到R[i - 1]重新建堆
}
}//HeapSort
算法分析:
- 时间复杂度为O(nlog2n)\
- 需要一个记录大小供交换用的辅助存储空间
- 是一种不稳定的排序方法
归并排序
基本思想:
将两个或两个以上的有序子序列“归并”为一个有序序列;
在内部排序中,通常采用的是2-路归并排序。
归并排序示例:
应用双指针比较大小:
算法分析:
- 时间效率:O(nlog2n)
- 空间效率:O(n)
- 稳定
- 缺点:需要一个与原始序列同样大小的辅助序列
基数排序
基本思想:分配 +收集
也叫桶排序或箱排序:设置若干个箱子,将关键字为k的记录放入第k个箱子,然后再按序号将非空的连接。
基数排序:
数字是有范围的,均由0 - 9这十个数字组成,则只需设置十个箱子,相继按个、十、百…进行排序。
时间效率:O(k*(n + m))
k:关键字个数
m:关键字取值范围为m个值
空间效率:O(n+m)
稳定性:稳定