排序所涉及操作:
- 比较
- 移动
插入排序
直接插入排序
- 排序策略:在有序表的恰当处插入一个新元素,并保持该有序表的有序性。即插入第i个元素时,1~i-1个元素已按关键字排序。
这里,r[0]的作用是监视哨兵。
(1)监视表头(结束)
(2)中间变量单元使用
把需要排序的表分为三部分:监视哨r[0]、已有序紫色表、待插入的橙色表。
- 直接排序插入示例: 数组{48,38,65,97,76,13,27,49}
![]()
![]()
![]()
![]()
![]()
![]()
......
- 代码实现:
void InsertSort()
{
for(i = 2; i<=n;i++) //从第二个元素开始比较
{
r[0] = r[i]; //r[0] 为监视哨
j = i-1;
while(r[0].key < r[j].key)
{
r[j+1] = r[j];
j--;
}//记录后移,边移动边找插入位置
r[j+1] = r[0];
}
}
- 算法分析
(1)时间分析: 用移动和比较次数可作为衡量时间复杂性的标准
正序时:比较次数
,移动次数0
逆序时:比较次数
,移动次数
所以时间复杂度T(n)=O(n2)
(2)空间分析: 在移动时不需要另增加存储空间,所以空间复杂度S(n)=O(1)
(3)稳定性: 由于是在线性表中逐一比较,所以该算法是稳定的。
希尔排序
-
基本思想:将待排序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”,再对全体进行一次直接插入排序 子序列的构成不是简单地“逐段分割”,而是将相隔某个“增量”的记录组成一个子序列
-
示例:
1.第一趟是59、36;20、98;17、11;......两两进行直接插入排序。此时增量为8
2.第二趟“缩小增量”,此时增量为4。36、28、59、65;20、14、98、41;......每组分别进行直接插入排序
3.由此不断缩小分组增量,分别进行直接插入排序
选择排序
简单选择排序
- 基本思想:从n-i+1(i=1,2,...,n-1)个记录中选出最小关键字,并和第i个记录交换
将{ 76,38,85,97,76,13,27,49}用简单选择排序方法进行排序。
堆排序
堆是一个完全二叉树。其中每个结点的关键字都大于其孩子结点的关键字。
堆的调整:(1)根的左右孩子比较(找出较大值)(2)父子比较(父>子,逆序则交换)
堆排序需要解决的两个问题:
(1)如何由一个无序序列建成一个堆 将序列看成是完全二叉树,最后一个非终端结点是第┕n/2┘(向下取整)个元素,从该元素开始堆的调整,逐步往上调整其他非终端节点
(2)如何在输出堆顶元素之后,调整剩余元素为堆 以队中最后一个元素替代输出的堆顶元素,自上而下进行堆的调整
交换排序
冒泡排序
- 简述基本思想:两两比较,逆序交换,一趟排序没有交换则排序结束。
- 个人理解:不如称之为“沉底排序”,每趟排序关键字大的像石块沉底。下一趟则是“次大”的石块沉底叠到上一次石块上。
快速排序
- 基本思想:首先确定一个关键字,通过一趟排序将代拍记录分割成独立的两部分,一部分均小于关键字、另一部分均大于关键字;后再分别对两部分记录重复上述步骤。
int Partition(int i, int j){
r[0] = r[i];
x = r[i].key;
while(i < j){
while(i < j && r[j].key >= x) j--;
r[i] = r[j];
while(i < j && r[i].key <= x) i++;
r[j] =r[i];
}
r[i] = r[0];
return i;
}
以上代码是对r[i]...r[j]中的记录进行一趟排序,将它们分成两部分。使:[...这部分的值<=x...] x [...这部分的值>=x...]
用子表的第一个记录作界点记录,第一个while循环从表的两端交替地向中间扫描 将比界点记录小的记录交换到低端,将比界点记录大的记录交换到高端。最后返回界点所在位置
void QuickSort(int low, int high){
if(low < high){
k = Partition(low, high);
QuickSort(low, k-1);
QuickSort(k+1, high);
}
}
以上对记录序列r[low]..r[high]进行快速排序, 用if语句保证长度大于1,随后将r[i]...r[j]分解成两部分,分别对左子表和右子表做快速排序。
归并排序
基本思想:n个记录看成n个长度为1的有序子序列,随后两两归并直至得到长度为n的有序序列——2路归并排序
void merge(int h, int m, int n){
int i = h;
int j = m+1;
k = h;
while(i <= m && j <= n){
if(r[i].key <= r[j].key) s[k++] = r[i++];
else s[k++] = r[j++];
}
while(i <= m) s[k++] = r[i++];
while(j <= n) s[k++] = r[j++];
for(i = h; i < n; i++) r[i] = s[i];
}
将两个有序表r[h]...r[m]和r[m+1]...r[n]归并为一个新的有序表r[h]...r[n]。设置i、j和k指示器的初值
随后将数值小的元素发在数组s中。后面两个while循环分别表示指示器j、i数值已经越界,随后使用for循环将有序表s赋给r
基数排序
- 基本思想
以10进制数为例。
对于d位的关键字(待排数据都是d位,不足者高位补0),从第d位(最低位)开始排序,直至第1位止。
也即先让个位有序,再使十位有序,......
它是一种按位进行的排序方法。
- 例:对{278,109,63,930, 589,184,505,269,8,83 }用基数排序法进行排序。
初始状态: 278,109,063,930,589,184,505,269, 008, 083
第一趟排序后(个位有序): 930, 063,083,184,505,278, 008,109,589, 269
第二趟排序后(在个位有序的基础上使拾位有序):505,008,109,930, 063,269 , 278,083,184,589
第三趟排序后(在拾位有序的基础上使百位有序):008,063,083,109,184,269,278,505,589,930