• 【算法设计与分析基础】17、堆


    以数组来存放堆数据

    package cn.xf.algorithm.ch06ChangeRule;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.junit.Test;
    
    /**
     * 
     * 功能:堆的构造
     * 1、堆可以定义为一颗二叉树,树的节点包含键,并且满足一下条件
     * 	1) 树的形状要求:这棵二叉树是基本完备的(完全二叉树),树的每一层都是满的,除了最后一层最右边的元素可能缺位
     *  2) 父母优势,堆特性,每一个节点的键都要大于或者等于他子女的键(对于任何叶子我们认为这都是自动满足的)
     *  
     * 对于堆:
     * 	 只存在一颗n个节点的完全二叉树他的高度:取下界的 log2的n的对数
     * 	堆的根总是包含了堆的最大元素
     * 	堆的一个节点以及该节点的子孙也是一个堆
     * 	可以用数组的来实现堆,方法是从上到下,从左到右的方式来记录堆的元素。
     * @author xiaofeng
     * @date 2017年7月9日
     * @fileName Heap.java
     *
     */
    public class Heap {
        /**
         * 堆的数据存放结构
         */
        private List<Double> heap;
    
    	/**
    	 * 自下而上构建一个堆
    	 */
    	private List<Double> createHeadDownToUp(List<Double> heap) {
    		if(heap == null || heap.size() <= 0)
    			return heap;
    		
    		//数据个数
    		int nums = heap.size();
    		//吧数组整体后移一位,方便数据的计算,因为从0开始,那么2*0还是0,没有体现出2*n就是n的左孩子的基本设定
    		heap.add(0, 0d);
    		
    		//构建一个堆,从数组的中间位置开始,因为中间位子mid的两倍正好差不多是这个树的末尾,而在这个2*mid的附近就是mid这个节点的孩子节点
    		for(int i = nums / 2 + 1; i > 0; --i) {
    			//获取基准节点的地址
    			int baseIndex = i; 
    			//获取这个节点的值
    			double vBaseValue = heap.get(baseIndex);
    			boolean isHeap = false; //这个用来判断当前遍历的这三个数字是否满足堆的概念
    			//进行堆变换,交换树的节点和孩子节点数值,使当前树满足堆的概念
    			//2 * baseIndex <= nums 这个用来判断这颗树的子树也满足堆的定义
    			while(!isHeap && 2 * baseIndex <= nums) {
    				//获取当前遍历到的数据的左孩子节点的位置
    				int maxChildIndex = 2 * baseIndex;
    				//从两个孩子节点中获取大的那个位置
    				if(maxChildIndex < nums) {
    					//如果左孩子的位置比总长还小,由于完全二叉树的属性,那么必定存在右孩子节点
    					//判断那个孩子节点的数据比较大,使max为大的那个
    					if(heap.get(maxChildIndex) < heap.get(maxChildIndex + 1)) {
    						//如果右孩子比较大
    						maxChildIndex += 1;
    					}
    				}
    				
    				//再判断,当前 节点的值是不是比孩子节点的值要大,如果是那么就当前子树是满足堆的属性
    				//maxChildIndex == nums  那还是瞒住条件,可以进行左子树的比较
    				if(maxChildIndex > nums || vBaseValue >= heap.get(maxChildIndex)) {
    					isHeap = true;
    				} else {
    					//如果不满住,那么交换,吧大的数据交换到节点上,吧节点的数据换到孩子节点上
    					heap.set(baseIndex, heap.get(maxChildIndex));
    					baseIndex = maxChildIndex;
    					heap.set(baseIndex, vBaseValue);
    				}
    			}
    		}
    		
    		//去除第一个0,然后返回
    		heap.remove(0);
    		return heap;
    	}
    	
        private void shifHeadDownToUp(int i) {
            if (heap == null || heap.size() <= 0)
                return;
            
            // 数据个数
            int nums = heap.size();
            // 吧数组整体后移一位,方便数据的计算,因为从0开始,那么2*0还是0,没有体现出2*n就是n的左孩子的基本设定
            heap.add(0, 0d);
            boolean isHeap = false;
            int baseIndex = i;
            double vBaseValue = heap.get(i);
            while (!isHeap && 2 * baseIndex <= nums) {
                // 获取当前遍历到的数据的左孩子节点的位置
                int maxChildIndex = 2 * baseIndex;
                // 从两个孩子节点中获取大的那个位置
                if (maxChildIndex < nums) {
                    // 如果左孩子的位置比总长还小,由于完全二叉树的属性,那么必定存在右孩子节点
                    // 判断那个孩子节点的数据比较大,使max为大的那个
                    if (heap.get(maxChildIndex) < heap.get(maxChildIndex + 1)) {
                        // 如果右孩子比较大
                        maxChildIndex += 1;
                    }
                }
                
                // 再判断,当前 节点的值是不是比孩子节点的值要大,如果是那么就当前子树是满足堆的属性
                // maxChildIndex == nums 那还是瞒住条件,可以进行左子树的比较
                if (maxChildIndex > nums || vBaseValue >= heap.get(maxChildIndex)) {
                    isHeap = true;
                } else {
                    // 如果不满住,那么交换,吧大的数据交换到节点上,吧节点的数据换到孩子节点上
                    heap.set(baseIndex, heap.get(maxChildIndex));
                    baseIndex = maxChildIndex;
                    heap.set(baseIndex, vBaseValue);
                }
            }
            
            // 去除第一个0,然后返回
            heap.remove(0);
        }
    	
    	//创建堆
    	public Heap() {
    	    heap = new ArrayList<Double>();
    	    createHeadDownToUp(heap);
        }
    	
    	public Heap(List<Double> data) {
    	    if(data == null || data.size() <= 0) {
    	        data = new ArrayList<Double>();
    	    } 
    	    heap = data;
            createHeadDownToUp(heap);
    	}
    	
    	@Override
    	public String toString() {
    	    return heap.toString();
    	}
    	
    	public void add(Double value) {
    	    if(value == null)
    	        return;
    	    heap.add(value);
    //	    int insertInedx = heap.size();
    	    //自底向上构建堆
    	    for(int i = heap.size() / 2; i >= 0; --i) {
    	        shifHeadDownToUp(i + 1);
    	    }
    	}
    	
    	
    	/**
    	 * 删除一个元素,获取这个元素的索引位置来删除
    	 * 1、根的键《和》堆的最后一个键K做交换
    	 * 2、堆的规模减一
    	 * 3、严格按照自底向上的够着算法的做法,吧K 向下筛选,堆数据进行堆化
    	 * @param index
    	 */
    	public void delete(int index) {
    	    //这个是自底向上进行堆化数据
    	    //吧最后一个数据填入到要删除的数据中
    	    Double lastValue = heap.get(heap.size() - 1);
    	    //删除最后一个元素,吧最后一个元素用来取代这个需要删除的元素
    	    heap.set(index, lastValue);
    	    heap.remove(heap.size() - 1);
    	    //自底向上开始堆化
    	    for(int i = index; i >= 0; --i)
    	    	shifHeadDownToUp(i + 1);
    	}
    	
    }
    

      

  • 相关阅读:
    课时28:文件:因为懂你,所以永恒
    课时27:集合:在我的世界里,你就是唯一
    课时26:字典:各种内置方法
    课时25:字典:当索引不好用时
    课时24:递归:汉诺塔
    课时23:递归:这帮小兔崽子
    课时22:函数:递归是神马
    有序表查找-折半查找
    C#
    C#
  • 原文地址:https://www.cnblogs.com/cutter-point/p/7193098.html
Copyright © 2020-2023  润新知