算法导论学习001-堆排序

堆排序

堆排序基本简介

1991年的计算机先驱奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德(Robert W.Floyd)和威廉姆斯(J.Williams)在1964年共同发明了著名的堆排序算法。

堆排序属于比较排序中的一种,具有O(nLgn)的时间复杂度,空间复杂度为O(1)。因此堆排序具有空间原址性,即任何时候都只需要常数个额外的元素空间存储临时数据。堆排序利用一种我们称为“堆”的数据结构来进行排序,因此在了解堆排序之前我们需要对堆这种数据结构有个了解,才能对堆排序是如何利用该数据结果完成对数据的排序的。

堆的定义

堆是一个数组,它可以被看成是一个近似的完全二叉树,树上的每一个节点对应数组中的一个元素。除了最底层外,该树是完全充满的,而且是从左向右填充。

堆的一些特性:

堆实际上是一棵完全二叉树,根据完全二叉树的一些特性我们可以得到父子节点之间的位置关系:对于给定的一个下标为i的节点我们可以很容计算到它的父节点下标为i/2 左孩子下标为2i,右孩子节点为2i+1(以上是在下标为1开始计算的,对于数组从0开始的计算作简单调整即可)

最大堆 最小堆

在堆排序中我们把一个无序的序列变为有序的过程使用的并不是一棵随意建立的二叉堆,对于升序和降序排列分别用到了最大堆以及最小堆。因此我们需要了解一下最大堆以及最小堆相对于普通二叉堆有什么特殊特性:
对于最大堆中 除了根节点以外的所有子节点i都要满足
A[Parent(i)]>=A[i]
这个定义表明父节点的值是不小于其任意的一个子节点的值。 因此根节点存储的是序列的最大值,并且任一子树中,该子树所包含的的所有节点的值都不大于该子树根节点的值。
对于最小堆则刚好与最大堆相反这里就不在介绍。
了解了最大堆和最小堆我们来看看堆排序的排序过程。(以下我们利用最大堆来进行排序)

堆排序的过程

  • BUILD-MAX-HEAP
  • MAX-HEAPIPY
  • HEAP-SORT

堆排序由以上三个步骤分别是建立最大堆,最大堆维护 堆排序。

由于BUILD-MAX-HEAP实际是通过循环调用 MAX-HEAPIPY过程来完成的 因此我们先介绍MAX-HEAPIPY:

MAX-HEAPIPY是用于维护最大堆性质的重要过程。它的输入为一个数组A和一个下标i,在调用MAX-HEAPIPY的时候,我们假定根节点为Left[i]和Right[i]的子树都是最大堆,但这时A[i]可能小于其孩子节点,这样就违背了最大堆的性质。MAX-HEAPIPY通过让A[i]的值在堆中“逐级下降”从而使得以下标i为根节点的子树重新遵循最大堆的性质。

MAX-HEAPIPY伪代码如下:

  1. l = LEFT(i)
  2. r = RIGHT(i)
  3. if l <= A.heap-size && A[l] > A [i]
  4. largest = l;
  5. else largest = i
  6. if(r<= A.heap-size &&  A[r] > A [i]
  7. largest = r
  8. if largest != i
  9. exchange A[i] with A[largest]
  10. MAX-HEAP(A,largest)

由以上伪代码可知维护最大堆性质就是将i节点开始遍历它的左右子树,从未保证i节点是这棵子树的最大子节点,由于在前面的假定中i的子树已经是一棵满足最大堆的二叉树因此经过MAX-HEAP过程加上i这一层后继续满足最大堆的特性

BUILD-MAX-HEAP 创建最大堆就是将一个大小n=Array.length的数组A[0,1...n-1]转换为最大堆的过程。
用伪代码表示创建最大堆如下
BUILD-MAX—HEAP(A)

  1. A.heap-size = A.lenth
  2. for i = A.length/2 down to 0
  3. MAX-HEAPRIFY(A,i)

由以上伪代码可知创建最大堆过程就是从树的底部向树的根部遍历调用MAX-HEAPRIFY方法来完成的A.length/2 表示从树的倒数第二层开始调用MAX-HEAP 从底部往上通过循环逐渐将一棵二叉树变为最大堆。在这个过程中先是把倒数第二层的子树变换为最大堆,然后根据MAX-HEAP的假定在最底层满足最大堆后遍历倒数第二层时 这样遍历倒数第三层进行MAX-HEAP时倒数第三层也满足了最大堆的特性,因此建立最大堆是一个从底部网上逐级满足最大堆的特性,遍历完根节点后整棵二叉树就成为了一棵最大堆。

最后我们来看HEAP-SORT即堆排序就非常简单了
先给出伪代码
HEAP-SORT(A)

  1. BUILD-MAX-HEAP(A)
  2. for(A.length dowm to 1
  3. exchange A[i] whih A[0]
  4. MAX-HEAPIFY(A,0)
    从以上伪代码可以看出堆排序是怎么利用堆这种数据结构进行排序以及非常巧妙的利用了最大堆的特性非常高效的就将一个序列变为了一个有序的序列。
    首先是根据输入数组建立一个最大堆,根据最大堆的性质此时序列最大元素就是根节点的元素,这时把根节点移到最后一个节点以后,根节点的左右子树仍然满足最大堆的条件,这时在调用MAX-HEAPIFY就可以很快的让整棵树重新成为一棵最大堆。经过遍历和交换过程中A[0]到A[i-1]最大堆 而A[i] 到A[A.length-1]为从小到大排列的数组,遍历过程就是不断的取出处于根节点的最大元素,然后将剩余元素从未维护为最大堆的过程。

样整个堆排序的过程就介绍完了,了解了堆排序后是不是得感叹发明此算法的人是有多聪明呀,想到这么巧妙数据构,和利用该数据结构巧妙高效的完成了排序,而我等普通人不是不奢望能发明算法,但能够把一种算法理解透并能够灵活运用到实际的项目中也是不错的了。

算法复时间空间复杂度分析

  1. 空间复杂度

于排序过程是在原数组上对元素进行处理,仅仅只是在元素交换时开辟了常数个元素空间,因此堆排序空间复杂度很简单就是O(1)

  1. 时间复杂度
    由上面的堆排序的伪代码可知首先建立最大堆是逐层建立根据完全二叉树的性质我们很容易得到完全二叉树的深度为lg(n) 而遍历一趟的时间复杂度为O(n),遍历趟数为lg(n)因此堆排序的时间复杂度为O(nlgn)

END Talk is cheap show me your code

void maxHeapify(int *unSortArray, int root, int bottom) {
int rootValue = unSortArray[root];
int left = root * 2 + 1;
while (left <= bottom) {
    if (left < bottom) {
        if (unSortArray[left] < unSortArray[left + 1]) {
            left = left + 1;
        }
    }

    if (rootValue < unSortArray[left]) {
        unSortArray[root] = unSortArray[left];
        root = left;
        left = root * 2 + 1;
    } else {
        left = bottom + 1;
    }
}

unSortArray[root] = rootValue;
}

void buildMaxHeap(int *unSortArray, int arraySize) {
for (int i = arraySize / 2 - 1; i >= 0; i--) {
    maxHeapify(unSortArray, i, arraySize - 1);
    }
}

void heapSortByMaxHeap(int *unSortArray, int arraySize) {
buildMaxHeap(unSortArray, arraySize);
for (int i = arraySize - 1; i >= 0; i--) {
    int max = unSortArray[0];
    unSortArray[0] = unSortArray[i];
    unSortArray[i] = max;
    maxHeapify(unSortArray, 0, i - 1);
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    蚁前阅读 5,149评论 0 52
  • 概述:排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    每天刷两次牙阅读 3,723评论 0 15
  • 1 序 2016年6月25日夜,帝都,天下着大雨,拖着行李箱和同学在校门口照了最后一张合照,搬离寝室打车去了提前租...
    RichardJieChen阅读 5,062评论 0 12
  • retain cycle 会造成内存溢出,严重情况会引起崩溃。一般注意点也不会发生,但在网络连接比较多的地方就会不...
    natewang阅读 3,422评论 0 3
  • 昨天28号是七夕情人节,你懂的,该干啥干啥。 秦观的那首词,金风玉露一相逢便胜却世间无数,听了让人不禁遐想万端,风...
    乘风破浪的Mrzhao阅读 266评论 0 0