zoukankan      html  css  js  c++  java
  • 常见算法排序,冒泡排序,快排,堆排,归并排序

    排序


    1. 插入类排序
    1、直接插入排序         O(n2)      O(n)              O(n2)

    每次将一个待排序元素按照关键字大小插入到已经排序的序列中去。

    void insert(int a[],int n)
    {
         int i,j;
         int temp;
         for(i=1;i<=n;i++)    //从第二个元素开始,因为第一个元素肯定是有序的
         {
              temp=a[i];     //temp存储a[i]防丢失
              j=i-1;
              while(j>=0 && temp<a[j])    //在i之前的元素已经是有序的
              {
                   a[j+1] = a[j];
                   j--;     //一个个往后移
              }
              a[j+1]=temp;
         }
    }


    1. 折半插入排序        O(n2)      O(n)              O(n2)

    折半查找法寻找元素插入位置。与1最大不同在于,1用的是顺序查找法。

    1. 希尔排序
        缩小增量排序。实质就是分组插入排序。把记录按步长分组,对每组记录采用直接插入排序方法进行排序。随着步长逐渐减小,所分成的组包含的记录越来越多,当步长的值减小到 1 时,完成排序。一个好的增量序列有以下特征:最后一个增量必须为1;尽量避免序列中的值,尤其是相邻的值,互为倍数。

    1. 交换类排序

    1、冒泡排序         O(n2)      O(n)              O(n2)

             结束条件是一趟排序中未发生元素交换。

       基本原理:依次比较两个相邻的数,若是按从大到小排序,将大数放前,小数放后,直到比较到最后两位数。重复上述步骤,直到一趟排序中未发生元素交换为止。

    //普通冒泡
    void bubble_sort(int a[],int n)//n为数组a的元素个数
    {
      //一定进行N-1轮比较
      for(int i=0; i<n-1; i++)
      {
           //每一轮比较前n-1-i个,即已排序好的最后i个不用比较
           for(int j=0; j<n-1-i; j++)
           {
                 if(a[j] > a[j+1])
                {
                     int temp = a[j];
                     a[j] = a[j+1];
                     a[j+1]=temp;
                }
           }
      }
    }

    //优化实现
    void bubble_sort_better(int a[],int n)//n为数组a的元素个数
    {
      //最多进行N-1轮比较
      for(int i=0; i<n-1; i++)
      {
          bool isSorted = true;
          //每一轮比较前n-1-i个,即已排序好的最后i个不用比较
          for(int j=0; j<n-1-i; j++)
         {
            if(a[j] > a[j+1])
            {
               isSorted = false;
               int temp = a[j];
               a[j] = a[j+1];
               a[j+1]=temp;
            }
         }
         if(isSorted) break; //如果没有发生交换,说明数组已经排序好了
      }
    }

    2、快速排序         O(nlog2n)       O(nlog2n)       O(n2)

           分治的思想。分治法通常有3步:Divide(分解子问题的步骤)Conquer(递归解决子问题的步骤)、Combine(子问题解求出来后合并成原问题解的步骤)。而求解递归式的三种方法有:

    (1)替换法:主要用于验证递归式的复杂度。

    (2)递归树:能够大致估算递归式的复杂度,估算完后可以用替换法验证。

    (3)主定理:用于解一些常见的递归式。

    快排中,基准的左边全是比它小的,右边都是大的。三种取基准的方法:第一个或最后一个元素,中间元素,随机元素。

    待排序列越接近有序算法效率越。当每次划分时,若都能分成两个等长的子序列时,效率会达到最大。最理想的方法是,选择的基准恰好能把待排序序列分成两个等长的子序列

    递归进行的,递归需要的辅助,因此辅助空间为O(log2n)

    void quiksort(int a[],int start,int end)
    {
      int low = start;
      int high= end;
      int key = a[low];

      if( start < end)
      {
           while(low <high && key<=a[high])
           high--; //如果high所在的值不比key小,就往左移动
           a[low] = a[high]; //遇到比key小的high值,就把a[high]值给a[low],a[low]原本的值已经在key里面了
           while(low < high && a[low] <= key)
                low++;
           a[high]= a[low];
           a[low]=key; //此时的low的左边全是比key小的,右边全是比key大的,把key值放大当前low位置
           quiksort(a,start,low-1);//左边重复
           quiksort(a,low+1,end);//右边重复
      }
      else
           return;
    }


    1. 选择类排序

    1. 简单选择排序        

    从头至尾顺序扫描未排序序列,选最小的元素与第一个进行交换。

    1. 堆排序     O(nlog2n)       O(nlog2n)       O(nlog2n)

    从无序序列所确定的完全二叉树的第一个非叶子节点开始,从右向左,从下往上,对每个节点进行调整(大顶堆,小顶堆),直到无序序列中只剩下一个元素。
    优点:a)最坏情况下时间复杂度也是O(nlog2n),这是它相对快排最大的优点。
    b)空间复杂度为O(1),这是在所有时间复杂度为O(nlog2n)中最小的。

    适用于元素很多的场合,比如100万个元素中选前10个最大的。
           堆排序相对快速排序:1、最好最坏情况下时间复杂度都为O(nlog2n),不会出现快排最坏的O(n2)    2、堆排序所需的辅助空间少,是O(1),而快排是O(log2n)


    4.    归并排序     O(nlog2n)       O(nlog2n)       O(nlog2n)

         采用分治法的思想。将n个元素的序列划分为两个序列,再将两个序列划分为4个序列,直到每个序列只有一个元素,最后,再有序序列两两归并成一个有序的序列。
       归并排序时间复杂度与初始序列无关。都是O(nlog2n)。空间复杂度为O(n)
       内存空间不足的时候,能够并行计算的时候使用归并排序。

    //归并,将有二个有序数列a[start…mid]和a[mid+1…end]合并。把结果放到temp里面
    void Merge(int a[],int temp[], int start, int mid, int end)
    {
      int i = start, j=mid+1, k = start;
      while(i!=mid+1 && j!=end+1)
      {
          if(a[i] >a[j])
            temp[k++] = a[j++];     //从小到大排序,a[j]放入temp[k],然后j,k都后移一位
          else
            temp[k++] = a[i++];
      }
      while(i != mid+1)
          temp[k++] = a[i++];
      while(j != end+1)
          temp[k++] = a[j++];
      for(i=start; i<=end; i++)
          a[i] = temp[i];
    }

    //内部使用递归
    void MergeSort(int a[], int tempArr[], int start, int end)
    {
      int mid;
      if(start < end)
      {
          mid = (start+ end) / 2;
          MergeSort(a, temp, start, mid);
          MergeSort(a, temp, mid+1, end);
          Merge(a, temp, start, mid, end);
      }
    }

    1. 基数排序
    多关键字排序。两种,最高位优先、最低位优先。 
    适用场景:序列中元素个数很多,但是组成元素的关键字的取值范围比较小。比如取值范围0~9.

    总结
    1、平均时间复杂度为O(nlog2n)  快些归队
                         快速  希尔 归并 堆排序
    2、不稳定     快些选
                  快速 希尔 选择类排序(简单选择、堆排序)
    3、特殊的空间复杂度  其他都是O(1)      快速O(log2n) 归并O(n)
    4、一趟保证一个元素到底最终位置         交换类    选择类
    5、比较次数与初始序列无关     简单选择       折半插入
    6、排序趟数与初始序列     交换类排序
    7、元素基本有序(正序)             直接插入       冒泡
    8、当数据规模n较小    直接插入排序 简单选择排序
    9、当数据规模n较大,采用时间复杂度为O(nlog2n)的排序方法(快些归队)

  • 相关阅读:
    【BZOJ 4151 The Cave】
    【POJ 3080 Blue Jeans】
    【ZBH选讲·树变环】
    【ZBH选讲·拍照】
    【ZBH选讲·模数和】
    【CF Edu 28 C. Four Segments】
    【CF Edu 28 A. Curriculum Vitae】
    【CF Edu 28 B. Math Show】
    【CF Round 439 E. The Untended Antiquity】
    【CF Round 439 C. The Intriguing Obsession】
  • 原文地址:https://www.cnblogs.com/kqZhu/p/6876167.html
Copyright © 2011-2022 走看看