• [转载]浅谈各种排序


    排序的现实意义每个人都知道,今日突然对各种排序算法产生了兴趣,于是谈下我所接触到的一些排序算法:

    选择,插入,冒泡,快排,堆排,归并,基数,计数,bogo。

    代码除个别都由本人所写,若有 bug 请您指正。

    个人建议,一定要看看计数排序,别的快排和归并排想法也很好。选择和插入很基本,也是最容易理解的,最逆天的就是 bogo 了,一定要看哦!!

    注:将input函数和output函数还有主函数写在了冒泡里,以后就直接写排序函数的代码了,其他部分的代码的输入输出函数、主函数和冒泡排序基本一致。

    1.冒泡排序

      描述:依次比较相邻的两个数,将小数放在前面,大数放在后面。这样每次都能将最大的数放在最后,多次迭代,一个有序的序列就出来了~~

        其实冒泡排序就是选择排序,而且还比选择多比较了很多次。

      实现:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 int a[10000];
     4 int n;/* 共有 n 个需要排序的数 */
     5 void input(){
     6     int i;
     7     scanf("%d", &n);
     8     for(i = 0;i < n;++i){
     9       scanf("%d", &a[i]);
    10     }
    11     return ;
    12 }
    13 void output(){
    14     /* 将排序好的结果输出,以验证 */
    15     int i;
    16     for(i = 0;i < n; ++i){
    17       fprintf(stdout,"%d ",a[i]);
    18     }
    19     printf("\n");
    20     return ;
    21 }
    22 void bubble_sort(){
    23     int i, j;
    24     for(i = 0;i < n;++i){/* 执行n此迭代 */
    25       for(j = 0;j < n-i-1; ++j){/* 比较相邻两个数,判断是否互换 */
    26           if(a[j] > a[j+1]){
    27             int temp = a[j];
    28             a[j] = a[j+1];
    29             a[j+1] = temp;
    30           }
    31       }
    32     }
    33 }
    34 int main(){
    35     input();
    36     bubble_sort();
    37     output();
    38     exit(0);
    39 }

      时间复杂度为n方,基本不需要额外空间。

    2. 插入排序:

      描述:想法非常简单,就像抓牌一样,来一张牌,把它放在前面 <= 它、后面 > 它的地方。当所有牌都抓完之后,自然就是一手有序的牌了。

      实现:

      

     1 /* 插入排序 */
     2 void insertion_sort(){
     3     int i;
     4     for(i = 0;i < n; ++i){
     5       int temp;
     6       scanf("%d", &temp);
     7       a[i] = temp;
     8       int j;
     9       /* 每次输入一个数字,都进行比较 */
    10       for(j = 0; j < i; ++j){
    11           if(temp < a[j]){/* 找到前面 <= 它,后面 > 它的位置 */
    12             int k = i;
    13             /* 因为是数组,所以需要每位后移,个人猜想用链表实现,会优化许多 */
    14             for(;k > j; --k){
    15                 a[k] = a[k-1];
    16             }
    17             a[j] = temp;
    18             break;
    19           }
    20       }
    21     }
    22     return ;
    23 }

      时间复杂度,毫无疑问的 n 方的。

      空间上基本上都是没有什么大的消耗了。

      用链表实现的话,在最优情况时间复杂度可以达到O(n)的,不过我写的太弱了。

    3. 选择排序

      描述:每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好的数列的最后,直到全部待排序的数据元素排完。

      实现:

     1 #define MAX 0xfffffff
     2 void selection_sort(){
     3     int aim[10000];
     4     int i;
     5     int min = MAX;/* 每次选出的最小值 */
     6     for(i = 0;i < n;++i){
     7       int j;
     8       int temp;/* 记录最小值的下标,以便修改其值*/
     9       for(j = 0;j < n;++j){ /* 选出数组中的最小值 */
    10           if(a[j] < min){
    11             min = a[j];
    12             temp = j;
    13           }
    14       }  
    15       a[temp] = MAX;
    16       aim[i] = min;
    17       min = MAX;
    18     }
    19     /* 将排序好的结果输出,以验证 */
    20     for(i = 0;i < n; ++i){
    21       fprintf(stdout,"%d ",aim[i]);
    22     }
    23     printf("\n");
    24     return ;
    25 }

      时间复杂度 n方,空间需要多开一个数组,来存放目标数据。

    4. 快速排序

      描述:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的数据要小,然后再按此方法对这两部分分别递归进行快速排序,然后使整个数组变成有序序列。

      实现:

     1 /* 此代码来自《代码之美》,个人以为,确实是非常美 */
     2 void quick_sort(int l, int u){/* l是较低的下标,u是较高的下标 */
     3     int i, m;
     4     if(l >= u) return;
     5     swap(l, randint(l, u));/* swap(i,j)是交换a[i],a[j],i,j不变 */
     6     m = l;/* 从与第一个值比较,小的放左边,大的放右边 */
     7     for(i = l+1; i <= u; ++i)
     8       if(a[i] < a[l])
     9           swap(++m, i);
    10     swap(l, m);/* 将第一个值放在它该在的地方 */
    11     quick_sort(l, m-1);/* 递归 */
    12     quick_sort(m+1, u);
    13 }

      时间复杂度:期望时间 O(n log n) , 最坏情况O(n2) ; 对于大的、乱数列表一般公认是最快的已知排序

      之所以会是log n,是由于二分了。。。

    5. 堆排序

      描述:堆排序就像是选择排序一样,每次都是选出最大或最小的值。不过选出最大或最小的值的方法不是线性的去找出来,而是用一种特殊的数据结构——堆,来管理算法执行中的信息。

      堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。小于的就是小顶堆,大的就是大顶堆。于是就要建堆,调整出一个顶堆,然后把顶点的值取出来,再调整,再取,直到所有的元素都被取出来,一个有序的序列就出来了。

      调整就是为了满足堆的性质。我们假定左右两颗子树都是大顶堆。但这时,顶点的值可能小于其孩子,于是就违反了大顶堆的性质,所以就和孩子中大的那个换一下位置,然后再递归判断两个子树的,然后再递归。最后就调整出一个大顶堆啦~~

      实现:

     1 void adjust(int ind, int len){
     2          /* 置i为要筛选的节点 */
     3         int i = ind;
     4         /* c中保存i节点的左孩子 */
     5      /* +1的目的就是为了解决节点从0开始而他的左孩子一直为0的问题 */
     6         int c = i * 2 + 1; 
     7         while(c < len){/* 未筛选到叶子节点 */
     8                 /* 如果要筛选的节点既有左孩子又有右孩子并且左孩子值小于右孩子 */
     9                 /* 从二者中选出较大的并记录 */
    10                 if(c + 1 < len && a[c] < a[c + 1])
    11                         c++;
    12                 /* 如果要筛选的节点中的值大于左右孩子的较大者则退出 */
    13                 if(a[i] > a[c]) break;
    14                 else{
    15                         /* 交换 */
    16                         int temp = a[c];
    17                         a[c] = a[i];
    18                         a[i] = temp;
    19                         /* 重置要筛选的节点和要筛选的左孩子 */
    20                         i = c;
    21                         c = 2 * i + 1;
    22                 }
    23          }
    24         return;
    25 }
    26  
    27 void heap_sort(int n){
    28         /* 初始化建堆, i从最后一个非叶子节点开始 */
    29         for(int i = n / 2; i >= 0; i--)
    30                 adjust(i, n);
    31         for(int j = 0; j < n; j++){
    32                 /* 交换 */
    33                 int t = a[0];
    34                 a[0] = a[n - j - 1];
    35                 a[n - j - 1] = t;
    36                 /* 筛选编号为0 */
    37                 adjust(0, n - j - 1);
    38          }
    39 }

      时间复杂度nlog(n)。

    6. 归并排序

      描述:有两个有序序列,把它们合并成一个,很简单是吧。只要把比较两个序列第一个数的大小,然后取小的放到新的序列里,原来的序列中删除这个数,再比,直到一个序列的所有元素都空了,把另一个序列剩余的全都放过去。一个新的有序序列就成了~~,归并排序基于的就是这个原理,用分治法的思想,递归的排序出来。

      实现:

     1 void merge(int first, int mid, int last){/* 两个有序数组分别是a[first...mid]和a[mid+1...last] */
     2     int temp[last - first + 1];/* 新的有序数组 */
     3     int i, j, k=0;
     4     for(i = first, j = mid + 1; i <= mid && j <=last ;){
     5         if(a[i] < a[j]){
     6             temp[k++] = a[i++]; 
     7         }else{
     8             temp[k++] = a[j++];
     9         }
    10         if(i == mid + 1){
    11             while(j <= last)
    12                 temp[k++] = a[j++];
    13             break;
    14         }else if(j == last + 1){
    15             while(i <=mid)
    16                 temp[k++] = a[i++];
    17             break;
    18         }
    19     }
    20     for(i = 0; i < k; i++)/* 把排好序的数组,放回这些数据该在的地方 */
    21         a[first + i] = temp[i];
    22 }
    23 void merge_sort(int first, int last){
    24     if(first < last){
    25         int mid = (first + last) / 2;
    26         /*传说中的分治法*/
    27         merge_sort(first, mid);
    28         merge_sort(mid + 1, last);
    29         merge(first, mid, last);/* 合并 */
    30     }    
    31 }

      时间复杂度 O(n log n),需要 O(n)的额外空间。

    7. 基数排序

      描述:原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。我理解的还是不够深。。。

      实现:

     1 /* 此代码来此维基百科,我没能写出来。。。 */
     2 #include <iostream>
     3  
     4 using namespace std;
     5  
     6 const int base=10;
     7  
     8 struct wx
     9 {
    10         int num;
    11         wx *next;
    12         wx()
    13         {
    14                 next=NULL;
    15         }
    16 };
    17  
    18 wx *headn,*curn,*box[base],*curbox[base];
    19  
    20 void basesort(int t)
    21 {
    22         int i,k=1,r,bn;
    23         for(i=1;i<=t;i++)
    24         {
    25                 k*=base;
    26         }
    27         r=k*base;
    28         for(i=0;i<base;i++)
    29         {
    30                 curbox[i]=box[i];
    31         }
    32         for(curn=headn->next;curn!=NULL;curn=curn->next)
    33         {
    34                 bn=(curn->num%r)/k;
    35                 curbox[bn]->next=curn;
    36                 curbox[bn]=curbox[bn]->next;
    37         }
    38         curn=headn;
    39         for(i=0;i<base;i++)
    40         {
    41                 if(curbox[i]!=box[i])
    42                 {
    43                         curn->next=box[i]->next;
    44                         curn=curbox[i];
    45                 }
    46         }
    47         curn->next=NULL;
    48 }
    49  
    50 void printwx()
    51 {
    52         for(curn=headn->next;curn!=NULL;curn=curn->next)
    53         {
    54                 cout<<curn->num<<' ';
    55         }
    56         cout<<endl;
    57 }
    58  
    59 int main()
    60 {
    61         int i,n,z=0,maxn=0;
    62         curn=headn=new wx;
    63         cin>>n;
    64         for(i=0;i<base;i++)
    65         {
    66                 curbox[i]=box[i]=new wx;
    67         }
    68         for(i=1;i<=n;i++)
    69         {
    70                 curn=curn->next=new wx;
    71                 cin>>curn->num;
    72                 maxn=max(maxn,curn->num);
    73         }
    74         while(maxn/base>0)
    75         {
    76                 maxn/=base;
    77                 z++;
    78         }
    79         for(i=0;i<=z;i++)
    80         {
    81                 basesort(i);
    82         }
    83         printwx();
    84         return 0;
    85 }

      时间复杂度是 O(k*n)的,k是数字位数。

    8. 计数排序
      描述:这是我最喜欢的排序算法了,典型的用空间换时间的。一看代码就知道了。

      实现:

     1 void counting_sort(){
     2     int i;
     3     int b[100001];/* 只能排序100000以下的数字 */
     4     memset(b, 0 ,sizeof(b));/* 临时存储区 */
     5     int max = -1; /* 记录需要排序的最大数 */
     6     int min = 0xffffffff;/* 记录需要排序的最小数 */
     7     for(i = 0; i < n; ++i){
     8         b[a[i]]++;/* 核心的就这一句 */
     9         max = a[i] > max ? a[i] : max;
    10         min = a[i] < min ? a[i] : min;
    11     }
    12     int k = 0;
    13     for(i = min ; i <= max; ++i){/* 按顺序将排序的数还原出来 */
    14         while(b[i]--)
    15             a[k++]=i;
    16     }
    17 }

    在k以内的数,时间复杂库为O(n),O(n)有木有啊,还容易实现,我最喜欢了,就是太需要空间了。

    9. bogo 排序

      描述:这个排序是真的超神了,想象一下,把一副扑克向天上一扔,掉桌子上后收拾收拾,看看是不是顺序的,不是的话再扔。。。。

      实现:真心不会。。。。

      时间复杂度维基百科上说是其平均时间复杂度是 O(n × n!),在最坏情况所需时间是无限。

      据说在量子计算机里,这个算法是O(1)的,表示不明原理。

    附上维基百科上排序的链接:https://zh.wikipedia.org/wiki/%E6%8E%92%E5%BA%8F

    还有很多我没有听说过,根本不会的。。。继续努力吧

    声明:

      本文为 iddmx 对自己所学的排序算法的整理和实现。

      本文档欢迎自由转载,但请务必保持本文档完整或注明来之本文档。本文档未经 iddmx 同意,不得用于商业用途。最后,如果您能从这个简单文档里获得些许帮助,iddmx 将对自己的一点努力感到非常高兴;iddmx 水平有限,如果本文档中包含的错误给您造成了不便,iddmx 在此提前说声抱歉。

      祝身体健康,工作顺利。☺

  • 相关阅读:
    Mysql的row_format(fixed与dynamic)
    no-referrer-when-downgrade什么意思
    a标签属性 rel=noopener noreferrer
    深入理解ob_flush和flush的区别
    win7下php7.1运行getenv('REMOTE_ADDR')fastcgi停止运行
    学会了这项技能,你就能获得任何想要的信息!
    原来游戏技术行业最大的秘密竟然是...
    王亮:游戏AI探索之旅——从alphago到moba游戏
    入门系列之在Ubuntu 14.04上备份,还原和迁移MongoDB数据库
    入门系列之在Ubuntu上安装Drone持续集成环境
  • 原文地址:https://www.cnblogs.com/kba977/p/3433755.html
Copyright © 2020-2023  润新知