算法导论-排序(三) 堆排序

目录

1、堆排序介绍

2、堆排序实例

3、c++ 完整代码

4、参考资料

内容

1、堆排序介绍

1.1 、堆是什么

堆是一颗完全二叉树,(设某一个节点为i,根节点从0开始,则其左孩子节点为2*i+1,右孩子节点为2*i+2),堆任意一个非叶节点满足:

Key[i]>=Key[2*i+1] &&  Key[i]>=Key[2*i+2] 或者 Key[i]<=Key[2*i+1] &&  Key[i]<=Key[2*i+2],

就是说对于任意一个父节点,要么大于等于子节点,要么小于等于子节点,对于Key[i]>=Key[2*i+1] &&  Key[i]>=Key[2*i+2]的堆叫做大顶堆;对于 Key[i]<=Key[2*i+1] &&  Key[i]<=Key[2*i+2]的叫做小顶堆;由上述性质可知大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的

1.2、什么是堆排序

就是在堆的基础上进行排序,如何排序呢?

[1] 首先,从要排序的数组开始建立一个大顶堆(这里以大顶堆说明,如何建大顶堆,下面详解),这样由大顶堆的性质可知,堆顶的数最大;

[2] 然后,交换堆顶key[0]和堆的最后一个元素Key[n-1],Key[0]<->Key[n-1] ,这样最大的数就放到堆的最后Key[n-1]了;由于交换了元素,0 ~ (n-2)的节点可能不满足大顶堆要求,所以然后对堆的Key[0]--Key[n-2]调整为大顶堆;

[3] 将调整好的大顶堆[0~(N-2)],交换堆顶Key[0]和堆的最后一个元素Key[n-2],Key[0]<->Key[n-2],这样,Key[n-2]、Key[n-1]最大的两个数都已经排好了;重新调整[0~(n-3)]为大顶堆;不断重复此过程直到整个排序过程完成

如何建大顶堆呢?

[1] 首先,从最大的非叶节点(设为Key[i])开始,往下进行调整,如果子孩子节点Key[2*i+1]和Key[2*i+2]较大的值比Key[i]的值大,则,交换该节点Key[i]与子节点中较大的节点。然后 以交换后的子节点作为当前节点,往下进行调整,直到遇到叶节点;

[2] 最大非叶节点Key[i]调整完毕后,对节点Key[i-1]进行往下调整,直到出现叶节点;不断重复此过程,直到对根节点往下调整完毕;

下面是建大顶堆的实例:待排序数组为[5 16 3 20 17 4]

2、堆排序实例

上面图说明了如何建大顶堆,建好大顶堆后就可以进行排序了,下面是堆排序的实例:

3、c++ 完整代码

Sort.h(堆排序函数实现)

  1 #ifndef SORT_HH
  2 #define SORT_HH
  3 template<typename T >
  4 class Sort
  5 {
  6 public:
  7     void Swap(T &m,T &n);//交换数据
  8     void print_element(vector<T> A);//打印数组
  9     void HeapSort(vector<T> &A,bool IsRecurFlag);
 10 private:
 11     void HeapAdjust(vector<T> &A,int i,int n);//非递归调整堆
 12     void HeapAdjust_Recursive(vector<T> &A,int i,int n);//递归调整堆
 13     void BuildBigHeap(vector<T> &A,bool IsRecurFlag);//建大顶堆
 14 };
 15 template<typename T>//交换数据
 16 void Sort<T>::Swap(T &m,T &n)
 17 {
 18     T tmp;
 19     tmp=m;
 20     m=n;
 21     n=tmp;
 22 }
 23 //从i节点开始调整,n为节点总数 从0开始计算 i节点的子节点为 2*i+1, 2*i+2
 24 template<typename T>
 25 void Sort<T>::HeapAdjust(vector<T> &A,int i,int n)
 26 {
 27     T temp = A[i];
 28     int j = 2*i+1;//下标从0开始
 29     while (j<n)//非递归实现
 30     {
 31         if (j+1<n&&A[j+1]>A[j])//找出子节点的最小值
 32             j++;
 33         if(A[j]<=temp)//子节点不大于父节点,调整结束
 34             break;
 35
 36         A[i] = A[j];// 子节点最大值大于父节点,值赋给父节点;
 37         i = j;
 38         j = 2*i+1;//下标从0开始
 39     }
 40     A[i] = temp;//调整结束
 41 }
 42 //从i节点开始调整,n为节点总数 从0开始计算 i节点的子节点为 2*i+1, 2*i+2
 43 template<typename T>
 44 void Sort<T>::HeapAdjust_Recursive(vector<T> &A,int i,int n)//递归调整堆
 45 {
 46     int lchild = 2*i+1;
 47     int rchild = 2*i+2;
 48     int max = i;
 49     if (i<=(n-1)/2)//从最大非叶节点开始调整
 50     {
 51         if (lchild<n&&A[lchild]>A[max])
 52         {
 53             max=lchild;
 54         }
 55         if(rchild<n&&A[rchild]>A[max])
 56         {
 57             max=rchild;
 58         }
 59         if (max!=i)//子节点值比父节点大,调整
 60         {
 61             Swap(A[i],A[max]);
 62             HeapAdjust_Recursive(A,max,n);//递归
 63         }
 64     }
 65 }
 66 //初始从待排序数组建大顶堆
 67 //数组从0开始计算,建堆从p到q
 68 template<typename T>
 69 void Sort<T>::BuildBigHeap(vector<T> &A,bool IsRecurFlag)
 70 {
 71     int n=A.size();
 72     for(int i=(n-1)/2;i>=0;i--)//从最大的非叶节点开始
 73     {
 74         if (IsRecurFlag)
 75             HeapAdjust_Recursive(A,i,n);
 76         else
 77             HeapAdjust(A,i,n);
 78     }
 79 }
 80 //堆排序,首先建大顶堆
 81 template<typename T>
 82 void Sort<T>::HeapSort(vector<T> &A,bool IsRecurFlag)
 83 {
 84     int len = A.size();
 85     BuildBigHeap(A,IsRecurFlag);//建大顶堆
 86     for(int i=len-1;i>=0;i--)
 87     {
 88         Swap(A[0],A[i]);//未排序堆第一个和最后一个交换
 89         if (IsRecurFlag)//递归
 90             HeapAdjust_Recursive(A,0,i);
 91         else//非递归
 92             HeapAdjust(A,0,i);//调整为大顶堆
 93     }
 94 }
 95
 96 template<typename T>//打印数组
 97 void Sort<T>::print_element(vector<T> A)
 98 {
 99     int len=A.size();
100     for (int i=0;i<len;i++)
101     {
102         std::cout<<A[i]<<" ";
103     }
104     std::cout<<std::endl;
105 }
106 #endif

Sort.cpp(主测试函数)

 1 #include <iostream>
 2 #include <vector>
 3 using namespace std;
 4 #include "Sort.h"
 5
 6 int main()
 7 {
 8     Sort<int> sort1;
 9     int a[]={2,4,3,178,23,134,1,-27,1345,80};
10     vector<int > vec_int1(a,a+10);
11     cout<<"源数组:";
12     sort1.print_element(vec_int1);
13     cout<<"非递归实现排序:";
14     sort1.HeapSort(vec_int1,false);
15     sort1.print_element(vec_int1);
16
17     vector<int > vec_int2(a,a+10);
18     cout<<"递归实现排序:";
19     sort1.HeapSort(vec_int2,true);
20     sort1.print_element(vec_int2);
21     system("PAUSE");
22     return 0;
23 }

输出:

4、参考资料

【1】 http://www.cnblogs.com/dolphin0520/archive/2011/10/06/2199741.html

【2】 http://blog.csdn.net/morewindows/article/details/6709644

时间: 2024-11-08 21:27:20

算法导论-排序(三) 堆排序的相关文章

算法导论——lec 06 堆排序

堆数据结构是一种数组对象,它可以被视为一颗完全二叉树,树中每个节点和数组中存放该节点值的那个元 素对应.如果表示堆的数组为A,那么树的根为A[1]. 一. 堆 1. 表示堆的数组A是一个具有两个属性的对象:length(A)是数组中的元素个数,heap-size(A)是存放在A中的堆的 元素个数:A[heap-size(A)]之后的元素都不属于相应的堆.也就是:Heap-size(A)<=length(A). 2. 给定某个节点的下标i,其父节点PARENT(i),左儿子LEFT(i)和右儿子R

算法导论 第三版 中文版

下载地址:网盘下载 算法导论 第三版 中文版 清晰 PDF,全书共8部分35章节,内容涵盖基础知识.排序和顺序统计量.数据结构.高级设计和分析技术.高级数据结构.图算法.算法问题选编.以及数学基础知识.非常实用的参考书和工程实践手册.此外,其他资源也已经上传,全部免费,欢迎大家下载! 第3版的主要变化 1.新增了van Emde Boas树和多线程算法,并且将矩阵基础移至附录. 2.修订了递归式(现在称为"分治策略")那一章的内容,更广泛地覆盖分治法. 3.移除两章很少讲授的内容:二项

算法导论—排序之插入排序

void insertion_sort(vector<int> &num){ for(int i = 1; i < num.size(); i++){ int j = i-1; int val = num[i]; while(j>=0 && num[j] >= val){ num[j+1] = num[j]; j--; } num[j+1] = val; } } 每次迭代时,将num[i] 作为key值,且前子数组[0,i-1] 构成已排好序,每次与左

字符串匹配算法的C语言实现-算法导论第三版(1)

因为最近准备开始学习做一些小的Android项目练手,看上了系统级的三个应用,拨号盘,通讯录和短信,准备从最简单的拨号做起,但是因为这些应用中都不可避免的会有自动提示,我觉得设计到的就是字符串匹配问题,这里准备使用C语言来实现,将来通过JNI集成到应用当中. 1.首先是朴素匹配,实际上就是穷举: 用C语言实现的函数我放到这里: 1 void naive_string_match(char *t,char *p){ 2 int n = strlen(t); 3 int m = strlen(p);

算法导论-排序(四)计数排序(线性时间排序)

目录 1.计数排序介绍 2.流程图 3.代码实现 4.性能分析 5.参考资料 内容 1.计数排序介绍 什么是计数排序? 计数排序是一种特殊的排序算法,之前介绍的排序算法需要对数进行两两比较,效率下界为θ(nlgn);   而计数排序不需要对数进行比较就可以进行排序:很神奇吧,只需要对待排序数组进行遍历就可以排序,效率为Θ(n)..哈哈,那么神奇,下面开讲!!!! 限制:计数排序只能对非负整数(0,1,2,3...N)进行排序 排序思想:计数排序的基本思想就是对每一个输入元素 x,确定出小于 x

算法导论 第三部分——基本数据结构——红黑树

红黑树 红黑树是一种二叉查找树,但在每个结点上增加了一个存储位表示结点的颜色,可以是RED或者BLACK.通过对任何一条从根到叶子的路径上各个着色方式的限制, 红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的.当二叉查找树的高度较低时,这些操作执行的比较快,但是当树的高度较高时,这些操作的性能可能 不比用链表好.红黑树(red-black tree)是一种平衡的二叉查找树,它能保证在最坏情况下,基本的动态操作集合运行时间为O(lgn). 1.红黑树的性质 #define RED 0

算法导论-排序(二)快速排序、随机化快速排序

目录 1.本文介绍 2.快速排序 3.随机化快速排序 4.完整源码 5.参考资料 内容 1.本文介绍 主要内容分为两部分,一部分是介绍快速排序算法,分析其在最好.最坏以及最好最差交替出现情况下的算法效率:另一部分则是介绍随机化快排算法,以及分析其算法复杂度.最后给出c++实现代码. 2.快速排序 快速排序也是基于分治思想,首先把要排序的数组分为两份,然后再分别对分好的子数组进行快速排序.当子数组为1个元素时递归介绍,排序完成.快速排序属于“原地排序”,就是说在原有的数组内存上排序.不占用其他内存

算法导论(1)堆排序

#pragma once #include<iostream> using namespace std; /*返回节点i的父结点*/ int Parent(int i) { if (i <= 0) return -1; else return (i - 1) / 2; } /*返回节点i的左孩子*/ int Left(int i) { return 2 * i + 1; } /*返回结点i的右孩子*/ int Right(int i) { return 2 * i+2; } /*交换两个

算法导论 第三章 函数的增长

//参考博文:http://blog.csdn.net/so_geili/article/details/53353593 //1.渐近效率: A:指的是当输入规模无限增加时,在极限中,算法的运行时间如何随着输入规模的变大而增加 B:通常,渐近的表示某个算法对除很小的输入外的所有情况都将是最好的选择 //2.Θ记号的数学含义: A:方式一:设f(n)和g(n)是定义域为自然数集合的函数.如果limn→∞f(n)/g(n)存在,并且等于某个常数c(c>0) 那么f(n)=Θ(g(n)).通俗理解为