常见算法排序,冒泡排序,快排,堆排,归并排序

来源:互联网 发布:手机加油软件 编辑:程序博客网 时间:2024/05/24 01:31
排序


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

0 0
原创粉丝点击