堆 续1

--------------------siwuxie095

堆的基本存储

在堆中实现的插入操作和删除操作,都是
logN 级别的,

显然,堆一定相应的是一个树形结构,最为经典的一种

堆的实现叫做
二叉堆(Binary Heap)

二叉堆对应的是二叉树,所谓二叉树,就是每一个节点,

最多有两个子节点

那么这个二叉树有什么特点呢?
分为两种情况:

(一)最大堆

1)它的任何一个节点都不大于它的父节点

2)它必须是一棵完全二叉树

满足上面两个性质的二叉树,称为
最大堆

也即

1)堆中某个节点的值总是不大于其父节点的值

2)堆总是一棵完全二叉树

(二)最小堆

1)它的任何一个节点都不小于它的父节点

2)它必须是一棵完全二叉树

满足上面两个性质的二叉树,称为
最小堆

也即

1)堆中某个节点的值总是不小于其父节点的值

2)堆总是一棵完全二叉树

『所谓完全二叉树,即
除了最底层,其它层的节点

都被元素填满,且最底层尽可能地从左到右填入』

下面以最大堆为例进行介绍:

最大堆在计算机上的一种实现:用数组存储二叉堆

可能很多人都曾经实现过一棵树,觉得既然堆是一个树形结构,就应该

采用链表的形式,设立一个节点,这个节点有左右两个指针分别指向它

的左右孩子,这样可以实现一个堆

不过,对于堆而言,有一个非常经典的实现,这种实现方式是使用数组

来存储一个二叉堆

之所以可以使用数组来存储一个二叉堆,正是因为堆是一棵完全二叉树

对这颗完全二叉树,自上到下、自左到右的给每一个节点标上一个序列

号,即
依照层序自上到下、之后在每一层自左到右的标上序列号

「序列号

索引」

如果第一个节点(根节点)的索引为
1,则:

对于每一个节点来说,相应的左孩子的索引就是自身索引的二倍,

而相应的右孩子的索引就是自身索引的二倍加一


0


1


2


3


4


5


6


7


8


9


10


-


62


41


30


28


16


22


13


19


17


15

注意:索引 0 是不使用的

通过如下公式,即可找到每个元素的双亲和左右孩子:

(1)parent ( i ) = i / 2

(2)left child ( i ) = 2 * i

(3)right child ( i ) = 2 * i + 1

如果第一个节点(根节点)的索引为 0,则:

对于每一个节点来说,相应的左孩子的索引就是自身索引的二倍加一,

而相应的右孩子的索引就是自身索引的二倍加二


0


1


2


3


4


5


6


7


8


9


62


41


30


28


16


22


13


19


17


15

通过如下公式,即可找到每个元素的双亲和左右孩子:

(1)parent ( i ) = ( i - 1 ) / 2

(2)left child ( i ) = 2 * i + 1

(3)right child ( i ) = 2 * i + 2

如何向最大堆中添加元素(即 插入)?

这里用到了一个核心操作,通常叫做 Shift Up

这个堆使用了数组进行表示,所以相应的,在最大堆中添加元素

就相当于在数组的末尾添加元素,但此时的堆可能不满足最大堆

的定义

所以要进行一系列的操作来维护堆的定义,具体应该怎么做呢?

其实这个过程非常简单,只需要把新加入的元素调整到一个合适

的位置,使得整个二叉树依然保持最大堆的性质

具体来说:将新加入的元素和父节点的元素进行比较,如果父节

点的元素比新加入的元素还要小,就交换位置

新加入的元素逐渐上移的过程,就是
Shift Up 的过程

如何从最大堆中取出元素(即 删除)?

这里用到了一个核心操作,通常叫做 Shift Down

如果要想从堆中取出元素,只能取出根节点的那个元素,对于最

大堆来说,就相当于取出了优先级最大的那个元素

此时,整个堆中相当于少了一个元素,怎么填补这个元素呢?

只需要把整个堆的最后一个元素放到整个堆的第一个元素(根节

点)的位置即可,但此时的堆可能不满足最大堆的定义

实际上是互换了位置,之后计数器
count--,无法访问原根节点

现在所在的位置,相当于少了一个元素,即被取出了

所以要进行一系列的操作来维护堆的定义,具体应该怎么做呢?

其实这个过程非常简单,只需要把此时根节点的元素调整到一个

合适的位置,使得整个二叉树依然保持最大堆的性质

具体来说:比较左右孩子的元素,谁大就和谁交换位置

根节点的元素逐渐下移的过程,就是
Shift Down 的过程

最小堆的情况类似 …

【made by siwuxie095】

时间: 2024-10-12 19:49:51

堆 续1的相关文章

堆 续4

--------------------siwuxie095 索引堆 这里介绍一个比普通堆更加高级的数据结构:索引堆(Index Heap) 首先来看一下普通堆有什么问题 或 缺点: 将一个数组通过 Heapify 构建成一个堆,对于这个数组而言, 在堆构建前和堆构建后,它的元素位置发生了改变,正是因为 元素位置的改变,才使得它被看做是一个堆 但在构建堆的过程中,改变元素的位置将会有一些局限性: (1)如果元素是非常复杂的结构,如:元素是字符串,那么交换这些元素 本身,消耗就是巨大的 比如说:如

堆 续2

---------------------siwuxie095 索引从 1 开始 程序 1:最大堆的实现 MaxHeap.h: #ifndef MAXHEAP_H #define MAXHEAP_H #include <iostream> #include <algorithm> #include <string> #include <cmath> #include <cassert> using namespace std; //最大堆:索引从

堆 续8

----------------------siwuxie095 索引从 0 开始 程序 1:最小索引堆的实现 MinIndexHeap.h: #ifndef MININDEXHEAP_H #define MININDEXHEAP_H #include <iostream> #include <string> #include <cassert> #include <algorithm> using namespace std; //最小索引堆:索引从0开始

数据结构-各类排序算法总结[续]

各类排序算法总结 三.交换类排序[接上] 2.快速排序 快速排序是通过比较关键码.交换记录,以某个记录为界(该记录称为支点),将待排序列分成两部分.其中,一部分所有记录的关键码大于等于支点记录的关键码,另一部分所有记录的关键码小于支点记录的关键码.我们将待排序列按关键码以支点记录分成两部分的过程,称为一次划分.对各部分不断划分,直到整个序列按关键码有序. 如果每次划分对一个元素定位后,该元素的左侧子序列与右侧子序列的长度相同,则下一步将是对两个长度减半的子序列进行排序,这是最理想的情况! [算法

栈内存和堆内存的区别

总结: 1 栈:为编译器自动分配和释放,如函数参数.局部变量.临时变量等等 2 堆:为成员分配和释放,由程序员自己申请.自己释放.否则发生内存泄露.典型为使用new申请的堆内容. 除了这两部分,还有一部分是: 3 静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.它主要存放静态数据.全局数据和常量. 转自: 栈内存和堆内存的区别(一个笔试题的一部分)http://blog.csdn.net/richerg85/article/details/19175133 笔试

【裸单源最短路:Dijkstra算法两种版本】hdu 1874 畅通工程续

Source : hdu 1874 畅通工程续 http://acm.hdu.edu.cn/showproblem.php?pid=1874 Problem Description 某省自从实行了很多年的畅通工程计划后,终于修建了很多路.不过路多了也不好,每次要从一个城镇到另一个城镇时,都有许多种道路方案可以选择,而某些方案要比另一些方案行走的距离要短很多.这让行人很困扰. 现在,已知起点和终点,请你计算出要从起点到终点,最短需要行走多少距离. Input 本题目包含多组数据,请处理到文件结束.

浅谈数据结构-堆

在数据结构的世界中有一个叫堆的玩意,这玩意有什么用呢?无用,都去pq了 堆,其实就是一棵完全二叉树. “若设二叉树的深度为h,除第 h 层外,其它各层 (1-h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树”  by 谋财害命公司 百度 ↑清真的 完全二叉树 ↓ 啊那么为什么会闲的无聊出现这种奇怪的数据结构呢? 因为我们的某些算法可能需要堆来进行优化,如dj,prim. 堆可以在O(1)的时间取出最优值,但是需要O(logn)的时间修改和O(nlogn)

1续hdu1009

上网查了资料,看了其他人写的代码,学到了一下一些: c++头文件:algorithm.h 这个头文件可以对范围内的数据做任何处理 #include<algorithm> 里面包含的函数有:   非修改性序列操作(12个)   循环 对序列中的每个元素执行某操作 for_each()   查找 在序列中找出某个值的第一次出现的位置 find()   在序列中找出符合某谓词的第一个元素 find_if()   在序列中找出一子序列的最后一次出现的位置 find_end()   在序列中找出第一次出

设计模式一(续)

本篇是接着设计模一:观察者模式的续写. 为什么要写这一篇呢: java在main函数里new出的对象都是局部变量,而用C++ 在main函数里new出来的都是 动态分配到堆区的. 那么可不可以按照java的思路来写呢. 这就是写本篇的原因了:C++完全可以按照java的思路来实现(使用引用) 附上代码: #include "stdafx.h" #include<iostream> #include<string> #include<vector> u