彻底搞懂堆排序

一、准备知识

1.堆

堆(英语:heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:

  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树。

将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。

堆是线性数据结构,相当于一维数组,有唯一后继。

堆的定义如下:n个元素的序列{k1,k2,ki,…,kn}当且仅当满足下关系时,称之为堆。

(ki <= k2i,ki <= k2i+1)或者(ki >= k2i,ki >= k2i+1), (i = 1,2,3,4...n/2)

若将和此次序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有非终端结点的值均不大于(或不小于)其左、右孩子结点的值。由此,若序列{k1,k2,…,kn}是堆,则堆顶元素(或完全二叉树的根)必为序列中n个元素的最小值(或最大值)。

2.最大堆

把根节点最大的堆叫最大堆。

二、堆排序的思想

堆排序是将一个数组通过数组下标关系虚构出一个最大堆,这样这个最大堆的根节点是最大的,然后将这个堆的最后一个节点和根节点交换,这个最大值就到了数组的最后,然后数组的长度减一,减一后的数组在调整顺序,使其再次成为一个最大堆,这是根节点就是第二大的元素,然后将重复上面的步骤,知道全部交换完毕,数组就排好了顺序。

三、排序

1.将数组构造为一个虚拟的最大堆

通过下标来构造这个最大堆。

(1)数组下标和堆元素的对应关系。

数组下标与堆对应图

通过上面的图,我们可以分析出,一个节点,我们可以使用(n-1)/2来计算它的父节点的坐标、使用2*n+1来计算它的左节点的坐标、使用2*n+2来计算它的右节点的坐标。

(2)构造最大堆

遍历整个数组,构造一个最大堆,每次插入一个数,然后使堆重新成为一个最大堆。构造过程如下:

  • 首先将1加入堆,这时堆中只有一个元素,数组现在为[1,2,3,4,5,6,7]。

  • 将2加入堆中,计算2的父节点(1-1)/2=0,2的父节点是数组下标为0的元素。这时候2成为了1的左节点,根节点小于子节点,不满足最大堆的定义,所以调整这个堆,让根节点最大,所以1,2交换位置,数组现在为[2,1,3,4,5,6,7]。

  • 将3加入堆中,计算3的父节点(2-1)/2=0,3的父节点是数组下标为0的元素。这时候3成为了2的右节点,发现根节点2小于3,调整堆,将根节点2和3交换位置,现在数组为[3,1,2,4,5,6,7]。

  • 重复上面的步骤,将每一个元素加入这个堆,加入一个节点后,对比加入的节点和它的父节点的大小,如果新加入的节点大于它的父节点,则将两者交换,然后在比较交换后的节点和它的父节点的大小,知道使堆重新成为一个最大堆。

    构造过程代码:

构造虚拟最大堆代码<

2. 通过堆的结构调整来排序。
通过上面的构造,我们已将一个数组通过下标关系构造成为了一个虚拟的最大堆,这时候我们知道这个最大堆的根节点(也就是数组的第一个元素)现在肯定是所有数字中最大的一个,然后根据这个已知关系调整这个堆,来达到排序的目的。

调整过程如下:

  • 将数组的第一个元素和数组的最后一个元素交换位置,这样最大的那个数就到了数组的最后,然后数组长度减一,将最后一个数排除在外,剩余的数重新调整顺序,重新成为一个最大堆,这样根节点就变成了一个次大的元素。

  • 然后再次将根元素和现在的最后一个元素(原数组的倒数第二个元素)交换位置,数组长度在减一,然后重新调整堆使其再次成为一个最大堆。

  • 重复上面的步骤,直到所有的数字都调整完毕,这时数组就排好了顺序。

数组调整过程:

  • 找出当前节点和它的左节点、右节点三者中最大的那个节点,如果最大的节点是它自己则不做任何调整,如果最大的节点是它的左节点或者右节点,则和该节点交换位置,然后将交换后的节点作为当前节点在重复判断。

  • 重复上面的步骤,使其重新成为一个最大堆。

调整过程代码如下:

调整堆过程代码

四、完整代码

public class HeapSort {

     public void heapSort(int[] arr) {
         for (int i = 0; i < arr.length; i++) {
             heapInsert(arr, i);
         }
         int heapSize = arr.length;
         while (heapSize > 1) {
             heapify(arr, --heapSize);
         }
     }

     private void heapInsert(int[] arr,int i) {
       int last = (i - 1) / 2; //计算父节点
       while (arr[i] > arr[last]) { // 比较当前节点和父节点
           //调整堆
          swap(arr,i,last);
           //继续判断他的上一层节点是否满足最大堆
           i = last;
           last = (i - 1) / 2;
       }
   }

 public void heapify(int[] arr, int heapSize) {
   swap(arr, 0, heapSize--);
   int cur = 0;
   while (2 * cur + 1 <= heapSize) {
       int left = 2 * cur + 1;
       int right = 2 * cur + 2;
       int lastMax = heapSize >= right && arr[left] > arr[right] ?
               arr[left] > arr[cur] ? left : cur
               : heapSize >= right ?
               arr[right] > arr[cur] ? right : cur :
               arr[left] > arr[cur]
                       ? left : cur;
       if (lastMax == cur) {
           break;
       }
       int temp = arr[cur];
       arr[cur] = arr[lastMax];
       arr[lastMax] = temp;
       cur = lastMax;
   }
 }

 public void swap(int[] arr, int i, int j) {
   int temp = arr[i];
   arr[i] = arr[j];
   arr[j] = temp;
 }

 @Test
 public void test() {
     int[] arr = {1,2,3,4,5,6,7};
     int[] arr1 = Arrays.copyOf(arr, arr.length);
     heapSort(arr);
     Arrays.sort(arr1);
     Assert.assertArrayEquals(arr, arr1);
   }
}

五、复杂度。

时间复杂度:O(nlogn)

空间复杂度:O(1)

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

推荐阅读更多精彩内容

  • 1 初级排序算法 排序算法关注的主要是重新排列数组元素,其中每个元素都有一个主键。排序算法是将所有元素主键按某种方...
    深度沉迷学习阅读 1,391评论 0 1
  • 排序的基本概念 在计算机程序开发过程中,经常需要一组数据元素(或记录)按某个关键字进行排序,排序完成的序列可用于快...
    Jack921阅读 1,416评论 1 4
  • 堆排序可以做什么 首先应该弄清楚堆排序可以解决什么问题,答案是显而易见的:排序。说得通俗点儿就是对一组无序的数字进...
    Springlord888阅读 30,341评论 11 52
  • 盛夏时节,家乡的风景秀丽。我们回妈妈家小住了两日。 白天阳光有些火辣,只适合宅在家里避暑,陪爸爸妈妈一起喝茶聊天。...
    独孤若曦阅读 1,524评论 35 34
  • 最近大家的简书质量都好高,顿时鸭梨山大,试问干货哪里有,问君能有几多愁(被Joel感染了)……突然想起姐的思维导图...
    waynedeng阅读 1,419评论 1 5