• 排序算法合集(冒泡,选择,插入,堆排,快排)


    1、冒泡排序

        最初在学c语言时,老师就教的这个排序算法,原理比较简单:从数组下标为0处开始遍历,相邻之间进行比较,若a[i]>a[i+1],则exchange(a[i],a[i+1]),当然也可以将小的往后传递,将此过程不断进行,那么最后数组就有序了。

    要点:(1)每遍历一遍,末尾就得到一个最大值(或最小值),那么接下来的遍历是不是每次都减少一个元素就好了,因为后边的已经排好序了啊。

          (2)遍历n-1遍就排好序了,因为最后一遍只剩一个元素了,它一定放那儿,所以最后一遍就不用遍历了。

    当然如果数据小,又懒得优化,多进行几遍也一样可以排序的,比如这样:

         

     1 for(int i=0;i<n;i++){                              //遍历了n遍
     2 
     3           for(int j=0;j<n-1;j++){                     //每次比较当前元素和下一个元素,所以循环结束条件为n-1
     4 
     5              {
     6 
     7                  if(a[j]>a[j+1])
     8 
     9                  {    int t=a[j];
    10 
    11                       a[j]=a[j+1];
    12 
    13                       a[j+1]=t;    }
    14 
    15               }
    16 
    17        }

     那么标准的冒泡排序,就是算法复杂度为n*(n-i)次也就是n^2如下:

     

     1        for(int i=0;i<a.length-1;i++){                              //遍历n-1遍就够了
     2 
     3           for(int j=0;j<a.length-i-1;j++){               //每次比较当前元素和下一个元素,每遍历一遍少比较一个元素,所以循环结束条件为n-i-1
     4 
     5              {
     6 
     7                  if(a[j]>a[j+1])
     8 
     9                  {    int t=a[j];
    10 
    11                       a[j]=a[j+1];
    12 
    13                       a[j+1]=t;    }
    14 
    15               }
    16 
    17        }

    2、选择排序

        这个也是学c语言的时候和冒泡一起学的,它和冒泡算法复杂度一样,原理为:从数组下标为0处开始遍历,每次取出剩下元素的最大值(或最小值)放在当前位置,也就是说,第一次取最大的放第一个位置,第二次取次大的放第二个位置....执行n-1次就好了,最后一个只能放最后了。

    标准选择排序代码如下:

              

     1  for(int i=0;i<a.length-1;i++){               //遍历n-1遍就够了
     2 
     3               int k=i;                                 //k为剩下元素中的最大值下标,初始化为当前位置
     4 
     5               for(int j=i+1;j<a.length-1;j++){        //找出剩下元素中的最大值下标
     6 
     7                  if(a[j]>a[k])
     8 
     9                     k=j;    
    10 
    11                  }
    12 
    13              if(k!=i){                               //若最大值不是当前位置的值,则交换,每次将最大值放前边
    14 
    15               int t=a[k];
    16 
    17                   a[k]=a[i];
    18 
    19                   a[i]=t;  
    20 
    21                 }
    22 
    23           }

    3、插入排序

       适用于较少元素时,效率比较高,原理:从数组下标为0处开始遍历,每次保证已经遍历的元素为有序序列,即每次将要遍历的数插入到前边数列的合适位置,保证已经遍历的元素为有序数列。如:遍历第一个元素,因为目前前边只有这一个元素,所以它是有序的,遍历第二个元素,和第一个元素比较,看它插在他前边还是后边,这样就保证已经遍历的元素都有序了,之后的类似,和前边的比较,然后插入合适的位置。

       插入排序代码如下:

         

     1  for(int i=1;i<a.length-1;i++){       //从下标1开始,一个元素的时候一定有序啊
     2 
     3       int j=i-1;                          // j初始值为前一个元素,因为要将前边的元素一一和当前元素比较
     4 
     5       int k=a[i];                         //记录当前位置元素的值,因为如果后边要移动的话会覆盖的
     6 
     7       while(j>0&&a[j]>k){                //将大于当前元素值的都依次向前移一位,好空出适合的位置给当前元素值
     8 
     9           a[j+1]=a[j];
    10 
    11           j--;
    12 
    13       }
    14 
    15      a[j+1]=k;                          //因为j处元素值小于等于k (j处跳出循环的),所以k的适合位置为j+1
    16 
    17    }

    4、堆排序

       要想理解堆排序,首先你要知道最大堆,要想理解最大堆,你得知道二叉树。

      二叉树:每个节点最多有俩个孩子节点。

      最大堆:父亲节点的值总是大于孩子节点的值。

      当然在这里二叉树的存储结构不是链表,是使用数组存的:(1)数组下标为0处是根节点。

                                                          (2)父亲节点下标*2为左孩子下标,父亲节点下标*2+1为右孩子下标。

                                                            根据这俩条准则我们就可以将二叉树存在数组了。

    堆排序原理:我们知道最大堆的性质(父亲节点的值总是大于孩子节点的值),那么根节点处不就是当前数列的最大值吗,那么我们每次取根节点的值放在末尾,然后将最大堆的大小-1,更新最大堆,取根节点放后边.....不断执行这个过程,直到最大堆中只剩一个元素,此时数组就是一个有序数组了。

    根据原理可以看出我们需要编的操作有(1)建最大堆  (2)更新最大堆,其实建立最大堆就是不断更新最大堆的过程,如果我们将每个结点都执行一遍更新最大堆操作(即父亲节点的值总是大于孩子节点的值,不符合的话将父亲节点与最大的孩子交换位置),当然执行顺序必须是从下往上,然后只需从非叶子节点开始执行就好了(非叶子节点就是有孩子的结点)。

      堆排序代码如下:

           

     1 //更新最大堆操作
     2 
     3           void dfDui(int x) {                         //参数为父亲节点下标
     4 
     5             int lchild=x*2;                           //左孩子下标
     6 
     7             int rchild=x*2+1;                         //右孩子下标
     8 
     9             int max=x;                                //最大值下标初始为父亲下标
    10 
    11           if(lchild<size&&a[lchild]>a[max])           //比较找出最大值
    12 
    13                max=lchild;
    14 
    15           if(rchild<size&&a[rchild]>a[max])
    16 
    17               max=rchild;
    18 
    19           if(max!=x){           //若父亲节点为最大值,则符合性质,否则交换,将最大值移到父亲节点处,然后因为孩子节点处已改变,更新此节点。
    20 
    21             int t=a[max];
    22 
    23                 a[max]=a[x];
    24 
    25                 a[x]=t;
    26 
    27             dfDui(max);
    28 
    29            }
    30 
    31        }
    32 
    33    //建最大堆操作
    34 
    35           void creatDui(){
    36 
    37           for(int i=a.length/2+1;i>=0;i--){     //叶子结点数为结点总数一半且都在最后(可以从孩子节点下标的算法为父亲节点*2看出),因此                     duDui(i);                       // a.length/2+1处开始为非叶子节点     
    38 
    39           }
    40 
    41       }
    42 
    43    //堆排序操作
    44 
    45        void sort(){
    46 
    47         creatDui();                                //建最大堆
    48 
    49         for(int i=size-1;i>=1;i--){               //每次将第一个数与最后一个数交换,然后大小-1,更新已经改变的根节点
    50 
    51             int t=a[0];
    52 
    53                 a[0]=a[size-1];
    54 
    55                 a[size-1]=t;
    56 
    57                 size--;
    58 
    59                 dfDui(0);
    60 
    61          }
    62 
    63      }

    5、快速排序

        快速排序是实际运用中用的最多的算法,虽然它在最坏的情况下会达到n^2,但它的平均性能非常好,期望时间复杂度为nlgn,而且隐含的常数因子非常小,并且是原址排序。

        快速排序原理:从一组数中任意选出一个数,将大于它的数放右边,小于它的数放左边,然后再从左边和右边的俩组数中分别执行此操作,知道组中元素数为1,此时,数组就是有序的了。

    从原理中可以清楚的看出此操作为递归操作,其代码如下:

               

     1   int partsort(int a[],int l,int r){                 //将比a[r]小的元素放左边,比它大的放右边,最后把a[r]放中间
     2 
     3                 int i=l;                                        //i为比a[r]大的元素的下标,初始为开始位置l
     4 
     5                 for(int j=l;j<r;j++){                           
     6 
     7                  if(a[j]<=a[r]){                               //如果元素比a[r]小则和大的元素交换位置,目的让小的放一起,大的放一起
     8 
     9                    int t=a[j];                                 //可以自己手运行几遍这个循环
    10 
    11                        a[j]=a[i];
    12 
    13                        a[i]=t;
    14 
    15                        i++;
    16 
    17                   }
    18 
    19                }
    20 
    21               int t=a[i];                                   //a[i]为小元素和大元素的交界处,将a[r]与之交换
    22 
    23                   a[i]=a[r];
    24 
    25                   a[r]=t;
    26 
    27               return i;                                    //返回交界处下标,继续排前边的和后边的这俩组
    28 
    29       }
    30 
    31            void quicksort(int a[],int l,int r){
    32 
    33                if(l<r){                                   //递归结束条件为组中只剩一个元素
    34 
    35                int p=partsort(a,l,r);                    //分成俩组返回交界处
    36 
    37                    quicksort(a,l,p-1);                  //继续分左边
    38 
    39                    quicksort(a,p+1,r);
    40 
    41                 }
    42 
    43            }

    主函数中调用代码为,若数组大小为n,则:quicksort(a,0,n-1);

    待续待续哈..........

  • 相关阅读:
    簡單的內容移動展示
    PHPExcel讀取excel數據
    javascript touch事件
    vue学习笔记之初识vue——模板声明与绑定
    vue学习笔记初识vue——使用HTML模板
    vue学习笔记初识vue——创建vue示例
    vue学习笔记之初识vue——渐进式前端框架
    vue整屏滑动组件
    CSS如何居中div??
    vue+百度地图API
  • 原文地址:https://www.cnblogs.com/llsq/p/7400587.html
Copyright © 2020-2023  润新知