- 浏览: 1115910 次
文章分类
- 全部博客 (379)
- S2SH (16)
- stuts2 (0)
- java语言 (81)
- JSP (17)
- <html>元素 (11)
- javaweb (4)
- web容器 (3)
- ext (23)
- javaScript (48)
- ant (1)
- liferay (1)
- sql (9)
- css (42)
- 浏览器设置 (3)
- office_world (1)
- eclipse (4)
- 其它 (28)
- 操作系统 (5)
- android (6)
- Struts2 (11)
- RegEx (3)
- mysql (5)
- BigDATA (1)
- Node.js (1)
- Algorithm (10)
- Apache Spark (1)
- 数据库 (5)
- linux (2)
- git (1)
- Adobe (3)
- java语言,WebSocket (1)
- Maven (3)
- SHELL (1)
- XML (2)
- 数学 (2)
- Python (2)
- Java_mysql (1)
- ReactJS (6)
- 养生 (4)
- Docker (1)
- Protocols (3)
- java8 (2)
- 书籍 (1)
- Gradle (2)
- AngularJS (5)
- SpringMVC (2)
- SOAP (1)
- BootstrapCSS (1)
- HTTP协议 (1)
- OAuth2 (1)
最新评论
-
Lixh1986:
Java并发编程:自己动手写一把可重入锁https://blo ...
Java之多线程之Lock与Condition -
Lixh1986:
http://win.51apps.com.cn/https: ...
temp -
ztwsl:
不错,支持很好
HttpServletRequest和ServletRequest的区别 -
guodongkai:
谢谢您能将知识精华汇编总结,让初学者们从原理中学会和提高。
javaScript之function定义 -
kangwen23:
谢谢了,顶顶
struts2中的ValueStack学习
Algorithm之排序之堆排序(Heap Sort)
一、什么是堆
在讲述堆之前,首先看一下二叉树。
二叉树:
每个节点最多有两个子节点,且这两个子节点有左右次序之分。
1、满二叉树
二叉树的所有非叶子节点都被填满。
因此:一颗深度为 K 的满二叉树,其节点总数为:2的k次方 - 1
= 1 + 2 + 4 + 8 + ... + 2 pow(k-1)
= 2^0 + 2^1 + 2^2 + 2^3 + ... + 2^k-1 = 2pow(k) - 1
图(深度为 3 的满二叉树):
2、完全二叉树
有 n 个节点,深度为 k 的二叉树,
当且仅当,这 n 个节点的排列序号,与深度为 k 的满二叉树中
序号为 1 至 n 的节点一一对应时,称之为完全二叉树。
即:
- 除了最外层,其它层都是有序且填满的。
- 上一层没有排完,不能排下一层。
图(深度为 3 的完全二叉树):
3、堆
仅从数据结构角度看:符合完全二叉树的数据结构称之为:堆。
且,所有父节点比其下子节点都大(或小),
但,堆不要求每层中左右子节点的大小顺序。
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
二、堆的操作方法
问题背景:为什么要操作堆?
从数据结构可以看出,堆是用来排序的。
所以对堆的操作,基本上都是排序操作。
1、排序之:插入节点
每次插入都是将新数据放在数组最后。然后依次向上调整。
直至符合:父节点比子节点都大(或小)。
2、排序之:删除节点
按定义,堆中每次都只能删除最顶层的那个节点(数组中索引为0的元素)。
为了便于重建堆,实际的操作是:
1. 将数组最末尾的元素与根节点位置调换,
2. 然后删除最末尾节点。
3. 然后再只需调整根节点使其符合堆特性即可。(其它节点是已调整好的)
以最小堆为例:
调整根节点时先在左右子结点中找最小的,如果根结点比这个最小的子结点还小说明不需要调整了,
反之将父结点和它交换后,还需要再考虑后面的结点。相当于从根结点将一个数据的“下沉”过程。
三、堆与数组的关系
基于:数组的索引是从 0 开始(Zero-Based):
从上图中可以发现,以数组表示的堆,其数据结构中的每个节点的索引值存在这样的特点:
父节点:parent(i) = (i - 1) >> 1;
子节点(左):leftChild(i) = i * 2 + 1;
子节点(右):rightChild(i) = i * 2 + 2;
任何一个数组,附以上述规则,那么这个数组就可以被看作是一个堆。
也就是说,堆可以用数组表示。反之:任何一个数组,都可以逻辑为一个堆。
堆的逻辑结构:完全二叉树
堆的存储结构:数组(内存中一块地址连续的空间),或:一堆空间。
既于,数组具天然有序索引这一特性:数组的索引就是一个排好序的堆。
那么,只需要按照数组的索引将其值放置为符合堆的规则即可——堆排序。
四、堆排序的过程
堆排序共分两步:
第一步:将数组堆化
把数组的每个节点,调整为具有堆的该特性:所有的父节点都比子节点大(或小)。
方法:遍历数组的每个节点
1、遍历的方向:从最外层向内层遍历(自底向上)
2、从哪里开始:从后向前数,从第一个非叶子节点开始遍历。
(没有必要从叶子节点开始。
叶子节点可以看作是已符合堆特点的节点。
因为它没有子节点,所以作为父节点,它就是最大的。)
第一个非叶子节点的索引值可以根据最后一个元素的索引值求得:
lastNodeIndex = (lastLeafIndex - 1) >> 1 = (arr.length - 2) >> 1;
3、每步遍历的规则:
非叶子节点与其下面的子节点做比较,大的排上面。
(虽然堆的子节点是不要求有大小顺序的,但是因为我们要对堆进行排序,
故:对于每个节点和它的子节点都要相互比较大小。
对于大堆,最大的那个排在父节点的位置。
对于小堆,最小的那个排在父节点的位置。)
4、每步遍历多少次:如果子节点与父节点交换了位置,则交换位置后的父节点仍需要向下验证。
第二步:将堆化数组排序
堆排序的过程就是,不断移出最顶层(数组的第一个)元素的过程。
1、把移出的顶层元素与数组最后一个元素交换位置。
2、同时遍历的长度减 1,
3、然后从新调整剩余的数据,使其符合堆的特性。(
此时只需要调整最顶层元素即可,其它层都是已经被初始化好的)
重复该过程,直至需要遍历的数组长度为 0 。
排好序的堆就是一颗完全二叉树了:
1、所有的父节点都比子节点小(或大)
2、同一节点的左子节点比右子节点小(或大)
排序动画:
Javascript代码实现:
测试:
Java 版本:
五、堆排序分析
堆排序与归并排序一样,是一种时间复杂度为O(N * logN)的算法,
同时和插入排序一样,是一种就地排序算法(不需要额外的存储空间)。
-
转载请注明,
原文出处:http://lixh1986.iteye.com/blog/2354246
-
引用:
堆_(数据结构)
https://zh.wikipedia.org/wiki/堆_(数据结构)
常见排序算法 - 堆排序 (Heap Sort)
http://bubkoo.com/2014/01/14/sort-algorithm/heap-sort/
堆排序详解
http://blog.csdn.net/daiyudong2020/article/details/52529791
堆排序详解
http://www.cnblogs.com/hdk1993/p/4635923.html
堆排序 - 维基百科
https://zh.wikipedia.org/wiki/堆排序
一、什么是堆
在讲述堆之前,首先看一下二叉树。
二叉树:
每个节点最多有两个子节点,且这两个子节点有左右次序之分。
1、满二叉树
二叉树的所有非叶子节点都被填满。
因此:一颗深度为 K 的满二叉树,其节点总数为:2的k次方 - 1
= 1 + 2 + 4 + 8 + ... + 2 pow(k-1)
= 2^0 + 2^1 + 2^2 + 2^3 + ... + 2^k-1 = 2pow(k) - 1
图(深度为 3 的满二叉树):
2、完全二叉树
有 n 个节点,深度为 k 的二叉树,
当且仅当,这 n 个节点的排列序号,与深度为 k 的满二叉树中
序号为 1 至 n 的节点一一对应时,称之为完全二叉树。
即:
- 除了最外层,其它层都是有序且填满的。
- 上一层没有排完,不能排下一层。
图(深度为 3 的完全二叉树):
3、堆
仅从数据结构角度看:符合完全二叉树的数据结构称之为:堆。
且,所有父节点比其下子节点都大(或小),
但,堆不要求每层中左右子节点的大小顺序。
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
二、堆的操作方法
问题背景:为什么要操作堆?
从数据结构可以看出,堆是用来排序的。
所以对堆的操作,基本上都是排序操作。
1、排序之:插入节点
每次插入都是将新数据放在数组最后。然后依次向上调整。
直至符合:父节点比子节点都大(或小)。
2、排序之:删除节点
按定义,堆中每次都只能删除最顶层的那个节点(数组中索引为0的元素)。
为了便于重建堆,实际的操作是:
1. 将数组最末尾的元素与根节点位置调换,
2. 然后删除最末尾节点。
3. 然后再只需调整根节点使其符合堆特性即可。(其它节点是已调整好的)
以最小堆为例:
调整根节点时先在左右子结点中找最小的,如果根结点比这个最小的子结点还小说明不需要调整了,
反之将父结点和它交换后,还需要再考虑后面的结点。相当于从根结点将一个数据的“下沉”过程。
三、堆与数组的关系
基于:数组的索引是从 0 开始(Zero-Based):
从上图中可以发现,以数组表示的堆,其数据结构中的每个节点的索引值存在这样的特点:
父节点:parent(i) = (i - 1) >> 1;
子节点(左):leftChild(i) = i * 2 + 1;
子节点(右):rightChild(i) = i * 2 + 2;
任何一个数组,附以上述规则,那么这个数组就可以被看作是一个堆。
也就是说,堆可以用数组表示。反之:任何一个数组,都可以逻辑为一个堆。
堆的逻辑结构:完全二叉树
堆的存储结构:数组(内存中一块地址连续的空间),或:一堆空间。
既于,数组具天然有序索引这一特性:数组的索引就是一个排好序的堆。
那么,只需要按照数组的索引将其值放置为符合堆的规则即可——堆排序。
四、堆排序的过程
堆排序共分两步:
第一步:将数组堆化
把数组的每个节点,调整为具有堆的该特性:所有的父节点都比子节点大(或小)。
方法:遍历数组的每个节点
1、遍历的方向:从最外层向内层遍历(自底向上)
2、从哪里开始:从后向前数,从第一个非叶子节点开始遍历。
(没有必要从叶子节点开始。
叶子节点可以看作是已符合堆特点的节点。
因为它没有子节点,所以作为父节点,它就是最大的。)
第一个非叶子节点的索引值可以根据最后一个元素的索引值求得:
lastNodeIndex = (lastLeafIndex - 1) >> 1 = (arr.length - 2) >> 1;
3、每步遍历的规则:
非叶子节点与其下面的子节点做比较,大的排上面。
(虽然堆的子节点是不要求有大小顺序的,但是因为我们要对堆进行排序,
故:对于每个节点和它的子节点都要相互比较大小。
对于大堆,最大的那个排在父节点的位置。
对于小堆,最小的那个排在父节点的位置。)
4、每步遍历多少次:如果子节点与父节点交换了位置,则交换位置后的父节点仍需要向下验证。
第二步:将堆化数组排序
堆排序的过程就是,不断移出最顶层(数组的第一个)元素的过程。
1、把移出的顶层元素与数组最后一个元素交换位置。
2、同时遍历的长度减 1,
3、然后从新调整剩余的数据,使其符合堆的特性。(
此时只需要调整最顶层元素即可,其它层都是已经被初始化好的)
重复该过程,直至需要遍历的数组长度为 0 。
排好序的堆就是一颗完全二叉树了:
1、所有的父节点都比子节点小(或大)
2、同一节点的左子节点比右子节点小(或大)
排序动画:
Javascript代码实现:
function heapSort(arr){ // step 1. heapify array var len = arr.length - 1; var lastNodeIndex = (len - 1) >> 1; for(var i = lastNodeIndex; i >= 0; i--){ maxHeapify(arr, i, len); } // step 2. reduce heap for(var i = 0; i < len; i++){ swap(arr, 0, len - i); maxHeapify(arr, 0, len - i - 1); } return arr; } function swap(arr, i, j){ var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } function maxHeapify(arr, index, len){ var li = (index << 1) + 1, // left child index. ri = li + 1, // right child index. cMax = li; // default Child Max valule. if(li > len) return; // left child is out of range. if(ri <= len && arr[ri] > arr[li]) // choose the bigger one. cMax = ri; if(arr[cMax] > arr[index]){ // if swap(arr, index, cMax); // swapped, maxHeapify(arr, cMax, len); // next check should be executed. } } /* // alternative method for maxHeapify(); function maxHeapify2(array, index, len) { var iLeft, iRight, iMax; while (true) { iLeft = (index << 1) + 1; iRight = iLeft + 1; iMax = iLeft; if(iLeft > len) break; if(iRight <= len && array[iRight] > array[iLeft]) iMax = iRight; if(array[iMax] <= array[index]) break; swap(array, iMax, index); index = iMax; } } */
测试:
var arr = [1,23,14,8,10,9,235,21,45,56,4,32,79,15,56,985,343]; heapSort(arr); // [1, 4, 8, 9, 10, 14, 15, 21, 23, 32, 45, 56, 56, 79, 235, 343, 985]
Java 版本:
import java.util.Arrays; public class HeapSort { private int[] arr; public HeapSort(int[] arr) { this.arr = arr; } /** * 堆排序的主要入口方法,共两步。 */ public void sort() { /* * 第一步:将数组堆化 * beginIndex = 第一个非叶子节点。 * 从第一个非叶子节点开始即可。无需从最后一个叶子节点开始。 * 叶子节点可以看作已符合堆要求的节点,根节点就是它自己且自己以下值为最大。 */ int lastLeafIndex = arr.length - 1; int beginIndex = (lastLeafIndex - 1) >> 1; for (int i = beginIndex; i >= 0; i--) maxHeapify(i, lastLeafIndex); /* * 第二步:对堆化数据排序 * 每次都是移出最顶层的根节点A[0],与最尾部节点位置调换,同时遍历长度 - 1。 * 然后从新整理被换到根节点的末尾元素,使其符合堆的特性。 * 直至未排序的堆长度为 0。 */ for (int i = lastLeafIndex; i > 0; i--) { swap(0, i); maxHeapify(0, i - 1); } } private void swap(int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } /** * 调整索引为 index 处的数据,使其符合堆的特性。 * * @param index 需要堆化处理的数据的索引 * @param len 未排序的堆(数组)的长度 */ private void maxHeapify(int parentNodeIndex, int lastLeafIndex) { int li = (parentNodeIndex << 1) + 1, // 左子节点索引 ri = li + 1, // 右子节点索引 cMax = li; // 子节点值最大索引,默认左子节点。 if (li > lastLeafIndex) return; // 左子节点索引超出计算范围,直接返回。 if (ri <= lastLeafIndex && arr[ri] > arr[li]) // 先判断左右子节点,哪个较大。 cMax = ri; if (arr[cMax] > arr[parentNodeIndex]) { swap(cMax, parentNodeIndex); // 如果父节点被子节点调换, maxHeapify(cMax, lastLeafIndex); // 则需要继续判断换下后的父节点是否符合堆的特性。 } } /** * 测试用例 * * 输出: * [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9] */ public static void main(String[] args) { int[] arr = new int[] {3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6}; new HeapSort(arr).sort(); System.out.println(Arrays.toString(arr)); } }
五、堆排序分析
堆排序与归并排序一样,是一种时间复杂度为O(N * logN)的算法,
同时和插入排序一样,是一种就地排序算法(不需要额外的存储空间)。
-
转载请注明,
原文出处:http://lixh1986.iteye.com/blog/2354246
-
引用:
堆_(数据结构)
https://zh.wikipedia.org/wiki/堆_(数据结构)
常见排序算法 - 堆排序 (Heap Sort)
http://bubkoo.com/2014/01/14/sort-algorithm/heap-sort/
堆排序详解
http://blog.csdn.net/daiyudong2020/article/details/52529791
堆排序详解
http://www.cnblogs.com/hdk1993/p/4635923.html
堆排序 - 维基百科
https://zh.wikipedia.org/wiki/堆排序
发表评论
-
TOPK
2019-03-03 13:11 690实现思路: TopN算法:从已经存在的数组中,找出最大(或最 ... -
二分查找有序数组并插入(不能解决TopK问题)
2019-03-03 11:00 1077需求:将一个数插入(替换原来的数)到一个有续的数组中,插入成功 ... -
Algorithm之Unique Path
2017-02-03 16:15 784经典排列组合与动态规划题 一、原题: // ... -
Algorithm之3sum closest及负数在Java中的表示
2017-01-23 14:41 890由一道算法题想到的 1 ... -
Algorithm之二分治应用之pow(x,n)
2017-01-23 08:36 1017设计算法,求 x 的 n 次幂:pow(x, n) 算法一 ... -
Algorithm之排序之归并排序
2017-01-18 20:54 726Algorithm之排序之归并排序 一、归并介绍 动画1: ... -
Algorithm之动态规划之最佳买股票时机
2017-01-16 14:22 2700动态规划(DP: Dynamic Programming) ... -
Algorithm之二分枚举之copy books
2017-01-13 16:29 1612copy books Before the inventio ... -
数据库之索引(Index)
2016-12-03 12:04 8774在数据之外,数据库系统还维护着满足特定查找算法的数据结构。 这 ...
相关推荐
C++实现时,可以使用标准库中的`<algorithm>`中的`make_heap`、`pop_heap`和`sort_heap`函数。堆排序在最坏、最好和平均情况下的时间复杂度都是O(n log n)。 4. **快速排序**: 快速排序是C++中最常用的排序算法之...
3. **完整实现**:在C++中,可以使用`make_heap()`函数来创建堆,`pop_heap()`函数来删除堆顶元素并调整堆,`sort_heap()`函数则可以直接完成堆排序。 下面是一个基本的C++实现堆排序的示例代码: ```cpp #include...
最大堆排序(MaxHeap Sort)是一种基于比较的排序算法,它利用了数据结构中的最大堆特性来实现排序。最大堆是一种特殊的二叉堆,每个父节点的值都大于或等于其子节点的值,通常以数组的形式存储。下面将详细介绍最大...
虽然堆排序可以手动实现,但VC++提供了标准模板库(STL),其中`<algorithm>`头文件包含了一些排序算法如`std::make_heap`、`std::push_heap`、`std::pop_heap`和`std::sort_heap`,这些可以简化堆排序的实现。...
8. 堆排序(Heap Sort): 堆排序是一种树形选择排序,通过构造二叉堆进行排序。它可以视为选择排序的一种优化,因为它在原地排序数组,并且只需要O(1)的额外空间。堆排序的平均时间复杂度为O(n log n)。 以上各种...
7. **堆排序**(Heap Sort):堆排序利用了数据结构——二叉堆的特性。将待排序的序列构造成一个大顶堆或小顶堆,此时整个序列的最大值或最小值就是堆顶的根节点。然后将其与末尾元素进行交换,此时末尾就为最大或...
C++作为一门强大的系统级编程语言,提供了标准模板库(STL),其中的`<algorithm>`头文件包含了各种排序算法,包括heap_sort()函数,可以直接用于堆排序。但在这个项目中,开发者可能选择手动实现堆排序,这样不仅...
在C++实现中,可以使用标准库中的`<algorithm>`头文件,特别是`make_heap()`、`push_heap()`、`pop_heap()`和`sort_heap()`这些函数来简化最小堆排序的过程。但是,为了更好地理解和控制堆的操作,自定义一个堆类...
在C++中,我们可以使用标准模板库(STL)中的`<algorithm>`库来辅助实现,例如使用`make_heap()`、`push_heap()`、`pop_heap()`和`sort_heap()`等函数。但为了理解堆排序的核心逻辑,通常我们会自己编写这些功能。 ...
**文件名称“A multi-branches heap-sort algorithm”** 这个文件名表明可能包含了一个多分支堆排序的算法实现,可能是代码示例或者算法分析文档。通过阅读这份文件,我们可以深入理解d叉树堆排序的实现细节,包括...
在C++中,我们通常使用`<algorithm>`库中的`make_heap()`, `push_heap()`, `pop_heap()` 和 `sort_heap()` 这些函数来实现堆排序。下面是一个简单的堆排序函数示例: ```cpp #include #include <algorithm> using ...
5. **堆排序(Heap Sort)**: - 过程:构造一个大顶堆或小顶堆,将堆顶元素与末尾元素交换,然后调整剩余元素重新形成堆,重复此过程直到只剩一个元素。 - 特点:原地排序,无需额外空间,但相比其他O(n log n)...
在C++中实现堆排序,可以利用标准库中的`<algorithm>`中的`make_heap`、`push_heap`、`pop_heap`和`sort_heap`函数。以下是一个简单的堆排序示例: ```cpp #include <algorithm> #include void heapSort(std::...
首先,我们来讨论堆排序(Heap Sort)。堆排序是一种基于比较的排序算法,它利用了完全二叉树的特性构建最大(或最小)堆。在最大堆中,每个父节点的值都大于或等于其子节点;在最小堆中则相反。堆排序的过程分为两...
在C++中,我们可以使用标准库中的`<algorithm>`头文件中的`make_heap()`、`pop_heap()`和`sort_heap()`函数来实现堆排序。这些函数可以帮助我们快速构建、调整和处理堆。 下面是一个简化的C++堆排序代码示例: ```...
各种排序算法,包括希尔算法,快速排序,堆排序-A variety of sorting algorithms, including the Hill algorithm, quick sort, heap sort
6. 堆排序(Heap Sort):利用堆这种数据结构所设计的一种排序算法,构建大顶堆或小顶堆,然后将堆顶元素与末尾元素交换,再调整堆。 7. 计数排序(Counting Sort)、桶排序(Bucket Sort)和基数排序(Radix Sort...
5. 堆排序(Heap Sort): 堆排序利用了堆这种数据结构。首先将待排序的序列构造成一个大顶堆或小顶堆,然后将堆顶元素与末尾元素交换,这样末尾就是最大(或最小)元素。之后对剩下的元素重新调整为堆,重复这个...
- `HeapAlgorithm.java` 文件包含了堆排序的实现。堆排序是一种树形选择排序,它维护了一个最大(或最小)堆,每次将堆顶元素与最后一个元素交换,然后调整堆,这样就能确保每个元素都在正确的位置。 6. **Quick ...
C++实现堆排序,可以使用标准库中的`<algorithm>`中的`make_heap`、`push_heap`、`pop_heap`和`sort_heap`函数。 对于四种不同类型的数据,排序算法的效率可能会有所不同。例如,如果数据已经部分有序,插入排序和...