堆以及堆排序
一、堆(大根堆)
堆可以定义为一棵具有父母优势的完全二叉树。
堆也继承了一些完全二叉树的性质。
父母优势,即每一个结点的键值总是要大于其子结点的。
于是我们可以认为,从任意一个结点到某个叶子(当然这个叶子是该结点的子孙)的路径上,键值总是递减,或者非增的(当允许子结点键值与父节点相等时)。
堆的特性:
1、n个节点的完全二叉树,其高度为floor(log2(n))
2、堆的根总是最大元素
3、其子树也是一个堆
4、用数组实现堆时,可预留数组头的元素,使得n个节点分别占据1-n的位置,这样以来,数组应该被初始化为n+1的长度,该数组应该有如下的性质:
扫描二维码关注公众号,回复:
155111 查看本文章
4.1、父母节点总是占据了前floor(n/2)个位置,叶子节点将占据后ceil(n/2)个位置,因为继承了完全二叉树的性质,所以该条规则对于最后一层右侧不满的堆也是成立的。
4.2、如果一个父节点的位置为i (1≤i≤floor(n/2)),则它的子节点(如果存在的话)位置应为2i和2i+1 ; 与之对应的,子节点的位置为i时,其父节点的位置应为floor(i/2)
一个数组调整为一个堆,4.1和4.2两条规则对调整堆的算法起到了重要作用。
堆的Java实现,包含创建堆、堆的任意索引位置删除、堆的插入:
package com.ryo.algorithm.heap; import com.ryo.algorithm.util.Util; /** * 采用插入排序的思想调整堆 * @author shiin */ public class InsertHeap implements Heap{ private int[] heap; private int size; public InsertHeap(int[] arr) { if(arr != null) { heap = new int[arr.length+1]; size = arr.length; if(size > 1) { for(int i=0 ;i<size ;i++) { heap[i+1] = arr[i]; } buildHeap(); } } } @Override public void buildHeap() { int floor = (heap.length-1)/2;//下界 最后一个父母节点的索引 int i = floor; int j ,temp ,nextindex; while(i > 0) { temp = heap[i]; j = i; while(j <= floor) { nextindex = getBigerIndex(j); if(temp < heap[nextindex]) { heap[j] = heap[nextindex]; } else break; j = nextindex; } heap[j] = temp; i--; } } /** * 插入一个int并放到合适的位置 * @param newint 新元素 */ @Override public void insert(int newint) { int[] temp = new int[heap.length+1]; for(int i=0 ;i<temp.length-1 ;i++) { temp[i] = heap[i]; } int index = heap.length; while(index > 1) { if(newint > temp[index/2]) { temp[index] = temp[index/2]; index = index/2; } else break; } temp[index] = newint; heap = temp; } /** * 删除指定索引的元素 * 基本思想是用末尾的元素取替代删除位置的元素再调整堆 * @param index 目标索引,从1开始为根 */ @Override public void delete(int index) { heap[index] = heap[heap.length-1]; int[] temp = new int[heap.length-1]; for(int i=0 ;i<temp.length ;i++) { temp[i] = heap[i]; } heap = temp; buildHeap(); } /** * 比较两个子节点的大小,返回较大节点的索引 * @param root 根节点位置 * @return 较大子节点的索引 */ private int getBigerIndex(int root) { if(2*root + 1 > size) { return 2*root; } else { if(heap[2*root] > heap[2*root+1]) return 2*root; else return 2*root+1; } } @Override public int[] getHeap() { return this.heap; } @Override public int size() { return this.size; } }
堆排序Java实现:
package com.ryo.algorithm.sort; import com.ryo.alogorithm.heap.InsertHeap; public class HeapSort implements Sort{ @Override public int[] sort(int[] arr) { int[] heap = new InsertHeap(arr).getHeap(); return heapSort(heap); } private int[] heapSort(int[] heap) { int cursor = heap.length-1; int[] result = new int[heap.length-1]; int i = result.length-1; int nextindex,temp,j; while(cursor > 0) { result[i] = heap[1]; j = 1; temp = heap[cursor]; while(j*2 < cursor) { nextindex = getBigerChildIndex(heap ,j); if(!compareAndSet(heap ,temp ,nextindex)) break; j = nextindex; } heap[j] = temp; i--; cursor--; } return result; } /** * 返回较大的子节点索引 * @param heap 目标数组 * @param index 父母节点索引 * @return 较大的子节点索引 */ public int getBigerChildIndex(int[] heap ,int index) { if(heap[2*index] > heap[2*index+1]) { return 2*index; } else return 2*index+1; } /** * 基于一种插入排序的思想 * 若child大于value,只设置该child的父母节点的值为该child的值,不对child做改变 * @param heap 目标数组 * @param value 待比较的值 * @param child 较大的子节点索引,该值应由getBigerChildIndex方法传入 * @return 是否需要继续向下比较 */ public boolean compareAndSet(int[] heap ,int value ,int child) { if(value < heap[child]) { heap[child/2] = heap[child]; return true; } else return false; } }