算法引论数据结构--堆的简介与实现

来源:互联网 发布:用什么软件做蹭饭地图 编辑:程序博客网 时间:2024/06/07 01:29

算法引论 数据结构—堆简介及部分代码实现

堆是一种二叉树,其所有节点的关键字均满足如下性质:

任意一个节点的关键字都大于或等于其任意一个子节点的关键字。

由传递性原理可知,堆得上述性质说明了堆上任意一个节点的关键字都大于或等于其“后代“的所有关键字。堆在实现优先级队列的时候很有用,优先级队列是一种抽象数据类型,通过如下两个操作定义:
Insert(x):把关键字x插入该数据结构
Remove():从该数据结构中删除最大的关键字。

堆可用显示结构表示,也可用隐式结构表示。我们要保证堆的平衡,我们是用隐式结构来实现。我们假设数组A[1…n]是其隐式表示,其中k可能是堆包含的元素的上限(如果上限不能确定,我们只能用链表)。把堆中目前元素的个数记做n,也就是说,我们只关心A[1..n].下面,我们就来描述如何用堆来实现对元素的高效插入和删除。

从Remove操作入手。根据堆的性质,堆中关键字最大的节点是根节点A[1],因此Remove总是删除根节点。我们首先取叶子节点A[n],把它移到根节点A[1]处,然后删除叶子节点A[n],n要自减1.我们记新的A[1]的值为x,得到的是两个独立的堆以及它们上方的值,但该值未必满足堆的性质。(此时只有从根节点到原先x所在节点的路径上的所有节点的值都是x,堆的性质才可能满足)。为了满足堆的性质,我们现在把x向树的下方移动,直到它到达某棵子树,在这棵子树中x是最大值。移动的方式则是通过比较x和它的两个子节点,比如(A[2],A[3]),如果x不是这里最大的值,那么我们把x与A[2],A[3]中最大的值作为交换,假设这里是A[3]最大,那么我们A[2]以及它的子树仍然满足堆的性质,我们只需考虑A[3]及它的子树是否满足堆的性质。我们用同样的方式对该子树进行同样的处理,假设目前处理到了第i步,A[j]中存放着x,按照上述方法比较x与它的子节点A[2j]和A[2j+1](如果存在的话),如果x不是这三个数中的最大值,则如上进行交换。当x为某棵子树的节点的最大值,或者x到达叶子节点时,算法终止。删除堆中元素的最大比较次数约为2lgn,刚好是树的两倍高。
下面是我们的实现代码
`
public class HeapDemo {
private int[] array;
private int size;

        public HeapDemo(int k) {            array = new int[k+1];   //下标0是不用的            size = 1 ;               //数组元素个数,也可以当成数组下标。        }        private void swap(int[] a, int i, int j) {            int temp = a[i]; a[i] = a[j]; a[j] = temp;        }        public void remove() throws Exception{            if (size == 0)                throw new Exception("heap empty");            array[1] = array[size];            size--;            int parent = 1, child = 2;            while (child <= size-1) {                if (array[child] < array[child+1])                         child++;                if (array[child] > array[parent]) {                    swap(array, child, parent);                    parent = child;                    child *= 2;                }                else                     child = size; //终止循环            }        }    }   `

Insert操作相似,先让n自增1,然后在叶子节点A[n]插入新值,接着比较这个新的叶子节点及其父节点的值,如果大于父节点的值,则交换这两个节点。这样一来,这个新值就是其子树的节点值最大的(因为原来的父节点的值最大,而它的值大于父节点的值)。通过归纳,可以假设以Aj为根节点的树满足堆的性质,且如果去掉这棵树,剩下的部分也满足堆的性质。由此继续执行,将新节点继续往树的上方移动,直到其值不比父节点的大(或者直到它已经到了根节点的位置)。至此,得到的这棵树就完全满足堆的性质。在堆中插入元素的最大比较次数约为lgn,恰好是树的高度。

在上述代码的基础上继续实现:

`    public void insert(int x) throws Exception{        if (size == array.length)            throw new Exception("heap full");        size++;        array[size] = x;        int child = size, parent = size/2;        //        while (parent >= 1) {            if (array[child] > array[parent]) {                swap(array, child, parent);                child = parent;                parent /= 2;            }            else                 parent = 0;        }    }`
阅读全文
0 0
原创粉丝点击