目录
一、排序的基本概念
排序:将一组杂乱无章的数据按一定规律次序排列起来。及:将无序序列排成一个有序数列(由小到大或由大到小)的运算。
基于顺序表的存储结构来排序:
#define MAXSIZE 20
typedef int KeyType;
typedef struct{
KeyType key;//关键字
InfoType otherinfo;
}RedType;
typedef struct{
RedType r[MAXSIZE + 1];//r[0]一般作哨兵或缓冲区
int length;
}SqList;
二、插入排序
在有序序列中插入一个元素,保持序列有序,有长度不断增加。
2.1 直接插入排序
用顺序查找法查找插入位置。
代码实现
void InsertSort(SqList &sl){
int i,j;
for(int i = 2;i<=sl.length;i++){
if(sl.r[i].key < sl.r[i-1].key){//比较关键字的大小
sl.r[0] = sl.r[i];//将目标元素存储在哨兵中
j = i - 1;
for(j = i-1;sl.r[0].key<sl.r[j].key;j--)//比较关键字的大小
sl.r[j+1] = sl.r[j];//将关键字大的元素向后移
sl.r[j] = sl.r[0];//将目标插入
}
}
}//顺序插入
效率分析
原始数据越接近有序,排序速度越快。
最坏情况:原始数据是逆序的。T(n) = O(n^2)
平均情况下,耗时差不多是最坏情况的一般。T(n) = O(n^2)
要提高查找速度:减少元素的比较次数,减少元素的移动次数。
空间复杂度:只需要一个哨兵空间。O(1)
2.2 折半(二分)插入排序
算法思想
对于即将要进行排序的元素i,将列表中已经排序好的第1个元素设为low,最后一个元素,即第i-1个元素设为high。将i的关键字与二者的中间元素mid的关键字相比:若i<mid,则high= mid-1,再次进行比较;若i>=mid,则low = mid + 1,再次进行比较。直到high<low,high在low的左边,则此时high+1的位置就是要插入的位置。
在过程中,i的关键字至于每次mid的关键字进行比较,并不是在一次折半后,和mid的左边/右边的全部元素的关键字进行比较;需要一直进行折半,直到不能继续折。
代码实现
void BInsertSort(SqList &sl){
//int i,j;
//int low,high,mid;
for(int i = 2;i<=sl.length;i++){
sl.r[0] = sl.r[i];//目标元素存入哨兵位置
int low = 1;
int high = i-1;
while(low<=high){
int mid = (low + high)/2;
if(sl.r[0].key<sl.r[mid].key)
high = mid - 1;
else
low = mid + 1;
}//循环结束时,high在low左边,high+1就是插入位置
for(int j = i-1;j>=high + 1;j--)
sl.r[j+1] = sl.r[j];
sl.r[high + 1] = sl.r[0];
}
}//折半插入
效率分析
1.折半查找比顺序查找快,所以折半插入排序比直接插入排序快。
2.其所需比较次数与序列的初始排列无关,仅与其对象个数相关。
3.当n较大时,其关键码比较次数比直接插入排序的最坏情况好得多,但比其最好情况差;
当队列在初始已经接近有序,直接插入排序比折半插入排序执行的关键码比较次数要少。
4.折半插入排序的对象移动次数与直接插入排序相同,依赖于队列的初始排列。
5.时间复杂度:O(n^2);空间复杂度:O(1)。
2.3 希尔排序
算法思想
先将整个待排序列分割成若干子序列,分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
代码实现
void ShellInsert(SqList &sl,int dk){
for(int num=1;num<=dk;num++){
int i,j;
for(i = 1+dk;i<=sl.length;i = i+dk){
if(sl.r[i].key<sl.r[i-dk].key){
sl.r[0] = sl.r[i];//存放目标元素
for(j = i-dk;sl.r[0].key<sl.r[j].key;j = j-dk){
sl.r[j+dk] = sl.r[j];}//将元素后移
sl.r[j] = sl.r[0];//插入目标元素
}
}
}
}//希尔插入
void ShellSort(SqList &L,int dlta[],int t){//dlta[]为存放步长的数组,t为需要进行希尔插入的次数。
for(int k = 0;k<t;k++)
ShellInsert(L,dlta[k]);//进行一趟步长为dlta[k]的希尔插入
}//希尔排序主程序
效率分析
1.其算法效率与增量序列的取值有关。
2.希尔排序是一种不稳定的排序算法。
3.时间复杂度为n和步长d的函数
空间复杂度为O(1).
4.增量序列的最后一个增量值必须为1.
5.不宜在链式存储结构上实现。
三、交换排序
基本思想
两两比较,如果发生逆序则交换,直到所有记录都排好序为止。
冒泡排序
基本思想:每趟不断将记录两两比较,并按“前小后大”的规则交换。
若按升序排列,冒泡排序的第i次过程就是一遍遍的将前n+1-i个元素中最大的元素排到最后。
若有n个元素,则需排列n-1趟;第 i 趟,需要比较n - i次。
void bubble_sort(SqList &sl){
int m,j;
RedType x;//用于临时存储
for(m = 1;m<=sl.length - 1;m++){//总共需m趟
for(j = 1;j<=sl.length - m;j++)
if(sl.r[j].key>sl.r[j+1].key){//发生了逆序
x = sl.r[j];
sl.r[j] = sl.r[j+1];
sl.r[j+1] = x;//交换
}
}
}//冒泡排序
若在某一趟中,没有发生交换,则说明元素已经排列好,后面的就不需要继续了。因此可以改进:
void bubble_sort(SqList &sl){
int m,j;
RedType x;//用于临时存储
int flag = 1;
for(m = 1;m<=sl.length - 1&&flag == 1;m++){//总共需m趟
flag = 0;
for(j = 1;j<=sl.length - m;j++)
if(sl.r[j].key>sl.r[j+1].key){//发生了逆序
x = sl.r[j];
sl.r[j] = sl.r[j+1];
sl.r[j+1] = x;//交换
flag = 1;//如果发生了交换,就将flag置为1,则下次循环可以进行;
//否则就会停止循环,后面的就不会继续了。
}
}
}//冒泡排序
效率分析
优点:每趟结束时,不仅能挤出一个最大值到最后,还能同时理顺其他元素。
最好情况(正序):
比较次数:n-1
移动次数:0
最坏情况(逆序):
时间复杂度:最好是O(n);最坏是O(n^2);平均是O(n^2)
快速排序
算法思想
1.任取一个元素为中心,将所有比它小的放在它左边,比它大的放它右边,形成两个子表。
2.对各子表重新选择中心元素并依此规则调整,直到每个子表的元素只剩一个。
代码实现
int Partition(SqList &sl,int low,int high){
sl.r[0] = sl.r[low];
int pivotkey = sl.r[low].key;
while(low<high){
while(low<high&&sl.r[high]>=pivotkey)
high--;//将比中心点大的数保留在后面不动
sl.r[low] = sl.r[high];//将比中心点小的移到前面
while(low<high&&sl.r[low]<=pivotkey)
low++;//将比中心点小的留在前面不动
sl.r[high] = sl.r[low];//将比中心点大的移到后面
}
sl.r[low] = sl.r[0];
return low;
}
void Quick_Sort(SqList &sl, int low,int high){
if(low<high){
int pivoloc;
pivoloc = Partition(sl,low,high);
Quick_Sort(sl,low,pivoloc - 1);
Quick_Sort(sl,pivoloc + 1,high);
}
}
效率分析
时间复杂度:
空间复杂度:
不是原地排序,使用了递归,调用了系统的栈,而栈的长度取决于递归调用的深度。
稳定性:快速排序不是一种稳定的排序方法。
快速排序不适合对原本有序或基本有序的序列进行排列。会退化为冒泡排序。
划分元素的选取是影响时间性能的关键。
四、直接选择排序
算法思想
在待排序的数据中选出最大(小)的元素放在最终位置。
代码实现
void SelectSort(SqList &sl){
for(int i = 1;i<sl.length;i++){
int k = i;
for(int j = i+1;j<=sl.length;j++)
if(sl.r[j].key<sl.r[k].key)
k = j;//记录最小值位置,k就是min
if(k!=i){ //存在比sl.r[i]小的
RedType x;
x = sl.r[i];
sl.r[i] = sl.r[k];
sl.r[k] = x; //交换
}
}
}//直接交换排序
效率分析
移动次数:最好:0;最坏:3(n-1)
比较次数:
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:直接选择排序是不稳定排序。