将一个记录插入到已排序好的有序表中,从而得到一个新,记录数增1的有序表。即:先将序列的第1个记录看成是一个有序的子序列,然后从第2个记录逐个进行插入,直至整个序列有序为止。

要点:设立哨兵,作为临时存储和判断数组边界之用。

直接插入排序示例:

如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。


2. 插入排序—希尔排序(Shell`s Sort)

希尔排序是1959 年由D.L.Shell 提出来的,相对直接排序有较大的改进。希尔排序又叫缩小增量排序

基本思想:

先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。

操作方法:

  1. 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
  2. 按增量序列个数k,对序列进行k 趟排序;
  3. 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

希尔排序的示例:

算法实现:

我们简单处理增量序列:增量序列d = {n/2 ,n/4, n/8 .....1} n为要排序数的个数

即:先将要排序的一组记录按某个增量d(n/2,n为要排序数的个数)分成若干组子序列,每组中记录的下标相差d.对每组中全部元素进行直接插入排序,然后再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。继续不断缩小增量直至为1,最后使用直接插入排序完成排序。

#include<stdio.h>

void print(int a[],int n,int i){
  printf("%d \n",i);
  printf("::");
 
  for(int j= 0; j<8; j++){  
       printf("%d ",a[j]);
    }  
printf("\n");
 
 }
 
 void haxi(int a[],int n, int dk){
 
for(int i=dk;i<n;++i){
if(a[i-dk]>a[i]){
int j=i-dk;
int x=a[i];
a[i]=a[i-dk];
while(x < a[j]&&j>=0){  //查找在有序表的插入位置   
                a[j+dk] = a[j];  
                j-=dk;         //元素后移   
            }  
            a[j+dk] = x;
}
}
   
     
    print(a, n,dk );  
 
 }
 
  
void shellSort(int a[], int n){  
 
    int dk = n/2;  
    while( dk >=1  ){  
        haxi(a, n, dk);  
        dk = dk/2;  
   }  
}

int main(){
  int a[8] = {3,1,5,7,2,4,9,6};

shellSort(a,8);
  //print(a,8,8);
  //printf("hello world\n");
 return 0;
 }

希尔排序时效分析很难,关键码的比较次数与记录移动次数依赖于增量因子序列d的选取,特定情况下可以准确估算出关键码的比较次数和记录的移动次数。目前还没有人给出选取最好的增量因子序列的方法。增量因子序列可以有各种取法,有取奇数的,也有取质数的,但需要注意:增量因子中除1 外没有公因子,且最后一个增量因子必须为1。希尔排序方法是一个不稳定的排序方法。

简单选择排序的改进——二元选择排序

简单选择排序,每趟循环只能确定一个元素排序后的定位。我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可。具体实现如下:

  1. void SelectSort(int r[],int n) {
  2. int i ,j , min ,max, tmp;
  3. for (i=1 ;i <= n/2;i++) {
  4. // 做不超过n/2趟选择排序
  5. min = i; max = i ; //分别记录最大和最小关键字记录位置
  6. for (j= i+1; j<= n-i; j++) {
  7. if (r[j] > r[max]) {
  8. max = j ; continue ;
  9. }
  10. if (r[j]< r[min]) {
  11. min = j ;
  12. }
  13. }
  14. //该交换操作还可分情况讨论以提高效率
  15. tmp = r[i-1]; r[i-1] = r[min]; r[min] = tmp;
  16. tmp = r[n-i]; r[n-i] = r[max]; r[max] = tmp;
  17. }
  18. }

4. 选择排序—堆排序(Heap Sort)

堆排序是一种树形选择排序,是对直接选择排序的有效改进。

基本思想:

堆的定义如下:具有n个元素的序列(k1,k2,...,kn),当且仅当满足

时称之为堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最小项(小顶堆)。
若以一维数组存储一个堆,则堆对应一棵完全二叉树,且所有非叶结点的值均不大于(或不小于)其子女的值,根结点(堆顶元素)的值是最小(或最大)的。如:

(a)大顶堆序列:(96, 83,27,38,11,09)

(b)  小顶堆序列:(12,36,24,85,47,30,53,91)

初始时把要排序的n个数的序列看作是一棵顺序存储的二叉树(一维数组存储二叉树),调整它们的存储序,使之成为一个堆,将堆顶元素输出,得到n 个元素中最小(或最大)的元素,这时堆的根节点的数最小(或者最大)。然后对前面(n-1)个元素重新调整使之成为堆,输出堆顶元素,得到n 个元素中次小(或次大)的元素。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。称这个过程为堆排序

因此,实现堆排序需解决两个问题:
1. 如何将n 个待排序的数建成堆;
2. 输出堆顶元素后,怎样调整剩余n-1 个元素,使其成为一个新堆。

首先讨论第二个问题:输出堆顶元素后,对剩余n-1元素重新建成堆的调整过程。
调整小顶堆的方法:

1)设有m 个元素的堆,输出堆顶元素后,剩下m-1 个元素。将堆底元素送入堆顶((最后一个元素与堆顶进行交换),堆被破坏,其原因仅是根结点不满足堆的性质。

2)将根结点与左、右子树中较小元素的进行交换。

3)若与左子树交换:如果左子树堆被破坏,即左子树的根结点不满足堆的性质,则重复方法 (2).

4)若与右子树交换,如果右子树堆被破坏,即右子树的根结点不满足堆的性质。则重复方法 (2).

5)继续对不满足堆性质的子树进行上述交换操作,直到叶子结点,堆被建成。

称这个自根结点到叶子结点的调整过程为筛选。如图:

再讨论对n 个元素初始建堆的过程。
建堆方法:对初始序列建堆的过程,就是一个反复进行筛选的过程。

1)n 个结点的完全二叉树,则最后一个结点是第个结点的子树。

2)筛选从第个结点为根的子树开始,该子树成为堆。

3)之后向前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点。

如图建堆初始过程:无序序列:(49,38,65,97,76,13,27,49)
                              

 算法的实现:

从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。

  1. void print(int a[], int n){
  2. for(int j= 0; j<n; j++){
  3. cout<<a[j] <<"  ";
  4. }
  5. cout<<endl;
  6. }
  7. /**
  8. * 已知H[s…m]除了H[s] 外均满足堆的定义
  9. * 调整H[s],使其成为大顶堆.即将对第s个结点为根的子树筛选,
  10. *
  11. * @param H是待调整的堆数组
  12. * @param s是待调整的数组元素的位置
  13. * @param length是数组的长度
  14. *
  15. */
  16. void HeapAdjust(int H[],int s, int length)
  17. {
  18. int tmp  = H[s];
  19. int child = 2*s+1; //左孩子结点的位置。(i+1 为当前调整结点的右孩子结点的位置)
  20. while (child < length) {
  21. if(child+1 <length && H[child]<H[child+1]) { // 如果右孩子大于左孩子(找到比当前待调整结点大的孩子结点)
  22. ++child ;
  23. }
  24. if(H[s]<H[child]) {  // 如果较大的子结点大于父结点
  25. H[s] = H[child]; // 那么把较大的子结点往上移动,替换它的父结点
  26. s = child;       // 重新设置s ,即待调整的下一个结点的位置
  27. child = 2*s+1;
  28. }  else {            // 如果当前待调整结点大于它的左右孩子,则不需要调整,直接退出
  29. break;
  30. }
  31. H[s] = tmp;         // 当前待调整的结点放到比其大的孩子结点位置上
  32. }
  33. print(H,length);
  34. }
  35. /**
  36. * 初始堆进行调整
  37. * 将H[0..length-1]建成堆
  38. * 调整完之后第一个元素是序列的最小的元素
  39. */
  40. void BuildingHeap(int H[], int length)
  41. {
  42. //最后一个有孩子的节点的位置 i=  (length -1) / 2
  43. for (int i = (length -1) / 2 ; i >= 0; --i)
  44. HeapAdjust(H,i,length);
  45. }
  46. /**
  47. * 堆排序算法
  48. */
  49. void HeapSort(int H[],int length)
  50. {
  51. //初始堆
  52. BuildingHeap(H, length);
  53. //从最后一个元素开始对序列进行调整
  54. for (int i = length - 1; i > 0; --i)
  55. {
  56. //交换堆顶元素H[0]和堆中最后一个元素
  57. int temp = H[i]; H[i] = H[0]; H[0] = temp;
  58. //每次交换堆顶元素和堆中最后一个元素之后,都要对堆进行调整
  59. HeapAdjust(H,0,i);
  60. }
  61. }
  62. int main(){
  63. int H[10] = {3,1,5,7,2,4,9,6,10,8};
  64. cout<<"初始值:";
  65. print(H,10);
  66. HeapSort(H,10);
  67. //selectSort(a, 8);
  68. cout<<"结果:";
  69. print(H,10);
  70. }

分析:

设树深度为k,。从根到叶的筛选,元素比较次数至多2(k-1)次,交换记录至多k 次。所以,在建好堆后,排序过程中的筛选次数不超过下式:

而建堆时的比较次数不超过4n 次,因此堆排序最坏情况下,时间复杂度也为:O(nlogn )。

转 http://blog.csdn.net/hguisu/article/details/7776068 排序算法相关推荐

  1. http://blog.csdn.net/hguisu/article/details/8836819

    1.  MySql+Memcached架构的问题 Memcached采用客户端-服务器的架构,客户端和服务器端的通讯使用自定义的协议标准,只要满足协议格式要求,客户端Library可以用任何语言实现. ...

  2. https://blog.csdn.net/blmoistawinde/article/details/84329103

    背景     很多场景需要考虑数据分布的相似度/距离:比如确定一个正态分布是否能够很好的描述一个群体的身高(正态分布生成的样本分布应当与实际的抽样分布接近),或者一个分类算法是否能够很好地区分样本的特 ...

  3. numpy的shape用法(转http://blog.csdn.net/by_study/article/details/67633593)

    https://www.bbsmax.com/A/1O5E78b7z7/ https://www.cnblogs.com/lindaxin/p/8074921.html http://blog.csd ...

  4. 系统权限管理设计 (转:http://blog.csdn.net/chexlong/article/details/37697555)

    权限设计(转:http://blog.csdn.net/chexlong/article/details/37697555)      1. 前言:      权限管理往往是一个极其复杂的问题,但也可 ...

  5. http://blog.csdn.net/u011001723/article/details/456210272222

    http://blog.csdn.net/u011001723/article/details/45621027

  6. http://blog.csdn.net/xingfuzhijianxia/article/details/6433918

    http://blog.csdn.net/xingfuzhijianxia/article/details/6433918

  7. http://blog.csdn.net/luoshengyang/article/details/6651971

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6651971 在Android系统中,提供了独特 ...

  8. http://blog.csdn.net/lovejavaydj/article/details/6

    2019独角兽企业重金招聘Python工程师标准>>> http://blog.csdn.net/lovejavaydj/article/details/6202610 转载于:ht ...

  9. http://blog.csdn.net/churximi/article/details/61210129

    http://blog.csdn.net/churximi/article/details/61210129http://blog.csdn.net/churximi/article/details/ ...

最新文章

  1. OSChina 周三乱弹 —— 孤独到都和病毒发生了感情了
  2. react 拖拽生成html,[React] 基于react 拖拽时间选择器
  3. 光纤中继器的防雷及日常维护方法介绍
  4. php速度优化,php优化及高效提速问题小结
  5. 离线在线计算机系统,离线计算机系统
  6. Git如何创建本地分支并推送到远程仓库
  7. Windows核心编程_窗口蒙版效果
  8. C#解决“Emgu.CV.CvInvoke”的类型初始值设定项引发异常 的其中一个办法
  9. tensorflow学习笔记1:batch normalization 用法
  10. 关于oracle误删数据如何进行恢复
  11. Mac系统下使用cd命令无法进入目录
  12. Nacos 国内镜像
  13. 关于Eclipse安装ArchStudio 5插件出现An error occurred while collecting items to be installed......的解决方法
  14. 【小程序精品源码系列】小说阅读器
  15. 计算机显示器工作的原理,液晶显示器的工作原理
  16. js破解 中国国际航空公司登录
  17. Linux 上好用的 R 语言 IDE
  18. 使用共享收款码系统,打造微商模式经营门店是关键
  19. IOS逆向笔记之HOOK实现(非越狱)
  20. 为什么D类音频功放可以免输出滤波器

热门文章

  1. HDFS文件读取流程
  2. Decorators TypeScript 装饰器
  3. linux创建虚拟磁盘的方法
  4. fieldset 标签 -- 对表单进行分组
  5. ffmpeg——TS流解析
  6. UiPath针对DataTable中的某几列数据去重
  7. XML刘伟视频--总结
  8. FIFO - linux内核数据结构
  9. 灵动ICPC冬令营基础-2
  10. web前端期末大作业:青岛旅游网页主题网站设计——青岛民俗4页 HTML+CSS 民俗网页设计与制作 web网页设计实例作业 html实训大作业