zoukankan      html  css  js  c++  java
  • 各排序算法总结(简单选择,冒泡,快速,希尔排序,堆排序)

     1)冒泡排序bubble_sort
            1.原理
                假设对a[N]进行排序
                依次比较相邻两个数,小数放前,大数放前。
                *1    从头开始进行第一轮比较,则得到最后一个位置是所有数中的最大的一个数;       
                    需要比较的次数是N-1,为什么是N-1?因为,总共是N个数,数组下标是从0开始,
                    如果比较最后两个数据,判断条件:if(a[N-1-1] > a[N-1]),a[N-1]就是数组的最后一个数了,
                    如果比较次数是N,则执行该论最后一对数据比较时就是if(a[N-1] > a[N]),
                    众所都知,a[N]中是没有a[N],所以次数会得到一个意想不到的排序,里面会多一个垃圾值,垃圾值的产生就是a[N]。
                    所以此时必须用N-1。
                *2    从头开始进行第二轮比较,则得到倒数第二个位置是所有数中的次最大数;       
                    需要比较的次数是(N-1)-1,为什么是(N-1)-1?因为,解释同上。
                *3    从头依次进行第三轮比较,则得到倒数第三个位置是所有数中的次次最大数;       
                    需要比较的次数是N-1-1-1,为什么是(N-1)-1-1?因为,解释同上。
                *N-1    依次从头开始比较,直至比较完。
            2.代码实现过程
                void bubble_sort(int a[], int N)            //N是数组a的长度
                {
                    int i, j, tmp;

                    for(i = 0; i < N-1; i++)            //进行比比较的趟数,此时的N-1如果换成N,对程序影响不大,只不过多一次循环而已
                    {
                        for(j = 0; j < N-i-1; j++)        //开始进行比较,此时的N-i-1不能换成N-i,如果写成N-i,会产生一个垃圾值,解释在*1中
                        {
                            if(a[j] > a[j+1])
                            {
                                tmp    = a[j];
                                a[j]   = a[j+1];
                                a[j+1] = tmp;
                            }
                        }
                    }
                   
                    return;
                }
       
        2)简单选择排序select_sort
            1.原理
                假设对a[N]进行排序
                假定选一个最小的,依次把后面的数与它进行比较,如果后面的比假定选的最小的还小,则进行交换这两个数。每进行一轮就要找出一个最小的数。
                *1    第一轮    假定第一个数是“最小的”,然后依次把后面的数与这个“最小的”的进行比较,如果比这个“最小的”小,首先标记该数为“最小的”,
                        然后把这个“最小的”与前面的“最小的”交换值,目的是保证第一个数是后面所有数中最小的一个。
                *2    第二轮    因为已经知道第一个是最小的,这时从第二个开始寻找最小的,假定第二个是最小的,然后依次把后面的数与这个“最小的”的进行比较,
                        如果比这个“最小的”小,首先标记该数为“最小的”,然后把这个“最小的”与前面的“最小的”交换值,目的是保证第二个数是后面所有数中最小的一个。

                *N    第N轮    步骤同上。
               
                说明    是进行N轮比较还是N-1轮比较,影响不大,原因是:该数组中共有N个数,如果对N-1个数进行了排序处理,即第N-1个数是后面中最小的一个数,
                    剩余的唯一的第N个数肯定是所有数中最大的,显然它应该放在最后一个位置上,没必要再进行一次循环。如果是进行N轮比较,无非是程序多执行一次循环,
                    其实实际上这次循环也没有执行,因为在子循环中无法满足循环条件,不能执行循环体。

            2.代码实现过程
                void select_sort(int a[], int N)        //N是数组a的长度
                {
                    int i, j, min, tmp;

                    for(i = 0; i < N; i++)            //从头开始比较
                    {
                        min = i;            //把第i个数当作最小的,把下标标记为min
                        for(j = i+1; j < N; j++)    //把a[min]依次和它后面的数进行比较
                        {
                            if(a[j] < a[min])    //如果后面的某个值比a[min]小
                            {
                                min = j;    //重新标记最小值下标
                            }
                            tmp = a[min];        //交换
                            a[min] = a[i];        //原来min = i的,如果没有找到比a[min]小的,就不进行交换(如果没找到,i还是和min一样,执行该三条语句没作用)
                            a[i] = tmp;        //只有a[j] < a[min]成立,执行了min = j,这时才能交换(这时min的已经改为j,不再是i了,所以执行才有作用)
                        }                //自己表达欠缺,上面三句表达不够明确,只要自己研读程序,因该能明白其中奥妙
                    }

                    return;
                }

        3)快速排序quick_sort
            1.原理
                假定对a[N]排序
                快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)。
                实现快速排序需要两个函数,分别是:partition()和quick_sort()
               
                函数:            int partition(int a[], int i, int j)
                函数功能:        把数组分为两段,左段都是比基准小的数,但是左段的数是无序的;右段都是比基准大的数,但是右段的数是无序的。
                函数大概实现过程:       
                        *1    选取一个基准(pivot),基准一般选取第一个数a[0];

                        *2    定义两个变量i、j;
                            令j从数组尾部向左扫描,直到遇到一个数比基准小的,进行交换。目的是:使比基准小的都放在基准左边。
                            令i从数组开头向右扫描,直到遇到一个数比基准大的,进行交换。目的是:使比基准大的都放在基准右边。
                            进行多次循环后,最终i的值就是基准应该放的位置。
                            原因是:我们假定的基准是数组的第一个数a[0],a[0]也许既不是数组中最大的数也不是数组中最小的数,
                            也就是基准的位置应该在数组中的某个位置,经过多次循环后,i不断往后递增,当不满足循环条件时,i就停止了,此时i就是基准的位置
                            (表达欠缺,还需要自己揣摩)
                   
                函数:            void quick_sort(int a[], int left, int right)
                函数功能:        实现排序
                函数大概实现过程:
                            递归的对每一段进行排序

            2.代码实现过程
                int partition(int a[], int i, int j)
                {           
                    int pivot;                //基准
           
                    pivot = a[i];               
                    while(i < j)
                    {
                        while(i < j && a[j] > pivot);   
                        {
                            j--;            //如果右边的比基准大,左移j
                        }
                        a[i] = a[j];            //如果遇到了右边的比基准小,和基准交换
                   
                        while(i < j && pivot <= a[i]);
                        {
                            i++;            //如果左边的基准小,右移i
                        }
                        a[j] = a[i];            //如果遇到了左边的比基准大,和基准交换
                    }
                    a[i] = pivot;

                    return i;
                }

                void quick_sort(int a[], int left, int right)
                {
                    int pivottag;
               
                    if(left < right)
                    {
                        pivottag = partition(a, left, right);
                        quick_sort(a, 0, pivottag-1);   
                        quick_sort(a, pivottag+1, right);
                    }

                    return;
                }

      4)希尔排序
       在直接插入排序算法中,每次插入一个数,使有序序列只增加1个节点, 并且对插入下一个数没有提供任何帮助。

      如果比较相隔较远距离(称为 增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除 多个元素交换。

       算法先将要排序的一组数按某个增量d分成若干组,每组中 记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量 对它进行,在每组中再进行排序。

       当增量减到1时,整个要排序的数被分成 一组,排序完成。
       下面的函数是一个希尔排序算法的一个实现,初次取序列的一半为增量, 以后每次减半,直到增量为1。

     void shell_sort(int *x, int n)

     {

        int h, j, k, t;

         for (h=n/2; h>0; h=h/2) /*控制增量*/

       {  

         for (j=h; j<n; j++) /*这个实际上就是上面的直接插入排序*/  

          {     

             t = *(x+j);   

             for (k=j-h; (k>=0 && t<*(x+k)); k-=h)

            {      

              *(x+k+h) = *(x+k);

            }     

                *(x+k+h) = t;     

          }

      }

    }

    5)堆排序

    堆排序是一种树形选择排序,是对直接选择排序的有效改进。 堆的定义如下:具有n个元素的序列(h1,h2,...,hn),当且仅当 满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1)(i=1,2,...,n/2) 时称之为堆。在这里只讨论满足前者条件的堆。

    由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项。完全二叉树可以 很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。 初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储顺序, 使之成为一个堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点 交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点 的堆,并对它们作交换,最后得到有n个节点的有序序列。

    从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素 交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数 实现排序的函数。

    堆排序是不稳定的。算法时间复杂度O(nlog2n)。

     功能:渗透建堆 输入:数组名称(也就是数组首地址)、参与建堆元素的个数、从第几个元素开始

    void sift(int *x, int n, int s)

    {

       int t, k, j;

       t = *(x+s);   /*暂存开始元素*/

         k = s;      /*开始元素下标*/

       j = 2*k + 1;   /*右子树元素下标*/

      while (j<n) {

          if (j<n-1 && *(x+j) < *(x+j+1))         /*判断是否满足堆的条件:满足就继续下一轮比较,否则调整。*/   

              {      j++;     }

            if (t<*(x+j))          /*调整*/   

               {    

                   *(x+k) = *(x+j);      k = j;     /*调整后,开始元素也随之调整*/    

                    j = 2*k + 1;   

                }   

             else          /*没有需要调整了,已经是个堆了,退出循环。*/   

              {     

                  break;

                }

         }
              *(x+k) = t;    /*开始元素放到它正确位置*/

          }

    /* 功能:堆排序 输入:数组名称(也就是数组首地址)、数组中元素个数 */

    void heap_sort(int *x, int n) {

        int i, k, t; int *p;

           for (i=n/2-1; i>=0; i--) {  

                sift(x,n,i);       /*初始建堆*/

            }
           for (k=n-1; k>=1; k--) {  

                 t = *(x+0);      /*堆顶放到最后*/  

                *(x+0) = *(x+k);   

               *(x+k) = t;  

               sift(x,k,0); /*剩下的数再建堆*/

               }

             }

  • 相关阅读:
    41. 缺失的第一个正数
    101. 对称二叉树 38. 外观数列
    leecode 394. 字符串解码 java版本
    敏感词过滤(java)
    vue项目中async、await+promise来将异步转为同步
    vue项目目录详解及自定义事件
    如何搭建vue项目
    Sublime Text 3 离线安装插件
    拿到别人的vue项目如何跑起来?
    js设置定时器和清除定时器
  • 原文地址:https://www.cnblogs.com/hainanlinyu/p/3293541.html
Copyright © 2011-2022 走看看