常用排序算法总结:
稳定性作用:
1.通常对只有一个key的记录来排序时,若两个记录的key相同,稳定排序不会改变排序前
后的顺序。
2.对有多个key来说,如基数排序,从次要key开始排序,在次要key排序完成后,a1排在
a2前,而a1和a2优先级大的key相同,当优先级大的key排序完成后a1会仍在a2前,保
持了稳定性。
稳定性排序的意义:
1.用于多个key的排序,如基数排序这样。
2.用于单个key的排序,如果key相同就不要改变它们的次序:
比如你要给一个结构体排序,要求按a的大小排序,a相同的话,按输入顺序输出。
a=4 b=100,a=1,b=200,a=4,b=300,a=5,b=400
你如果冒泡的话(所有的稳定排序都行),会输出
a=1 b=200,a=4 b=100,a=4 b=300,a=5 b=400
但如果快排的话很可能输出
a=1 b=200,a=4 b=300,a=4 b=100,a=5 b=400
在解决某些问题时,你会发再用不稳定排序是做不到的。
插入排序
a.直接插入排序:
#include <iostream.h> void InsertSort(int seq[],int len){ int tem; //辅助空间tem,o(1) for(int i=1;i<len;i++) //从第二个开始找 if(seq[i]<seq[i-1]){ //如果小于前一个 tem = seq[i]; //提取出来 seq[i] = seq[i-1]; //先后移一格 for(int j=i-2;j>=0&&tem<seq[j];j--)//从前两个开始往前找,直到大于等于时停止 seq[j+1] = seq[j]; //边找边后移 seq[j+1] = tem; //放入正确位置 } } void main() { int len = 8; int seq_test[] = {5,9,8,10,30,1,45,34}; InsertSort(seq_test,len); for(int i=0;i<len;i++) cout<<seq_test[i]<<endl; }
b.Shell排序:
//shell就是用的直接插入思路,只是多加一个增量dk,dk=1时就是直接插入排序(基本有序思想) void ShellSort(int seq[],int len,int dk){ int tem; for(int i=dk;i<len;i++) if(seq[i]<seq[i-dk]){ tem = seq[i]; seq[i] = seq[i-dk]; for(int j=i-2*dk;j>=0&&tem<seq[j];j-=dk) seq[j+dk] = seq[j]; seq[j+dk] = tem; } }
选择排序
a.简单选择排序:
void SelectSort(int seq[],int len){ int tem,k; //K为最小值的下标 for(int i=0;i<len-1;i++){ //总共进行len-1次排序 k = i; for(int j=i+1;j<len;j++) if(seq[j]<seq[k]) k = j; if(k!=i){ tem = seq[i]; seq[i] = seq[k]; seq[k] = tem; } } }
b.堆排序(完全二叉树):
void HeapAdjust(int seq[],int left,int right){ //左边界,右边界 int head; head = seq[left]; //要排序的头结点 for(int i=left*2;i<=right;i*=2){ //i当作上指针,left当作下指针 if(i<right&&seq[i]<seq[i+1]) i++; if(head>=seq[i]) break; seq[left] = seq[i]; left = i; } seq[left] = head; } void HeapSort(int seq[],int len){ int tem,i; for(i=len/2;i>0;i--) //将无序二叉树调整为大顶堆 HeapAdjust(seq,i,len); for(i=len;i>1;i--){ //边排列边调整 tem = seq[1]; seq[1] = seq[i]; seq[i] = tem; HeapAdjust(seq,1,i-1); } }
交换排序
a.冒泡排序:
void BubbleSort(int seq[],int len){ int tem; for(int i=0;i<len-1;i++) //排序要走的趟次,满足len-1趟即可 for(int j=0;j<len-1-i;j++)//排序范围,起始0~len-1,且每次减1 if(seq[j]>seq[j+1]){ tem = seq[j]; seq[j] = seq[j+1]; seq[j+1] = tem; } } 也许你会问:为什么冒泡排序最好情况下时间复杂度为o(n)?因为还可以这样来改进: void BubbleSort2(int seq[],int len){ int tem; int FLAG; //增加一位标志 for(int i=0;i<len-1;i++){ FLAG = 0; //每趟初始为0 for(int j=0;j<len-1-i;j++) if(seq[j]>seq[j+1]){ tem = seq[j]; seq[j] = seq[j+1]; seq[j+1] = tem; FLAG = 1; //交换过说明序列无序,置为1 } if(!FLAG) //为0说明已经排序完成,直接返回 return; } }
b.快速排序:
int Partition(int seq[],int low,int high){ //一次划分排序 int pivotkey = seq[low]; //取最低位为轴点 while(low<high){ //循环直到low high相遇 while(low<high&&seq[high]>=pivotkey) high--; //首先从最高点开始找 seq[low] = seq[high]; while(low<high&&seq[low]<=pivotkey) low++; seq[high] = seq[low]; } seq[low] = pivotkey; //最终位置,此时low=high return low; //返回划分界点 } void QuickSort(int seq[],int low,int high){ //递归直到low=high时结束排序 int pivotloc; //划分界点位置 if(low<high){ pivotloc = Partition(seq,low,high); QuickSort(seq,low,pivotloc-1); QuickSort(seq,pivotloc+1,high); } }
其它:
1.用得较多的3种算法:快排,堆排,归并排序(时间nlogn)
快排:最常用的排序算法,速度通常也是最快的。
最坏:O(n^2)
空间复杂度:O(n*logn)
不稳定
堆排:特别适用于数据量很大的场合(百万级数据)。因为快排和归并排序都是基于递归的,数据量很大的情况下容易发生堆栈溢出。
空间复杂度:O (1)
不稳定
归并排序:稳定
空间复杂度:O(n)
值得注意的是,它是一种稳定的排序算法。
与前两种排序算法不同的是,归并排序需要额外的数组开销。