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)的排序方法(快些归队)

  • 相关阅读:
    设置和查看时间
    通过linux ssh远程登录另一台Linux,无需密码,用证书验证
    openssl生成自签名证书
    技术集锦
    centos系统安装中文字体几种方法
    集群中几种session同步解决方案的比较
    leetcode 88. C++ 合并两个有序数组
    CycleGAN --- Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks
    剑指offer---二叉树的深度
    C++基础(2)
  • 原文地址:https://www.cnblogs.com/kqZhu/p/6876167.html
Copyright © 2011-2022 走看看