博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
排序 之 堆排序 归并排序
阅读量:4507 次
发布时间:2019-06-08

本文共 2685 字,大约阅读时间需要 8 分钟。

堆的概念

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

堆排序

堆排序(Heap Sort)就是利用堆(假设利用大顶堆)进行排序的方法。它的基本思想是,将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是对顶的根结点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次大值。如此反复执行,就可以得到一个有序序列了。

有上述思想可知,堆排序的两个要点:

  • 1.如何由一个无序序列构建成一个堆?
  • 2.如何在输出堆顶元素后,调整剩余元素称为一个新的堆?
/* 堆排序********************************** *//* 已知L->r[s..m]中记录的关键字除L->r[s]之外均满足堆的定义, *//* 本函数调整L->r[s]的关键字,使L->r[s..m]成为一个大顶堆 */void HeapAdjust(SqList *L,int s,int m){     int temp,j;    temp=L->r[s];    for(j=2*s;j<=m;j*=2) /* 沿关键字较大的孩子结点向下筛选 */    {        if(j
r[j]
r[j+1]) ++j; /* j为关键字中较大的记录的下标 */ if(temp>=L->r[j]) break; /* rc应插入在位置s上 */ L->r[s]=L->r[j]; s=j; } L->r[s]=temp; /* 插入 */}/* 对顺序表L进行堆排序 */void HeapSort(SqList *L){ int i; for(i=L->length/2;i>0;i--) /* 把L中的r构建成一个大根堆 */ HeapAdjust(L,i,L->length); for(i=L->length;i>1;i--) { swap(L,1,i); /* 将堆顶记录和当前未经排序子序列的最后一个记录交换 */ HeapAdjust(L,1,i-1); /* 将L->r[1..i-1]重新调整为大根堆 */ }}

时间复杂度

构建堆的时间复杂度为O(n)。在正式排序过程中,第i次取堆顶记录重建堆需要用O(logi)的时间,需要取n-1此堆顶记录。因此重建堆的时间复杂度为O(nlogn).

不过由于记录的比较与交换是跳跃式进行,因此堆排序也是一种不稳定的排序方法。

归并排序

分治法

分治法(divide-and-conquer)是一种算法设计策略,其思想是将原问题划分成n个规模较小而结构与原问题相似的子问题;递归地解决这些子问题,然后再合并其结果,就得到原问题的解。归并排序,希尔排序和快排都是分治思想的典型应用。

分治模式在每一层递归上都有三个步骤:

  • 分解(Divide):将原问题分解成一系列子问题;
  • 解决(Conquer):递归地解各子问题。若子问题足够小,则直接求解;
  • 合并(Combine):将子问题的结果合并成原问题的解。

归并排序(merge sort)算法完全依照了上述模式,直观地操作如下:

分解:将n个元素分成各含n/2个元素的子序列;

解决:用合并排序法对两个子序列递归地排序;

合并:合并两个已排序的子序列以得到排序结果。

/* 归并排序********************************** *//* 将有序的SR[i..m]和SR[m+1..n]归并为有序的TR[i..n] */void Merge(int SR[],int TR[],int i,int m,int n){    int j,k,l;    for(j=m+1,k=i;i<=m && j<=n;k++)    /* 将SR中记录由小到大地并入TR */    {        if (SR[i]
r,L->r,1,L->length);}/* 非递归法 *//* 将SR[]中相邻长度为s的子序列两两归并到TR[] */void MergePass(int SR[],int TR[],int s,int n){ int i=1; int j; while(i <= n-2*s+1) {
/* 两两归并 */ Merge(SR,TR,i,i+s-1,i+2*s-1); i=i+2*s; } if(i
<= n;j++) TR[j] = SR[j];}/* 对顺序表L作归并非递归排序 */void MergeSort2(SqList *L){ int* TR=(int*)malloc(L->length * sizeof(int));/* 申请额外空间 */ int k=1; while(k
length) { MergePass(L->r,TR,k,L->length); k=2*k;/* 子序列长度加倍 */ MergePass(TR,L->r,k,L->length); k=2*k;/* 子序列长度加倍 */ }}

复杂度

时间复杂度为O(nlogn),

空间复杂度为O(n+logn),由于归并排序在归并过程中需要与原始记录序列同样数量的存储空间存放归并结果以及递归时深度为log2n的栈空间。

归并排序是一种比较占用内存,但却效率高且稳定的算法。

非递归的迭代方法,避免了递归时深度为log2n的栈空间,因此空间复杂度为O(n).并且避免递归在时间性能上有一定的提升(函数调用的开销)。所以,使用归并排序时,尽量考虑使用非递归方法。

转载于:https://www.cnblogs.com/xingchenfeng/p/3726547.html

你可能感兴趣的文章
二叉树三种遍历调试运行版
查看>>
关于PHP、python使用的CRC32函数
查看>>
JS自动关闭授权弹窗,并刷新父页面
查看>>
c#语言几种常见循环代码
查看>>
SQL多表连接查询(详细实例)
查看>>
Http中涉及到的知识点总结
查看>>
测试计划
查看>>
adb命令记录
查看>>
Ecstore Nginx Rewrite(去掉链接中的index.php) ECSTORE 伪静态
查看>>
Dash
查看>>
BZOJ 1876: [SDOI2009]SuperGCD
查看>>
swift初学日志
查看>>
CCF真题之出现次数最多的数
查看>>
Eclipse上GIT插件_客户端配置
查看>>
使用HANA Web-based Development Workbench创建最简单的Server Side JavaScript
查看>>
JavaScript浏览器对象之二Document对象
查看>>
算法导论 第二部分——排序和顺序统计量
查看>>
监督学习——AdaBoost元算法提高分类性能
查看>>
SharePoint 使用代码创建 SPWeb/SPSiite/SPWebApplication以及WebPart添加到页面与删除 (三)...
查看>>
[原创]在Linux系统Ubuntu14.04上安装部署docker。
查看>>