排序算法的衡量
时间效率
比较次数与移动次数
空间效率
占内存辅助空间的大小
稳定性
假设 ki 是记录 Ri 中的关键字,kj 是记录 Rj 的中关键字,ki = kj 且在排序前的序列中,Ri 领先于Rj 。若在排序后的序列中 Ri 仍领先于 Rj,则称所用的排序方法是稳定的。
假设 ki 是记录 Ri 中的关键字,kj 是记录 Rj 的中关键字,ki = kj 且在排序前的序列中,Ri 领先于Rj 。若在排序后的序列中 Ri 仍领先于 Rj,则称所用的排序方法是稳定的。
直接插入排序
过程
整个排序过程为 n-1 趟插入。
1.先将序列中第1个记录看成是一个有序子序列。
2.取未排序子序列中的第一个元素,在已排序子序列中按简单顺序查找法(挨个比较)查找插入位置,边查找边移位。(重复此操作直到排序完成)
演示
用一个 n×n 的表格来表示。
[13] 6 3 31 9 27 5 ——-初始
[6 13] 3 31 9 27 5 ——-第一躺
[3 6 13] 31 9 27 5 ——-第二趟
[3 6 13 31] 9 27 5 ——-第三趟
[3 6 9 13 31] 27 5 ——-第四趟
[3 6 9 13 27 31] 5 ——-第五躺
[3 5 6 9 13 27 31] ——-第六躺
分析
时间复杂度: O(n2)
空间复杂度: O(1)
稳定性:稳定
实现
C语言实现
void InsertSort(SqList &L)
{
int i,j;
for(i=2; i<=L.length; ++i)
if( L.r[i].key < L.r[i-1].key )
{
L.r[0]=L.r[i]; // 复制为哨兵
for(j=i-1; L.r[0].key<L.r[j].key; --j)
L.r[j+1]=L.r[j]; // 第j个位置上的记录后移
L.r[j+1]=L.r[0]; //插入到正确位置
}
}
哨兵也可以单独的挑出来 , 数组从零开始。
void InsertSort(SqList &L)
{
int i,j;
r_type guard;
for(i = 1; i <= L.length; ++i)
if( L.r[i].key < L.r[i-1].key )
{
guard = L.r[i]; // 复制为哨兵
for(j = i-1; guard.key < L.r[j].key; --j)
L.r[j+1] = L.r[j]; // 第j个位置上的记录后移
L.r[j+1] = guard; //插入到正确位置
}
}
折半插入排序
过程
1.先将序列中第1个记录看成是一个有序子序列。
2.取未排序子序列中的第一个元素,在已排序子序列中按折半查找法查找插入位置,边查找边移位。(重复此操作直到排序完成)
插入位置的规则
当low>high时,low位置就是待插入的位置;
当r[mid]=r[i]时,mid+1位置就是插入位置;
分析
时间复杂度: O(n2) (比直接插入快)
空间复杂度: O(1)
稳定性:稳定
实现(待定)
希尔排序
过程
先将整个待排记录序列分割成若干子序列,分别进行直接插入排序。
待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
演示
划分序列为 {5,3,1}。
实现(待定)
冒泡排序(起泡排序)
过程
每趟不断将相邻两记录比较,并按“前小后大” 规则交换
演示
太简单了啊不想演示
分析
时间复杂度: O(n2)
空间复杂度: O(1)
稳定性:稳定
实现
C语言实现
void BubbleSort(SqList &L){
a_type temp;
for(i = 1; i < L.length; i++){
c = 0;
for(j = 1; j <= L.length-i; j++){
if(L.a[j].key > L.a[j+1].key){
temp = L.a[j];
L.a[j] = L.a[j+1];
L.a[j+1] = temp; //交换
c++;
}
if(c == 0) break; //一趟没有发生交换,提前结束排序
}
}
快速排序
过程
1.任取一个元素 为中心(支点),一般刚开始取首位元素。
2.所有比它小的元素放在它前面,比它大的元素放在它后面,形成左右两个子表
3.对各子表重新选择中心元素并依此规则调整,直到每个子表的元素只剩一个
演示
太难了我不想演示
我又想演示了
从
high
开始往前找比“枢纽”小的数,赋予low
处。从
low
开始往后找比“枢纽”大的数,赋予high
处。重复1,2步直到
low >= high
实现
C语言实现
int Partition(SqList &L, int low, int high)
{ //定位“枢纽”
L.r[0] = L.r[low]; //将“枢纽”值赋予L.r[0]
while(low < high)
{
/* 从high开始往前找比“枢纽”小的数,赋予`low`处 */
while(low < high && L.r[high].key >= L.r[0].key)
--high;
L.r[low] = L.r[high];
/* 从`low`开始往后找比“枢纽”大的数,赋予`high`处 */
while(low < high && L.r[low].key <= L.r[0].key)
++low;
L.r[high] = L.r[low];
}
L.r[low]=L.r[0]; //将“枢纽”值赋予L.r[low]
return low; //返回“枢纽”位置
}
void QSort ( SqList &L, int low, int high )
{
if(low < high)
{
pivotloc = Partition(L, low, high); //pivot:枢纽
QSort(L, low, pivotloc-1); //对左子表排序
QSort(L, pivotloc+1, high); //对右子表排序
}
}
简单选择排序
过程
每一趟在 n-i +1个中选出关键码最小的对象, 作为有序序列的第 i 个记录
分析
时间复杂度: O(n2)
空间复杂度: O(1)
稳定性:稳定
演示
实现
C语言实现
void SelectSort(SqList &K)
{
rtype temp;
for (i=1; i<L.length; ++i)
{
k = i; //k为当前最小记录的位序
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];
}
}
树形排序
不考,我就先不写了啊
堆排序
堆
堆:n个元素的序列{k1,k2,…,kn},当且仅当满足下列关系时,称为堆:
或
前者称为小顶堆(最小堆),后者称为大顶堆(最大堆),即堆顶元素(根)为最小值或最大值。
堆是一个线性结构。
如果将堆序列看成一个完全二叉树的线性存储结构,那么非终端结点的值均小于或大于左右孩子结点的值。
过程
1. 将待排序元素依次输入建立完全二叉树
2. 从最后一个非终端节点(第
⌊
n
⌋ 元素)开始,至第一个元素,若存在比其小(大)的子节点,将其与最小(大)的子节点做替换,堆顶元素为最小(大),输出堆顶元素。
3. 将其与堆中最后一个元素(当前无序序列的最后一个元素)互换,并“剔除”最后一个元素。
4. 重复1,2,3步直到排序完成。
分析
时间复杂度: O(nlog2n)
空间复杂度: O(1)
稳定性:不稳定
适用于n 较大的情况
演示
待排序序列{30 60 8 40 70 12 10}
建大顶堆(从小大大排序)
归并排序
之前有专门总结:二路归并算法
基数排序
多关键字的排序
高位优先法
划分子序列的思想
低位优先法
分配再收集的思想
基数
d 为分量数。
设关键字的每个分量的取值范围均是:
可能的取值个数 rd 称为基数。
链式基数排序
过程
建立一组关键字链表,用于分配和回收各位关键字;
从低位关键字到高位关键字依次进行分配和回收;
分配:根据当前关键字的取值将记录插入到相应的链表中;
回收:各链表“头尾相连”后回收所有记录作为下次分配的记录序;
演示
待排序序列:{47 82 40 07 59 38 69 05}
比较
快速排序平均状态下最快,但不稳定。
数据量大,要稳定,要快,归并。
好的,继续学kava。