排序算法总结

1插入类排序

(1)直接插入排序

  算法大致流程:给定初始序列L,L从前往后依次取出一个数据,将其直接插入到有序序列中。

  算法的复杂度分析:时间复杂度:最坏的情况下,每从无序序列中取一个元素,就要遍历一遍有序序列,复杂度为O(n2);最好的情况下,每从无序序列中取出一个元素,直接放在了有序序列的后面,也就是不用遍历有序序列,此时,复杂度为O(n)。所以,平均复杂度为O(n2)。

           空间复杂度:O(1)

 1 #include<cstdio>
 2 #define MAXSIZE 100
 3 void out(int number[],int n)
 4 {
 5     for(int i=0; i<n; ++i)
 6         printf("%d	",number[i]);
 7 }
 8 void InsertSort(int number[],int n)
 9 {
10     int i,j;
11     int temp,flag=0;
12     for(i=1; i<n; ++i)
13     {
14         flag=0;
15         temp=number[i];
16         for(j=0; j<i; ++j)
17         {
18             if(number[i]<number[j])
19             {
20                 for(int k=i; k>j; --k)
21                     number[k]=number[k-1];
22                 number[j]=temp;
23                 flag=1;
24             }
25             if(flag==1)
26                 break;
27         }
28     }
29     out(number,n);
30 }
31 
32 int main()
33 {
34     int number[MAXSIZE];
35     int n;
36     scanf("%d",&n);
37     for(int i=0; i<n; ++i)
38         scanf("%d",&number[i]);
39     InsertSort(number,n);
40     return 0;
41 }

(2)折半插入排序

  算法大致流程:给定初始序列L,L从前往后依次取出一个数据,将其插入到有序序列中,到这里和直接插入排序相同。不同之处就在于,插入到有序序列中时,由于其是有序的,我们就可以采用二分的方式快速找到该元素在有序序列中的位置。

  算法的复杂度分析:时间复杂度:最坏的情况下,每从无序序列中取一个元素,就要遍历n/2个有序序列元素,复杂度为O(n2),最好的情况和直接插入排序一样,也是O(n)。平均复杂度为:O(n2)。

           空间复杂度:O(1)

(3)希尔排序(缩小增量排序)

  算法大致流程:给定初始序列L,假设下标为0、1、2、3……n,例如,首先以5为增量分割序列,所以,0、5、10、15……为一组;1、6、11、16……为一组;2、7、12、17……为一组,将数列分为5组,这五组序列分别进行排序,构成一趟希尔排序。然后对排序后的整个数列,以3为增量分割序列,所以,0、3、6、9……一组;1、4、7、……一组,2、5、8、……一组,分为3组,对这三组数据分别进行排序,又做了一趟希尔排序。最后直到增量为1,就相当于整个序列作为一组进行一趟排序,从而使最终序列有序。

  注:对于增量的选择,增量序列中没有除1以外的公因子,例如5、3、1就符合,8、4、2、1有公因子2,不符合。

  算法的复杂度分析:时间复杂度:O(nlog2n)

           空间复杂度:O(1)

2交换类排序

(1)冒泡排序

  算法大致流程:给定初始序列L(含有n个元素),先用第0个元素和第1个元素比较,如果第0个元素比第1个元素大,则两个元素交换,否则什么都不做,然后第1个元素和2个元素比较,如果第1个元素大,则交换,一直进行下去,直到比较到n-2和n-1,完成一趟冒泡,最大的元素a被就冒上来,放在了正确的位置上(即最大的放在了最后)。然后采用相同的方法完成第二次冒泡,直到比较到n-3和n-2,那么除了a意外的其他所有元素的最大值最终放在了n-2的位置上……最后,只剩2个元素时,比较,交换,算法完成。在一趟冒泡过程中,没有元素交换,也结束算法。

  算法的复杂度分析:时间复杂度:最坏情况下,每次都要交换,复杂度为O(n2),最好的情况是已经有序,复杂度为O(n)。平均复杂度为:O(n2)。

           空间复杂度:O(1)

 1 #include <stdio.h>
 2 
 3 void swap(int *a,int *b)
 4 {
 5     int temp;
 6     temp=*a;
 7     *a=*b;
 8     *b=temp;
 9 }
10 
11 int main()
12 {
13     int i,j,n;
14     int number[100000];
15     scanf("%d",&n);
16     for(int i=0;i<n;++i)
17         scanf("%d",&number[i]);
18     for(i=n-1;i>0;--i)
19     {
20         for(j=0;j<i;++j)
21         {
22             if(number[j]>number[j+1])
23             {
24                 swap(&number[j],&number[j+1]);
25             }
26         }
27     }
28     for(i=0;i<n;++i)
29     {
30         printf("%d ",number[i]);
31     }
32     return 0;
33 }

(2)快排

  算法的大致流程:给定初始序列L,任意选定某个数作为基准(一般选择序列的第一个数L[0]),从L[1]开始向右依次比较,第一次发现某个数L[k]比基准大的时候,交换L[0]和L[left],此时的位置记为“left=left+1”,然后再开始从右往左查看,L[n-1]、L[n-2]……,当发现第一个比L[0]小的L[right]时候,交换L[0]和L[right],此时记录位置为“right=right+1”,然后从L[left]开始向右看,重复上述过程,直到遇到比L[0]大的元素,记录位置,从L[right]开始向左看……直到left和right相遇,一趟快排完成,此时L[0]左边的元素均比L[0]小,L[0]右边的元素均比L[0]大。L[0]将L分成左右两部分,L和R。

  对L和R分别执行和L一样的操作,直到最后分得的L和R中只有两个元素。

  算法的复杂度分析:时间复杂度:最好的情况下是:O(nlog2n),最坏的情况下是O(n2),待排序列越接近无序,效率越高,越有序,效率越低,排序的趟数和初始序列有关。平均复杂度为O(nlog2n)。

           空间复杂度:需要借助栈,O(log2n)

 1 #include<stdio.h>
 2 int n;
 3 void quick_sort(int number[],int left,int right)
 4 {
 5     int a,b;
 6     int start;
 7     int temp;
 8     if(left>=right)
 9     {
10         return;
11     }
12     temp=left;
13     a=left;
14     b=right;
15     start=number[left];
16     while(left<right)
17     {
18 
19         while(number[right]>=start&&right>left)
20         {
21             right--;
22         }
23         number[temp]=number[right];
24         temp=right;
25         while(number[left]<=start&&left<right)
26         {
27             left++;
28         }
29         number[temp]=number[left];
30         temp=left;
31     }
32     number[temp]=start;
33     quick_sort(number,a,right-1);
34     quick_sort(number,left+1,b);
35 }
36 
37 int main()
38 {
39     int i,j;
40     int number[100000];
41     scanf("%d",&n);
42     for(int i=0;i<n;++i)
43         scanf("%d",&number[i]);
44     quick_sort(number,0,n-1);
45     for(int i=0; i<n; i++)
46     {
47         printf("%d ",number[i]);
48     }
49     return 0;
50 }

3选择类排序

(1)直接选择

  算法大致流程:给定初始序列L(n个元素),从L中选择最小的值,和第一个元素交换,完成一次选择,此时有序序列的元素个数为1,无序序列的元素个数为n-1。接着对无序序列做相同的操作……直到无序序列个数为0,算法结束。

  算法的复杂度分析:时间复杂度:平均复杂度为O(n2)。

           空间复杂度:O(1)

 1 #include<stdio.h>
 2 void swap(int *a,int *b)
 3 {
 4     int temp;
 5     temp=*a;
 6     *a=*b;
 7     *b=temp;
 8 }
 9 
10 int main()
11 {
12     int i,j,n;
13     int number[1000];
14     int point;
15     scanf("%d",&n);
16     for(i=0;i<n;i++)
17     {
18         scanf("%d",&number[i]);
19     }
20 
21     for(i=0;i<n;i++)
22     {
23         int min=100000;
24         for(j=i;j<n;j++)
25         {
26             if(number[j]<=min)
27             {
28                 min=number[j];
29                 point=j;
30             }
31         }
32         swap(&number[point],&number[i]);
33     }
34 
35     for(i=0;i<n;i++)
36     {
37         printf("%d ",number[i]);
38     }
39     return 0;
40 }

(2)堆排序

  堆:可看做一棵完全二叉树,对于每个非叶节点,满足父亲比孩子小(最小堆)或者父亲比孩子大(最大堆)。

  算法大致流程:首先,用初始序列构建一棵完全二叉树,从最后一个非叶节点往前依次调整——比较他和它两个孩子的大小关系,若比两个孩子小,则将该父节点和两个孩子中较大的交换,即构建最大堆。

  建立好最大堆时,第一趟排序完成,此时堆顶元素为最大元素,将他和最后一个元素交换,剩下的元素还是一棵完全二叉树,但是可能不满足堆的定义,将其重新构建为最大堆,(此时不满足最大堆的元素只有交换的那个值,所以,只需要调整那个值就可以了),将堆顶元素和最后一个元素交换,将剩下的元素构建最大堆,直到剩余元素只有1个,算法结束。

  算法的复杂度分析:时间复杂度:最好和最坏的情况下复杂度均为O(nlog2n)。

           空间复杂度:O(1)

4归并排序

二路归并排序

  算法的大致流程:首先将初始序列L中每个元素作为一组,共分为n组。n组中相邻的两两归并,形成n/2(向上取整)组,将这些组内的元素进行排序,然后将这n/2(向上取整)组元素再两两归并,形成n/4(向上取整)组,将组内元素排序……重复这个操作,直到n/2k=1,即最后归并为整个序列L,使之有序,算法结束。

  算法的复杂度分析:时间复杂度:和初始序列无关,最好和最坏的情况下复杂度均为O(nlog2n)。

           空间复杂度:需要转存序列,空间复杂度为O(n)

5基数排序

  算法大致流程:基数排序就是按多个关键字进行排序,这里举两个栗子:

  1、扑克牌(除大小王)排序:按照两个关键字:花色和数字。先按花色将牌分为4堆,然后将这4堆牌分别按照数字大小排序,最终使整副牌有序。

  2、数字排序:按照低位优先的顺序,分别进行“分配”和“收集”操作,从低位到高位所有位都比完之后,算法完成,整个序列有序。

  例如:对278、109、063、930、589、184、505、269、008、083进行基数排序。

  首先,纵观全部的数字,0~9都有,那么我们就设置10和桶来接收这些位。

  (1)先从最低位(个位)开始,即按照个位数进行分配,其余先不管,分配结果如下:

0 1 2 3 4 5 6 7 8 9
 元素  930    

063

083

 184  505    

278

008 

109

589

269

  收集的时候,每个桶看成队列,执行“先进先出”,所以收集结果为“930、063、083、184、505、278、008、109、589、269”

  (2)按照十位分配,结果如下:

0 1 2 3 4 5 6 7 8 9
元素

505

008

109

    930    

063

269

278

083

184

589

 

  收集:“505、008、109、930、063、269、278、083、184、589”

  (3)按照百位进行分配,结果如下:

0 1 2 3 4 5 6 7 8 9
元素

008

063

083

109

184

269

278

   

505

589

      930

   收集结果:“008、063、083、109、184、269、278、505、589、930”

  至此,所有的位都比较完,算法结束,可见,最后的序列就是从小到大有序的。

  该算法的思想就是,每次比较某一位时,“分配”的目的是保证下一位小的在前面,“收集”的目的是保证,该为小的在前面,这样,所有的收集分配结束后,小的在前面,大的在后面。

  算法的复杂度分析:时间复杂度:平均和最坏的情况下复杂度均为O(d*(n+rd))。其中,d为关键字位数,栗子中d=3。n为元素数,栗子中,n=10。rd关键字取值范围,栗子中rd=10(即0~9)。

           空间复杂度:需要rd个桶,空间复杂度为O(rd)。

原文地址:https://www.cnblogs.com/wktwj/p/4914718.html