1. 程式人生 > >【算法設計與分析基礎】23、堆排序-2

【算法設計與分析基礎】23、堆排序-2

mov 完全二叉樹 return 遍歷 滿足 指定 val cti eap

package cn.xf.algorithm.ch09Greedy.util;

import java.util.ArrayList;
import java.util.List;

/**
 * 堆構造以及排序
 * 
 * .功能:堆的構造
 *  1、堆可以定義為一顆二叉樹,樹的節點包含鍵,並且滿足一下條件
 *  1) 樹的形狀要求:這棵二叉樹是基本完備的(完全二叉樹),樹的每一層都是滿的,除了最後一層最右邊的元素可能缺位
 *  2) 父母優勢,堆特性,每一個節點的鍵都要大於或者等於他子女的鍵(對於任何葉子我們認為這都是自動滿足的)
 *  
 * 對於堆:
 *   只存在一顆n個節點的完全二叉樹他的高度:取下界的 log2的n的對數
 *  堆的根總是包含了堆的最大元素
 *  堆的一個節點以及該節點的子孫也是一個堆
 *  可以用數組的來實現堆,方法是從上到下,從左到右的方式來記錄堆的元素。
 * 
 * @author xiaof
 * @version Revision 1.0.0
 * @see:
 * @創建日期:2017年8月25日
 * @功能說明:
 *
 */
public class Heap {
    private List<Integer> heap;
    
    //構造函數
    public Heap() {
    	//創建堆
        heap = new ArrayList<Integer>();
    }
    
    public Heap(List<Integer> heap) {
    	//創建堆
    	this.heap = heap;
        createHeadDownToUp(this.heap);
    }
    
    /**
     * 從小到大的堆
     * @param heap
     * @return
     */
    private void createHeadDownToUp(List<Integer> heap){
        //對數組進行堆排序
    	if(heap == null || heap.size() <= 0)
    		return;
    	int len = heap.size();
    	//從樹的中間開始循環
    	for(int i = len / 2; i > 0; --i) {
    		//首先預存當前進行操作的節點‘
    		//索引和值
    		int selectIndex = i - 1;
    		int selectValue = heap.get(selectIndex);
    		boolean isHeap = false; //用來判斷當前節點下是否已經沒有其他節點比這個節點小了,作為是否成堆的標識
    		while(!isHeap && 2 * (selectIndex + 1) <= len) {
    			//當前節點的最大的孩子節點的位置,開始默認是第一個孩子節點的位置
        		int childIndex = 2 * i - 1;
        		//判斷是否存在兩個孩子節點,如果存在,那麽就選出最大的那個
        		if(2 * i < len) {
        			//獲取比較小的那個節點作為備選替換節點
        			childIndex = heap.get(childIndex) < heap.get(childIndex + 1) ? childIndex : childIndex + 1;
        		}
        		//判斷當前節點是不是比下面最小的那個節點還要小
        		if(selectValue <= heap.get(childIndex)) {
        			//如果比下面最大的還大,那就表明這個節點為根的子樹已經是一顆樹了
        			isHeap = true;
        		} else {
        			//如果節點不是小的,那麽更換掉
        			heap.set(selectIndex, heap.get(childIndex));
        			//並交換當前遍歷交換的節點 
        			selectIndex = childIndex;
        			//這個節點和子節點全部遍歷結束之後,交換出最初用來交換的選中節點
            		heap.set(selectIndex, selectValue);
        		}
    		}
    	}
    }
    
    /**
     * 對堆的節點的單次變換
     * @param i 第幾個節點
     */
	private void shifHeadDownToUp(int i) {
		if(heap == null || heap.size() <= 0)
			return;
		int len = this.heap.size();
		//索引i需要存在於這個節點中
		if(i >= len)
			return;
		// 首先預存當前進行操作的節點‘
		// 索引和值
		int selectIndex = i - 1;
		int selectValue = heap.get(selectIndex);
		boolean isHeap = false; // 用來判斷當前節點下是否已經沒有其他節點比這個節點小了,作為是否成堆的標識
		while (!isHeap && 2 * (selectIndex + 1) <= len) {
			// 當前節點的最大的孩子節點的位置,開始默認是第一個孩子節點的位置
			int childIndex = 2 * (selectIndex + 1) - 1;
			// 判斷是否存在兩個孩子節點,如果存在,那麽就選出最大的那個
			if (2 * (selectIndex + 1) < len) {
				// 獲取比較小的那個節點作為備選替換節點
				childIndex = heap.get(childIndex) < heap.get(childIndex + 1) ? childIndex : childIndex + 1;
			}
			// 判斷當前節點是不是比下面最小的那個節點還要小
			if (selectValue <= heap.get(childIndex)) {
				// 如果比下面最大的還大,那就表明這個節點為根的子樹已經是一顆樹了
				isHeap = true;
			} else {
				// 如果節點不是小的,那麽更換掉
				heap.set(selectIndex, heap.get(childIndex));
				// 並交換當前遍歷交換的節點
				selectIndex = childIndex;
				// 這個節點和子節點全部遍歷結束之後,交換出最初用來交換的選中節點
				heap.set(selectIndex, selectValue);
			}
		}

	}
	
	//向堆添加元素
	public void add(int element) {
//		int oldLen = heap.size();
		heap.add(element);
		//然後從加入的位置的父節點開始,從下向上所有父節點,全部變換一次
		for(int i = heap.size() / 2; i > 0; i = i / 2) {
			this.shifHeadDownToUp(i);
		}
	}
	
	/**
	 * 移除堆中一個指定元素
	 * @param index
	 * @return
	 */
//	public int remove(int index) {
//		int result = heap.get(index - 1);
//		//思路是吧剩下的最後一個元素作為參照元素,填充進去
//		int lastValue = heap.get(heap.size() - 1);
//		heap.set(index - 1, lastValue);
//		heap.remove(heap.size() - 1);
//		//然後從下向上,吧這個節點對應的位置的數據進行遞歸
//		for(int i = index; i > 0; i = i / 2) {
//			this.shifHeadDownToUp(i);
//		}
//		return result;
//	}
	
	public int remove(Integer object) {
		int index = heap.indexOf(object);
		//思路是吧剩下的最後一個元素作為參照元素,填充進去
		int lastValue = heap.get(heap.size() - 1);
		heap.set(index, lastValue);
		heap.remove(heap.size() - 1);
		//然後從下向上,吧這個節點對應的位置的數據進行遞歸
		for(int i = index + 1; i > 0; i = i / 2) {
			this.shifHeadDownToUp(i);
		}
		return index;
	}
	
	/**
	 * 默認刪除根節點
	 * @return
	 */
	public int remove() {
		int result = heap.get(0);
		//思路是吧剩下的最後一個元素作為參照元素,填充進去
		int lastValue = heap.get(heap.size() - 1);
		heap.set(0, lastValue);
		heap.remove(heap.size() - 1);
		//然後從下向上,吧這個節點對應的位置的數據進行遞歸
		for(int i = 1; i > 0; i = i / 2) {
			this.shifHeadDownToUp(i);
		}
		return result;
	}
    
    @Override
	public String toString() {
	    return heap.toString();
	}
}

  

【算法設計與分析基礎】23、堆排序-2