写在前面:

排序是计算机程序设计中的一种重要操作,它的功能是将一个数据元素的随意序列,又一次排列成一个按keyword有序的序列。因此排序掌握各种排序算法很重要。

对以下介绍的各个排序,我们假定全部排序的keyword都是整数、对传入函数的參数默认是已经检查好了的。仅仅是简单的描写叙述各个算法并给出了详细实现代码。并未做其它深究探讨。

基础知识:

因为待排序的记录数量不同,使得排序过程中设计的存储器不同,可将排序方法分为两大类:一类是内部排序,指的是待排序记录存放在计算机随机存储器中进行的排序过程。还有一类是外部排序,指的是待排序记录的数量非常大。以致内存一次不能容纳所有记录。在排序过程中尚需对外存进行訪问的排序过程。

在排序的过程中需进行下列两种基本操作:1、比較两个keyword的大小;2、将记录从一个位置移动至还有一个位置。

操作1对大多数排序方法来说都是必要的。而操作2能够通过改变记录的存储方式予以避免。

待排序记录序列可有下列3种存储方式:1、待排序的一组记录存放在地址连续的一组存储单元上。2、一组待排序记录存放在静态链表中,记录之间的次序关系由指针指示,则实现不须要移动记录,仅须要改动指针就可以。3、待排序记录本身存储在一组地址连续的存储单元内,同一时候另设一个指示各个记录存储位置的地址向量。在排序过程中不移动记录本身。而移动地址向量中这些记录的”地址“,在排序结束之后再依照地址向量中的值调整记录的存储位置。

算法分析:

1、插入排序:

基本思想:将一个记录插入到已排好序的有序表中。从而得到一个新的、记录数增1的有序表。

时间复杂度为O(n^2),若待排记录序列为正序,时间复杂度可提高至O(n);空间上仅仅须要一个记录的辅助空间。

a、直接插入排序

演示样例代码1:

void InsertionSort(ElementType A[], int N)
{  int j, P;  ElementType Tmp;//记录辅助空间  for(P = 1; P < N; P++){  Tmp = A[P];   for(j = P; j > 0 && A[j - 1] > Tmp; j--)//将一个记录插入已排好序的有序表中  A[j] = A[j - 1];  A[j] = Tmp;  }
}  

演示样例代码2:

void insertionsort(ElementType A[], int N)
{  for(int i = 1; i < N; i++){  int tmp = A[i]; //记录辅助空间  int j = i - 1;  while(j > -1 && A[j] > A[i]){  A[j+1] = A[j];  //将一个记录插入已排好序的有序表中  --j;  }  A[j+1] = tmp;  }  return;
}  

插入排序算法简单,且easy实现。当待排序记录的数量n非常小时。这是一种非常好的排序方法。但n非常大时,则不宜採用直接排序。由于直接排序。基本的时间消耗在“比較”和“移动”上,因此。在直接排序的基础上,从降低“比較”和“移动”这两种操作的次数着眼,可得“折半插入排序”、“2-路插入排序”、“表插入排序”等。

b、折半插入排序

因为插入排序的基本操作是一个有序表中进行查找和插入,这个"查找"操作可利用"折半查找"来实现。由此进行的插入排序称之为折半插入排序。

2、希尔排序

基本思想:先将整个待排记录序列切割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。能够看出希尔排序事实上仅仅是改进了的插入排序,因此上面的插入排序也被称为直接插入排序。

特点:子序列的构成不是简单地“逐段切割”。而是将相隔某个“增量”的记录组成一个子序列。它通过比較相距一定间隔的元素来工作;各趟比較所用的距离随着算法的进行而减小,直到仅仅比較相邻元素的最后一趟排序为止。

演示样例代码

void Shellsort(ElementType A[], int N)
{  int i, j, Increment;  ElementType Tmp;  for(Increment = N / 2; Increment > 0; Increment /= 2){  for(i = Increment; i < N; i++){  Tmp = A[i];   for(j = i; j >= Increment; j -= Increment){  if(Tmp < A[j - Increment])     A[j] = A[j - Increment];  else  break;  }  A[j] = Tmp;  }     }
} 

上面给出的演示样例中选择的排序增量是使用shell建议的序列:N/2和Increment/2。

使用希尔增量时希尔排序的最坏情形执行时间为O(n^2)。

3、冒泡排序

基本思想:首先将第一个记录的keyword和第二个记录的keyword进行比較。若为逆序,则将两个记录交换之,然后比較第二个记录和第三个记录的keyword。依次类推,直至第n-1个记录和第n个记录的keyword进行过比較为止。上述过程称做第一趟冒泡排序,其结果使得keyword最大的记录被安置到最后一个记录的位置上。

然后进行第二趟冒泡排序。对前n-1个记录进行相同操作,其结果是使keyword次大的记录被安置到第n-1个记录的位置上。一般地。第i趟冒泡排序是从1到n-i+1依次比較相邻两个keyword,并在“逆序”时交换相邻记录,其结果是这n-i+1个记录中keyword最大的记录被交换到第n-i+1的位置上。判别冒泡排序结束的条件应该是“在一趟排序过程中没有进行过交换记录的操作”。

演示样例代码1:

void bubblesort(ElementType A[], int N)
{  int i, j;  ElementType tmp;  for(i = 0; i < N; i++)  {  for(j = 0; j < N-i; j++){      if(A[j] > A[j+1]){  tmp = A[j];   A[j] = A[j+1];  A[j+1] = tmp;  }  }  }
}  

演示样例代码2:

void bubblesort(ElementType a[], int n)
{  int j;  bool flag;  ElementType tmp;  flag = true;  while(flag){  flag = false;  for(j = 1; j < n; j++){  if(a[j-1] > a[j]){  tmp = a[j-1];  a[j-1] = a[j];    a[j] = tmp;  flag = true;  }     }     n--;  }
}  

冒泡排序的时间复杂度为O(n^2)。

效率比較底下。当数据量比較小的时候,能够採用冒泡排序。

4、简单选择排序

基本思想:每一趟在n-i+1(i=1,2,…,n-1)个记录中选取keyword最小的记录作为有序序列中第i个记录。

直接选择排序和直接插入排序类似,都将数据分为有序区和无序区,所不同的是直接插入排序是将无序区的第一个元素直接插入到有序区以形成一个更大的有序区,而直接选择排序是从无序区选一个最小的元素直接放到有序区的最后。

演示样例代码:

void Selectsort(int a[], int n)
{  int i, j, nMinIndex, tmp;  for(i = 0; i < n; i++){  nMinIndex = i;  for(j = i + 1; j < n; j++)     if(a[j] < a[nMinIndex])  nMinIndex = j;  tmp = a[i];  a[i] = a[nMinIndex];  a[nMinIndex] = tmp;  }
}  

简单选择排序的时间复杂度是O(n^2)。

5、高速排序

基本思想:高速排序是对冒泡排序的一种改进。

它的基本思想是。通过一趟排序将待排记录切割成独立的两部分,当中一部分记录的keyword均比还有一部分记录的keyword小。则可分别对这两部分记录继续进行排序,以达到整个序列有效。

一趟高速排序的详细做法是:附设两个指针low和high,它们的初值分别为low和high。设枢轴记录的keyword为pivotkey,则首先从high所指位置起向前搜索找到第一个keyword小于prvotkey的记录和枢轴记录互相交换,然后从low所指位置起向后搜索,找到第一个keyword大于privotkey的记录和枢轴记录互相交换,反复这两步直至low=high为止。

演示样例代码1:

void Swap(ElementType *left, ElementType *right)
{  ElementType temp = *left;  *left = *right;  *right = temp;
}  int Partition(ElementType A[], int low, int high)
{  ElementType pivotkey = A[low];  while(low < high){  while(low < high && A[high] >= pivotkey)    high--;  Swap(&A[low], &A[high]);  while(low < high && A[low] <= pivotkey)  low++;  Swap(&A[low], &A[high]);  }  return low;
}  void QSort(ElementType A[], int low, int high)
{  int pivotloc;  if(low < high){  pivotloc = Partition(A, low, high);   QSort(A, low, pivotloc - 1);  QSort(A, pivotloc + 1, high);  }
}  void QuickSort(ElementType A[], int low, int high)
{  QSort(A, low, high);
}  

高速排序的平均时间为O(n) = nlogn;它是眼下被觉得的最好的一种内部排序方法。

6、归并排序

基本思想:将两个或两个以上的有序表组合成一个新的有序表。2-路归并排序为例:如果初始序列含有n个记录,则可看成是n个有序的子序列,每一个子序列的长度为1,然后两两归并,得到n/2(或n/2+1)个长度为2或1的有序子序列;再两两归并,……如此反复。直至得到一个长度为n的有序序列为止。

演示样例代码:

void Merge(ElementType A[], ElementType TmpArray[], int Lpos, int Rpos, int RightEnd)
{  int i, LeftEnd, NumElements, TmpPos;  LeftEnd = Rpos - 1;  TmpPos = Lpos;  NumElements = RightEnd - Lpos + 1;  /*main loop*/  while(Lpos <= LeftEnd && Rpos <= RightEnd)  if(A[Lpos] <= A[Rpos])  TmpArray[TmpPos++] = A[Lpos++];  else  TmpArray[TmpPos++] = A[Rpos++];  while(Lpos <= LeftEnd)       /*Copy rest of first half*/  TmpArray[TmpPos++] = A[Lpos++];  while(Rpos <= RightEnd)      /*Copy rest of second half*/  TmpArray[TmpPos++] = A[Rpos++];  /*Copy TmpArray back*/  for(i = 0; i < NumElements; i++, RightEnd--)  A[RightEnd] = TmpArray[RightEnd];
}  void MSort(ElementType A[], ElementType TmpArray[], int Left, int Right)
{  int Center;  if(Left < Right){  Center = (Left + Right) / 2;      MSort(A, TmpArray, Left, Center);  MSort(A, TmpArray, Center + 1, Right);  Merge(A, TmpArray, Left, Center + 1, Right);  }
}  void Mergesort(ElementType A[], int N)
{  ElementType *TmpArray;  TmpArray = (ElementType *)malloc(N*sizeof(ElementType));  if(TmpArray == NULL){  fprintf(stderr, "no space for tmp array!\n");     return;  }  MSort(A, TmpArray, 0, N-1);  free(TmpArray);  return;  }  

归并排序的效率比較高,设数列长为N。将数列分开成小数列一共要logN步,每步都是一个合并有序的数列的过程,时间复杂度记为O(N),因此时间复杂度是O(N*LogN)。

它非常难用于主存排序,主要问题在于合并两个排序的表须要线性附加内存。在整个算法中还要花费将数据复制到暂时数组再拷贝回来这样一些附加的工作,其结果严重放慢了排序的速度。

7、堆排序

堆是具有下列性质的全然二叉树:每一个节点的值都大于或等于其左右孩子节点的值,称为大顶堆;或者每一个节点的值都小于或等于其左右孩子节点的值,称为小顶堆。

堆排序就是利用堆进行排序的方法.基本思想是:将待排序的序列构造成一个大顶堆.此时,整个序列的最大值就是堆顶 的根结点.将它移走(事实上就是将其与堆数组的末尾元素交换, 此时末尾元素就是最大值),然后将剩余的n-1个序列又一次构造成一个堆,这样就会得到n个元素的次大值.如此重复运行,便能得到一个有序序列了。 时间复杂度为 O(nlogn),好于冒泡,简单选择,直接插入的O(n^2)

该算法的主要问题在于它使用了一个附加的数组。因此,存储需求添加一倍。注意:将第二个数组拷贝回第一个数组的额外时间消耗仅仅是O(N),这不可能显著影响执行时间。这个问题是空间的问题。

演示样例代码:

#define LeftChild(i) (2*(i) + 1)  void Swap(ElementType *pa, ElementType *pb)
{  ElementType *pc = pa;  pa = pb;  pb = pc;
}  void PercDown(ElementType A[], int i, int N)
{  int Child;  ElementType Tmp;  for(Tmp = A[i]; LeftChild(i) < N; i = Child){  Child = LeftChild(i);     if(Child != N-1 && A[Child + 1] > A[Child])  Child++;  if(Tmp < A[Child])  A[i] = A[Child];  else  break;  }  A[i] = Tmp;
}  void Heapsort(ElementType A[], int N)
{  int i;  for(i = N/2; i >= 0; i--)    /*BuildHeap*/  PercDown(A, i, N);    for(i = N - 1; i > 0; i--){  Swap(&A[0], &A[i]);     /*DeleteMax*/     PercDown(A, 0, i);  }
}  

总结:

上面尽管给了7种内部排序的方法。可简单的对它们大致分为下面几类:插入排序(直接插入排序、希尔排序)、高速排序(冒泡排序、高速排序)、选择排序(简单选择排序、堆排序)、归并排序和基数排序。

各内部排序方法的比較:

1、平均时间性能而言,高速排序最佳,其所需时间最省。但高速排序的最坏情况下的时间性能不如堆排序和归并排序。而后两者相比較的结果是,在n较大时,归并排序所需时间较堆排序省,但它所需的辅助存储量最多。

2、简单排序包含除希尔排序之外的全部插入排序,冒泡排序和简单选择排序。当中以直接插入排序为最简单。当序列中的记录"基本有序"或n值较小时,它是最佳的排序方法。因此常将它和其它的排序方法,诸如高速排序、归并排序等结合在一起使用。

3、基数排序的实际复杂度可写成O(d*n)。它最适用于n值非常大而keyword较小的序列。

若keyword也非常大,而序列中大多数记录的"最高位keyword"均不同。则亦可先按"最高位keyword"不同将序列分成若干"小"的子序列,而后进行直接插入排序。

4、从方法的稳定性来比較,基数排序是稳定的内排方法,全部时间复杂度为O(n^2)的简单排序法也是稳定的。然而。高速排序、堆排序和希尔排序时性能更好的排序方法是不稳定。

版权声明:本文博客原创文章,博客,未经同意,不得转载。

转载于:https://www.cnblogs.com/hrhguanli/p/4682716.html

七内部排序算法汇总(插入排序、Shell排序、冒泡排序、请选择类别、、高速分拣合并排序、堆排序)...相关推荐

  1. 终于,把十大经典排序算法汇总了!(Java实现版)

    转载自  终于,把十大经典排序算法汇总了!(Java实现版) 最近几天在研究排序算法,看了很多博客,发现网上有的文章中对排序算法解释的并不是很透彻,而且有很多代码都是错误的,例如有的文章中在" ...

  2. 排序算法汇总(C/C++实现)

    前言:     本人自接触算法近2年以来,在不断学习中越多地发觉各种算法中的美妙.之所以在这方面过多的投入,主要还是基于自身对高级程序设计的热爱,对数学的沉迷.回想一下,先后也曾参加过ACM大大小小的 ...

  3. 【算法系列 | 2】深入解析排序算法之——插入排序

    序言 你只管努力,其他交给时间,时间会证明一切. 文章标记颜色说明: 黄色:重要标题 红色:用来标记结论 绿色:用来标记一级论点 蓝色:用来标记二级论点 决定开一个算法专栏,希望能帮助大家很好的了解算 ...

  4. 排序算法java源代码_排序算法汇总(java实现,附源代码)

    整理系统的时候发现了原来写的各种算法的总结,看了一下,大吃一惊,那时候的我还如此用心,具体的算法,有的已经模糊甚至忘记了,看的时候就把内容整理出来,顺便在熟悉一下,以后需要的时候就可以直接过来摘抄了. ...

  5. 排序算法 | 直接插入排序算法的图解、实现、复杂度和稳定性分析

    排序算法 | 直接插入排序算法的图解.实现.复杂度和稳定性分析 目录 1.直接插入排序定义 2.直接插入排序,步骤说明 3.动态图演示 4.代码实现,运行结果 5.算法分析 ① 时间复杂度分析 ② 空 ...

  6. Java常见排序算法之插入排序

    一.概述 本节由小千给大家分享Java常见排序算法之插入排序,之前我们说过排序是算法中的一部分.所以我们学习排序也是算法的入门,为了能让大家感受到排序是算法的一部分,我举个例子证明一下:比如麻将游戏, ...

  7. 【面试笔记系列】排序算法汇总

    摘要 排序算法已经是面试中被问烂的题目了,可以说经常面试都会被问到排序算法.一般面试官的问题比较宽泛,比如:"说说排序算法?". 抛出这样的一个问题有的人可能就直接回答了排序算法有 ...

  8. 插入排序 php,常用的排序算法(二)--插入排序(PHP实现)

    常用的排序算法系列 插入排序 插入排序是一种逻辑上非常好理解的排序方式,整个排序的核心就是不断在当前已经排好部分数据的数组里,找到合适的位置插入新数据.就像抓扑克牌,抓一张,然后再手里已经部分已经排好 ...

  9. 插入排序算法 java_排序算法实现-插入排序(Java版本)

    原标题:排序算法实现-插入排序(Java版本) 插入排序(英语:Insertion Sort)是一种简单直观的排序算法.它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到 ...

最新文章

  1. Linux下screen的应用
  2. 深度学习可解释性!深度taylor分解
  3. 图片居中裁剪_魔镜,魔镜,谁最美丽!利用PS图层混合模式打造图片幻觉效果
  4. python软件下载3版本-Python软件下载-Python最新版 v3.7.3 - 动力软件园
  5. html整体居中文字,html文字居中 html图片居中代码
  6. LU分解、LDLT分解和Cholesky分解
  7. 【Bugs】Hbase:File system needs to be upgraded. You have version null and I want version(habse缓冲问题)
  8. 第三代酷睿i3处理器_轻薄本CPU谁更强?英特尔21款低功耗处理器大排行!
  9. 学简单python好学吗_python好学吗
  10. 浅析LruCache原理
  11. 使用Python实现基于API的网易有道翻译功能
  12. 第十六章 综合实例——《跟我学Shiro》
  13. 谭浩强c语言课后习题笔记[1-4章]
  14. [CF765F] Souvenirs
  15. azure 微软云 规范挂载附加数据磁盘
  16. 全国银行列表json格式
  17. 6.5 地理数据可视化
  18. 【代码超详解】洛谷 P2922 [USACO08DEC]秘密消息Secret Message
  19. Dart源码学习01
  20. php获取天气组件,php获取中国天气,中国天气插件说明,根据IP获取天气情况

热门文章

  1. Apache OpenNLP下载
  2. 《利用Python进行数据分析·第2版》第13章 Python建模库介绍
  3. 企业私有云之rabbitmq高可用
  4. 中兴通讯和江苏电信携手推进SDN IPRAN创新进程
  5. 《c++语言导学》——1.3 Hello,World!
  6. unicode学习笔记
  7. commands moudle on python will replace with subprocess on py3.0
  8. JAMStack-SSR/SSG 框架
  9. srpc 高性能通用 RPC 框架
  10. 前端web 技术盘点