一.排序算法
1.直接插入排序
算法思想:直接插入排序是利用顺序查找来确定r[i]在r[1..i-1]有序序列区中插入的位置,将第i个记录的关键字Ki与前面记录r[1]~r[i-1]的关键字从后面向前顺序进行比较,将所有关键字大于Ki的记录依次向后移动一个位置,知道遇到一个关键字小于或等于Ki的记录,该记录的位置即为r[i]的位置。
c语言代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 void InsertSort(RecordList L) 2 { 3 for(int i=2;i<=L.length;i++) 4 { 5 if(L.r[i].Key<L.r[i-1].key) 6 { 7 L.r[0] = L.r[i]; //监视哨备份待查记录 8 for(j=i-1;L.r[0].key<L.r[j].key;j--) 9 L.r[j+1] = L.r[j]; //记录后移 10 L.r[j+1]=L.r[0]; //插入到正确的位置 11 12 } 13 } 14 }
java语言实现:
1 public class InsertSort { 2 public static void main(String[] args) { 3 int [] arr = new int[]{4,5,2,1,0,3,2,5,7,98,0,12,34,6,7,4}; 4 insertSort(arr); 5 for (int i = 0; i < arr.length; i++) { 6 System.out.println(arr[i]); 7 } 8 } 9 public static void insertSort(int [] arr){ 10 for (int i = 1; i < arr.length; i++) { 11 if(arr[i] < arr[i - 1]){ 12 int temp = arr[i]; 13 for (int j = i ; j >= 0; j--) { 14 if(temp < arr[j]) { 15 arr[j + 1] = arr[j]; 16 arr[j] = temp; 17 } 18 } 19 } 20 } 21 } 22 }
时间复杂度:在最坏情况下(倒序)时间复杂度为O(n^2)。
空间复杂度:它只需要一个元素的辅助空间,即监视哨r[0],用于元素位置的交换,所以空间复杂度为O(1)。
稳定性:插入排序是稳定的,因为具有相同值得元素必然插在同一值得前一个元素的后面。
2.折半插入排序
算法思想:此算法思想与直接插入算法思想类似,将直接插入法每次折半后,再进行插入。
c语言代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 void BiInsertSort(RecordList L) 2 { 3 for(int i=2;i<L.length;i++) 4 { 5 L.r[0] = L.r[i]; //监视哨备份待查记录 6 if(L.r[i].key>L.r[i-1].key) 7 { 8 low=1; high=1; //折半查找r[i]的位置 9 while(low<high) 10 { 11 mid = (low+high)/2; 12 if(L.r[i].key<L.r[mid].key) 13 high=mid-1; 14 else low=mid+1; 15 } 16 for(j=i-1;j>=low;j-) 17 L.r[j+1] = L.r[j];//记录后移 18 L.r[low]=L.r[0];//插入正确的位置 19 } 20 } 21 }
时间复杂度:O(n^2)
空间复杂度与稳定性和直接插入法一致。
3.希尔排序
算法思想:先将整个待排的序列分割成若干个子序列,分别进行插入排序,然后依次缩减增量再进行排序,待整个序列中元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。
c语言代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 void shellInsert(RecordList L,int dk) 2 { 3 for(i=dk+1;i<=L.length;i++) 4 if(L.r[i].key<L.r[i-dk].key) 5 { 6 L.r[0] = L.r[i]; 7 for(j=i-dk;j>0&&(L.r[0].key<L.r[j].key);j-=dk;) 8 L.r[j+dk] = L.r[j]; 9 L.r[j+dk]=L.r[0]; 10 } 11 } 12 void ShellSort(RecordList L,int dlta[],intt) 13 { 14 for(k=0;k<L.length;t++) 15 shellInsert(L,dlta); 16 }
时间复杂度:O(n^2)
空间复杂度与直接插入法一致,只需要一个辅助空间。
稳定性:在排序过程中两个相同的关键字可能发生顺序的改变,所以不稳定。
4.冒泡排序法
算法思想:对待排序的记录的关键字两两进行比较,只要发现两个记录为逆序之后进行交换,知道没有逆序为止。
C语言代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 void BubbleSort(RecordList L) 2 { 3 flag = 1; 4 for(int i=1;i<L.length-1&&flag;i++) 5 { 6 flag=0; 7 for(j=1;j<L.length-i;j++) 8 if( L.r[j].key>L.r[j+1].key) 9 } 10 t=L.r[j]; 11 L.r[j]=L.r[j+1]; 12 L.r[j+1] = t; 13 flag=1; 14 { 15 16 } 17 }
时间复杂度:O(n^2)
空间复杂度:需要一个辅助空间进行交换,为O(1)。
5.快速排序法
算法思想:从待排序列中任意选择一个记录,以该记录的关键字作为“枢轴”,凡是关键字小于枢轴的记录均移动至该记录之前,反之凡是关键字大于轴枢的记录均移至该记录之后。
c语言代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 一趟排序 2 int QKpass(RecordList L,int low,int high) 3 { 4 L.r[0]=L.r[low]; 5 while(low<high) 6 { 7 while(low<high&&L.r[high].key>=L.r[0].key) --high; 8 L.r[low] = L.r[high]; 9 while(low<high&&L.r[low].key<=L.r[0].key) ++low; 10 L.r[high] = L.r[low]; 11 } 12 L.r[low]=L.r[0]; 13 return low; 14 } 15 //快速排序 16 void QKSort(RecordList L,int low,int high) 17 { 18 if(low<high) 19 { 20 pos=QKpass(L,low,high); 21 QKSort(L,low,pos-1); 22 QKSort(L,pos+1;high); 23 } 24 }
时间复杂度:最坏情况下:O(n^2)
最好情况下:T(n)=θ(nlogn)
空间复杂度:快排总共需要分割log2n次,即需要log2n个辅助空间记录枢轴的位置,所以复杂度为O(log2n)。
稳定性:不稳定。
6.简单选择排序法
算法思想:从第一个记录开始,将后面n-1个记录进行比较,找到最小的和第一个记录交换,从第二个记录开始,重复以上步骤。
c语言代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 void sellectSort(RecordList L) 2 { 3 for(int i=1;i<L.length;i++) 4 { 5 k=i; 6 for(j=i+1;j<L.length;j++) 7 if(L.r[j]<L.r[i]) 8 k=j; 9 if(k!=i) 10 { 11 t=L.r[i]; 12 L.r[i]=L.r[k]; 13 L.r[k]=t; 14 } 15 } 16 }
时间复杂度:O(n^2)
空间复杂度:需要一个辅助空间进行交换,故为O(1).
7.堆排序
算法思想及步骤:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 void HeapSort(RecordList L) 2 { 3 CreatHeap(L); 4 for(i=L.length;i>=2;i--) 5 { 6 L.r[0]=L.r[1]; 7 L.r[1]=L.r[i]; 8 L.r[i]=L.r[0]; 9 } 10 } 11 //建初始堆 12 void CreatHeap(RecordList L) 13 { 14 for(i=L.length/2;i>=1;i--) 15 HeapAdjust(L,i,L,length); 16 } 17 //堆的筛选 18 void HeapAdjust(RecordList L,int s,int m) 19 { 20 t=L.r[s]; 21 for(j=2*s;j<=m;j*=2) 22 { 23 if(j<m&&L.r[j].key>L.r[j+1].key) 24 j++; 25 if(t.key<=L.r[j].key) 26 break; 27 L.r[s]=L.r[j]; 28 s=j; 29 } 30 L.r[s]=t; 31 }
时间复杂度:O(nLog2n)
空间复杂度:O(1)
稳定性:不稳定。
8.桶排序
桶排序的前提条件:必须知道待排的数据的范围。有以下两种情况:
(1)待排数据大小的范围比较小的情况,例如:有5个待排的序列(a[4,7,9,4,3]),每个数字的大小都在0~10之间.
算法思想:创建10个桶,即大小为10的一维数组b,初始化为0,依次遍历原数组(4,7,9,4,3),将遍历的每个数字对应下标的数组值加1(例如:遍历第一个数字为4,则b[4]+1;第二个数为7,则b[7]+1;.....)最后得到数组内b[3]=1;b[4]=2;b[7]=1;b[9]=1;最后将b数组打印出来就是所排的序列(例如:b[3]=1,则打印一个3,b[4]=2打印两个4)。
c语言代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 void TSort(int a[], int n) 2 { 3 int t=0; 4 int b[k] = {0}; //k为桶的个数,自行定义 5 for (int i = 0; i< n; i++) 6 { 7 t = a[i]; 8 b[t]++; 9 } 10 for (int i = 1; i< 10; i++) 11 for (int j = 1; j <= b[i]; j++) 12 printf("%d ", i); 13 }
(2)待排数据范围比较大的情况,例如有20个待排数据,每个数据的范围为0~999,那么如果按照上面的方法,则需要创建999个桶,能用到的桶<=20,显然不是最优算法,那么就需要第二种解法:
算法思想:创建10个桶即创建大小为10 的一维数组,进行三次桶排:
第一次:将20个数的元素本身依次放入个位数对应下标的数组中(例如:将123放入b[3]中)每个桶中可能有多个数。
再讲20个数字依次存入原来的数组中。
第二次:将第一次排好的数组中的20个数的元素本身依次放入十位数对应下标的数组中(例如:将123放入b[2]中)每个桶中可能有多个数,再讲20个数字依次存入原来的数组中。
第三次:将第二次排好的数组中的20个数的元素本身依次放入百位数对应下标的数组中(例如:将123放入b[1]中)每个桶中可能有多个数,再讲20个数字依次存入原来的数组中。即已经排序好。