zoukankan      html  css  js  c++  java
  • 8. 最大堆

    一、最大堆的定义

    最大堆是一棵完全二叉树,而且每个结点的值都不小于其孩子结点的值。

    二、选择数组作为最大堆的内存表现形式

    由于最大堆是一棵完全二叉树,所以我们使用数组的形式来存储最大堆的值,并从1号单元开始存储。

    假设树的节点个数为n,以1为下标开始编号,直到n结束。对于下标为 i 的结点,其父结点的坐标为i / 2,左孩子结点的坐标为i * 2,右孩子结点的坐标为i*2 + 1。

    例如,有数组heap[10] = {0, 100, 19, 36, 17, 3, 25, 1, 2, 7},其对应的完全二叉树如下图所示。

    • heap[leftChild] = heap[father * 2]
    • heap[rightChild] = heap[father * 2 + 1]
    • heap[fathrt] = heap[leftChild / 2] = heap[rightChild / 2]

    三、构造最大堆

    构造堆的基本思想就是:首先将每个叶子结点视为一个堆,再将每个叶子结点与其父结点一起构造成一个包含更多节点的堆。

    构造最大堆:首先需要找到最后一个结点的父结点,从这个结点开始构造最大堆,直到该结点前面的所有分支节点都处理完毕,这样最大堆就构造完毕了。

    如下图所示,我们要将一个heap[11] = {0, 4, 1, 3, 2, 16, 9, 10, 14, 8, 7}的二叉完全树构造成最大堆。因为最后一个结点为7,其父结点为16,所以应从16这个结点开始构造最大堆;构造完毕之后,转移到下一个父结点2,直到所有父结点都构造完毕。

    注:如上图(d)所示,当我们将结点1和结点16互换后,发现结点1又要和结点7互换。即结点1和结点16对调后,需要递归进行调整,请一定注意!

    代码:

    void create(MaxHeap &H)
    {
    	// 从最后一个结点的父结点开始构造最大堆 
    	for(int i = H.heapSize / 2; i > 0; --i) {
    		int temp = H.heap[i];			// 将父结点存放到temp中 
    		// 本次for循环的以下过程皆是为了寻找父结点最终应存放的位置
    		// 因为假设当前父结点与其子结点对调了,但它可能还要再与其下的新子结点对调
    		
    		// son一直记录当前父结点的孩子结点的坐标,所以它的值才一直在变  
    		int son = i * 2;
    		// 当前父结点递归下调过程				
    		while(son <= H.heapSize) {
    			// 右孩子结点的值更大 
    			if(son < H.heapSize && H.heap[son] < H.heap[son+1])
    				son++;
    			// 当前父结点已是以其为根结点的最大堆的最大值 
    			if(temp >= H.heap[son])
    				break;
    			// 需要与孩子结点对调,但此时尚不能退出循环,因为是递归下调 
    			else {
    				H.heap[son / 2] = H.heap[son];
    				son = son * 2;
    			}
    		}
    		H.heap[son/2] = temp;			// 找到父结点最终的位置,此时的son为父结点的最新孩子 
    	}
    } 
    

      

    四、插入

    上浮:先在堆的最后添加一个结点,然后沿着堆上升,直到找到正确的插入位置。

    过程:

    1. 在下一个空闲位置创建一个空穴。

    2. 如果元素X可以放在该空穴而并不破坏堆的序,那么插入完成;

    3. 否则,我们把空穴的父结点上的元素移入该空穴中,继续该过程直到X能被放入空穴。

    代码:

    /* 插入值为x的结点 */
    /*
     * 在堆的最后产生一个空穴,然后不断上移,直到找到能插入结点x的位置 
     * 空穴的最终位置就是结点x应插入的位置
     * 补:在空穴不断上移的过程中,要更换对应结点的值 
     */ 
    bool insert(MaxHeap &H, int x)
    {
    	if(H.maxSize == H.heapSize)	
    		return false;
    
    	H.heapSize++;
    	int xIndex = H.heapSize;		// 空穴的下标 
    	while(xIndex != 1 && x > H.heap[xIndex / 2]) {
    		H.heap[xIndex] = H.heap[xIndex / 2];	// 将那个比x小的根结点下沉到空穴 
    		xIndex = xIndex / 2;					// 空穴上浮到新的位置(上浮一层) 
    	} 
    	H.heap[xIndex] = x;
    	return true; 
    }

    五、删除最大元

    下沉:将堆的最后的结点提到根结点,然后删除最大值,然后再把新的根节点放到合适的位置。

    过程:

    1. 删除最大元后,会在根结点处产生一个空穴。

    2. 由于堆中少了一个元素,故堆中最后一个元素X必须移动到该堆的某个地方。

    3. 将空穴的两个儿子中较小者移入空穴,重复该过程直到X可以被放入空穴中。

    代码:

    /* 删除根结点 */
    bool deleteMax(MaxHeap &H)
    {
    	if(H.heapSize == 0)
    		return false;
    	// 将最后一个结点存到temp中 ,因为temp是要填入空穴的数,也即空穴的值就是temp 
    	int temp = H.heap[H.heapSize];			
    	H.heapSize--;
    	// 空穴出现在堆的根结点处,即下标为1处 
    	int newIndex = 1, son = newIndex * 2;		// son一直为空穴的孩子结点,随着空穴的下沉而改变
    	// 当空穴没有到最后一个位置时 
    	while(newIndex < H.heapSize) {
    		// 右孩子结点的值更大 
    		if(newIndex < H.heapSize && H.heap[son] <= H.heap[son+1]) 
    			son++;
    		// 孩子结点的值比空穴的值大,空穴下沉	
    		if(H.heap[son] > temp) {
    			H.heap[newIndex] = H.heap[son];
    			newIndex = son;
    			son = son * 2;	
    		}
    		// 得到空穴的最终位置 
    		if(H.heap[son] <= temp)
    			break;
    	}
    	H.heap[newIndex] = temp;
    	return true;
    } 
    

      

  • 相关阅读:
    关于浏览器及系统的判断
    toggle与slideToggle
    安卓与ios的不同处理
    关于常用循环遍历获取数据
    docker
    Mysql
    rabbitMQ的使用转载
    Git命令行
    vue项目创建完整版
    redis操作(str.hash.list.set)
  • 原文地址:https://www.cnblogs.com/xzxl/p/9575431.html
Copyright © 2011-2022 走看看