3.比较排序之堆排序

  对于堆排序会涉及一些完全二叉树知识。对于待排序列{10, 2, 11, 8, 7},把它看成是一颗完全二叉树,如下图所示。

  堆分为大根堆和小根堆:大根堆表示每个根节点均大于其子节点(L(i) >= L(2i) && L(i) >= L(2i + 1)),小根堆表示每个根节点均小于其子节点(L(i) <= L(2i) && L(i) <= L(2i + 1))。(在完全二叉树中第i个节点的左子节点为2i,其右字节点为2i + 1)

  本文将以大根堆的构建作为示例进行讲解。

  堆排序的第一步——构建初始堆。如何构建初始堆呢?根据定义,关键点在于每个根节点。观察上述待排序列的完全二叉树,不难发现存在节点2和节点10有子节点,它们是需要关注的节点。

  如何定位节点2呢?发现它是叶子节点,或者最后一个节点的父节点,根据完全二叉树的性质可知,除根节点外任意节点的父节点的编号为⌊n / 2⌋。已知n = 5,易知节点2的编号为⌊5 / 2⌋ = ②。比较它与左右子节点的大小并调整。

  最后剩下根节点10,已知节点2的编号为②,② - 1 = ①即得到根节点10的编号。比较它与左右子节点的大小并调整。

  调整完毕后发现已经构成了一个“大根堆”,示例中的待排序列较为简单,再给出一个较为复杂的待排序列,观察其构建大根堆的过程。对于待排序列{53, 17, 78, 09, 45, 65, 87, 32},将它看成一颗完全二叉树。

  同样我们来看它所需要关注的节点有哪些。

  根据第一个例子,我们很容易能定位节点09的编号为⌊8 / 2⌋ = ④,节点78的编号为④ - 1 = ③……,依次类推,发现了一定的规律,即需要调整的节点位置从n / 2开始依次递减直到根节点①结束(n / 2 ~ 1)。现在开始调整。

  在第四次调整结束后发现节点53不满足大根堆的定义,其右子节点大于它,此时需要做进一步的向下调整。

  注意向下调整是每次向上调整的时候都需要做的判断是否需要向下调整,而不是在所有的向上调整结束过后再回过头来向下调整。这样大根堆就建立好了,此时待排序列数组情况已经发生了改变:{87, 45, 78, 32, 17, 65, 53, 09}。接下来是如何进行排序的问题。将大根堆的根节点与最后一个节点互换,并调整二叉树使其仍然满足大根堆。

  可以看到将根节点与最后一个节点呼唤后,待排序列的最大值已经放到了数组的最后一个位置{……, 87},此时完成了第一趟排序,但这第一趟排序还没有结束,此时除节点87外,其余节点并不满足大根堆的条件,所以需要对其余节点进行调整为大根堆。排序过程不再给出,Java和Python3的代码实现如下。

  Java

 1 package com.algorithm.sort.heap;
 2
 3 import java.util.Arrays;
 4
 5 /**
 6  * 堆排序
 7  * Created by yulinfeng on 6/20/17.
 8  */
 9 public class Heap {
10
11     public static void main(String[] args) {
12         int[] nums = {53, 17, 78, 09, 45, 65, 87, 32};
13         nums = heapSort(nums);
14         System.out.println(Arrays.toString(nums));
15     }
16
17     /**
18      * 堆排序
19      * @param nums 待排序数组序列
20      * @return 排好序的数组序列
21      */
22     private static int[] heapSort(int[] nums) {
23
24         for (int i = nums.length / 2 - 1; i >= 0; i--) {
25             heapAdjust(nums, i, nums.length);
26         }
27         for (int i = nums.length - 1; i > 0; i--) {
28             int temp = nums[i];
29             nums[i] = nums[0];
30             nums[0] = temp;
31             heapAdjust(nums, 0, i);
32         }
33         return nums;
34     }
35
36     /**
37      * 调整堆
38      *
39      * @param nums   待排序序列
40      * @param parent      待调整根节点
41      * @param length 数组序列长度
42      */
43     private static void heapAdjust(int[] nums, int parent, int length) {
44         int temp = nums[parent];
45         int childIndex = 2 * parent + 1;    //完全二叉树节点i从编号1开始的左子节点位置在2i,此处数组下标从0开始,即左子节点所在数组索引位置为:2i + 1
46         while (childIndex  < length) {
47             if (childIndex + 1 < length && nums[childIndex] < nums[childIndex + 1]) {
48                 childIndex++;   //节点有右子节点,且右子节点大于左子节点,则选取右子节点
49             }
50             if (temp > nums[childIndex]) {
51                 break;  //如果选中节点大于其子节点,直接返回
52             }
53             nums[parent] = nums[childIndex];
54             parent = childIndex;
55             childIndex = 2 * parent + 1;    //继续向下调整
56         }
57         nums[parent] = temp;
58     }
59 }

  Python3

 1 #堆排序
 2 def heap_sort(nums):
 3
 4     for i in range(int(len(nums) / 2 - 1), -1, -1):
 5         heap_adjust(nums, i, len(nums))
 6
 7     for i in range(len(nums) - 1, -1, -1):
 8         temp = nums[i]
 9         nums[i] = nums[0]
10         nums[0] = temp
11         heap_adjust(nums, 0, i)
12
13     return nums
14
15 #调整堆
16 def heap_adjust(nums, parent, length):
17
18     temp = nums[parent]
19     childIndex = 2 * parent + 1
20     while childIndex < length:
21         if childIndex + 1 < length and nums[childIndex] < nums[childIndex + 1]:
22             childIndex += 1
23         if temp > nums[childIndex]:
24             break
25         nums[parent] = nums[childIndex]
26         parent = childIndex
27         childIndex = 2 * parent + 1
28
29     nums[parent] = temp
30
31 nums = [53, 17, 78, 09, 45, 65, 87, 32]32 nums = heap_sort(nums)
33 print(nums)
时间: 2024-10-28 10:12:47

3.比较排序之堆排序的相关文章

阿布学排序之堆排序

/** * 需求:堆排序的实现 * 知识储备: * 满二叉树:除叶子结点外的所有结点均有两个子结点,所有叶子结点必须在同一层上. * 完全二叉树: * 若二叉树的深度为h,除第h层外,其它各层(1~h-1)的节点数都达到最大个数,第h层所有结点都连续集中在最左边. * 完全二叉树是有满二叉树而引出来的,对于深度为K的,有N个结点的二叉树,当且仅当每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称它为完全二叉树. * * 二叉堆是完全二叉树或者是近似完全二叉树. * 二叉堆特性: *

排序值 堆排序

按照算法导论上的实现,不过把下标改成从0开始了. 原理: import java.util.Arrays; public class Solution { /** * 每次将堆顶元素交换到最后并从堆中除掉. * * @param a */ public static void heapSort(int[] a) { buildHeap(a); int n = a.length - 1; for (int i = a.length - 1; i > 0; i--) { swap(a, 0, i);

选择排序:堆排序

堆排序(Heap Sort):使用堆这种数据结构来实现排序. 先看下堆的定义: 最小堆(Min-Heap)是关键码序列{k0,k1,-,kn-1},它具有如下特性: ki<=k2i+1, ki<=k2i+2(i=0,1,-) 简单讲:孩子的关键码值大于双亲的. 同理可得,最大堆(Max-Heap)的定义: ki>=k2i+1, ki>=k2i+2(i=0,1,-) 同样的:对于最大堆,双亲的关键码值大于两个孩子的(如果有孩子). 堆的特点: 堆是一种树形结构,而且是一种特殊的完全二

排序 之 堆排序 归并排序

堆的概念 堆是具有下列性质的完全二叉树:每个节点的值都大于或等于其左右孩子结点的值,称为大顶堆:或着每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆. 堆排序 堆排序(Heap Sort)就是利用堆(假设利用大顶堆)进行排序的方法.它的基本思想是,将待排序的序列构造成一个大顶堆.此时,整个序列的最大值就是对顶的根结点.将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次大值.如此反复执行,就可以得到一个

排序算法2--简单选择排序、堆排序

一.简单选择排序和堆排序都属于选择排序 选择排序的思想是:每一趟从待排序的记录中选出关键字最小的记录,按顺序放在以排序的记录序列的后面,知道全部拍完为止. 二.简单选择排序(直接选择排序) 1.简单选择排序法是每次循环找出最值,循环结束后将最值调整到合适位置,交换的次数少. 每次找出当前无序队列中的最小的元素与第一个交换位置,再选择第二小的与第二个交换位置 原始队列:   3 5 6 2 4 1(最小元素1与3交换) 第一步: 1 5 6 2 4 3 (当前最小元素2与5交换) 第二步: 1 2

处理海量数据的三大排序之——堆排序(C++)

在面对大数据量的排序时(100W以上量级数据),通常用以下三种的排序方法:快速排序.归并排序,堆排序.在这个量级上,其他冒泡,选择,插入排序等已经根本没法看了,效率极低,跟前面三种排序差了千百倍,因此不作比较. 这三种排序的平均时间复杂度均为O(nlogn),快速排序,归并排序在面对基本有序序列排序时,效率反会降低.且归并排序需要用到O(n)的临时存储空间.而堆排序没有明显缺点,特别在面对经常会插入新元素的排序需求,堆排序效果最好. 下面是三种排序对100W个无序数组进行排序的时间对比,可以看出

插入排序,希尔排序,堆排序详解

本文将介绍三种排序算法--插入排序,希尔排序,堆排序.本文所有例子都是使用升序 一.插入排序 算法思想 维护一个有序数组,将要插入的数据与有序数组自最后一个元素直到合适位置的数一一比较. eg: 有序数组:1,3,5,6,7   现在待插入数据为2,那么他将会和7,6,5,3,依次作比较,当带插入数据小于有序数组最后的元素大小,则将该元素后移,直到待插入元素找到合适位置为止. 代码实现 void InsertSort(int* a, int size) 02 { 03     assert(a)

插入排序,希尔排序,堆排序

本文将介绍三种排序算法--插入排序,希尔排序,堆排序.本文所有例子都是使用升序 一.插入排序 算法思想 维护一个有序数组,将要插入的数据与有序数组自最后一个元素直到合适位置的数一一比较. eg: 有序数组:1,3,5,6,7   现在待插入数据为2,那么他将会和7,6,5,3,依次作比较,当带插入数据小于有序数组最后的元素大小,则将该元素后移,直到待插入元素找到合适位置为止. 代码实现 void InsertSort(int* a, int size) { assert(a); for (int

排序算法七:选择排序之堆排序

排序算法七:选择排序之堆排序 声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 引言 在我的博文<"主宰世界"的10种算法短评>中给出的首个算法就是高效的排序算法.本文将对排序算法做一个全面的梳理,从最简单的"冒泡"到高效的堆排序等. 上博文讲述了选择排序中的简单排序算法,本文介绍的堆排序是树性选择排序,采用堆这个数据结构来辅助排序. 排序相关的的基本概念 排序:将一组杂乱无章的数据按一定的规律顺次排列起来. 数据

选择排序之堆排序

/* * 选择排序之堆排序 * 按照完全二叉树的顺序存储方式,建立一颗完全二叉树 * 若是大根堆:l(i)>=l(2*i),l(i)>=l(2*i+1) * 若是小根堆:l(i)<=l(2*i),l(i)<=(2*i+1) * 此时,初始堆建成,接下来的工作是进行调整 * 调整的是从[n/2]~1. * 针对大根堆:对于[n/2]处的元素,若其比左右孩子中的较大者小,进行交换 * 同理对于其他的 * void BuildHeap(ET a[],int len){ * for(int