优先队列的Java实现(最大二叉堆)
2017-12-08 15:03
886 查看
最大二叉堆的Java实现 :
测试代码 :
程序输出 :
———-测试插入方法开始———-
循环加动态步长的插入算法 : [44, 12, 9, 3, 10, 5]
递归的插入算法 : [44, 12, 9, 3, 10, 5]
———-测试插入方法结束———-
———-测试数据初始化方法开始———-
循环加动态步长方式,先填充数组,然后将 size/2 ~ 1 的顶点元素逐一过滤下沉 : [44, 12, 9, 10, 3, 5]
递归方式,先填充数组,然后将 size/2 ~ 1 的顶点元素逐一过滤下沉 : [44, 12, 9, 3, 10, 5]
先置空数组,然后逐个插入 : [44, 12, 9, 3, 10, 5]
———-测试数据初始化方法结束———-
———-测试提取并删除最大节点方法开始———-
初始堆 : [44, 12, 9, 10, 3, 5]
当前删除的最大值 : 44 , 剩余堆 : [12, 10, 9, 5, 3]
当前删除的最大值 : 12 , 剩余堆 : [10, 5, 9, 3]
当前删除的最大值 : 10 , 剩余堆 : [9, 5, 3]
当前删除的最大值 : 9 , 剩余堆 : [5, 3]
当前删除的最大值 : 5 , 剩余堆 : [3]
当前删除的最大值 : 3 , 剩余堆 : []
———-测试提取并删除最大节点方法结束———-
———-测试调整优先级相关方法开始———-
初始堆 : [44, 12, 9, 10, 3, 5]
将最后元素(5)优先级增加100 : [105, 12, 44, 10, 3, 9]
将第三个元素(44)优先级降低100 : [105, 12, 9, 10, 3, -56]
删除第三个元素(12) : [105, 10, 9, -56, 3]
———-测试调整优先级相关方法结束———-
———-测试Java自带优先队列PriorityQueue开始———-
当前删除的最小值 : 3 , 剩余堆 : [5, 10, 9, 12, 44]
当前删除的最小值 : 5 , 剩余堆 : [9, 10, 44, 12]
当前删除的最小值 : 9 , 剩余堆 : [10, 12, 44]
当前删除的最小值 : 10 , 剩余堆 : [12, 44]
当前删除的最小值 : 12 , 剩余堆 : [44]
当前删除的最小值 : 44 , 剩余堆 : []
———-测试Java自带优先队列PriorityQueue结束———-
GitHub 源码:
https://github.com/wodong/java-learning-stack/tree/master/algorithm/src/main/java/com/guyuesoft/structure/priorityqueue
堆算法的动态可视化展示:
http://zh.visualgo.net/en/heap
/** * @ADT abstract data type 抽象数据类型 * @PriorityQueue 优先队列 * * @KeyMethod : * insert(enqueue) , deleteMax(extractMax,dequeue) * * @最大二叉堆实现核心算法(2-堆) : * 1. 向上过滤 : 用在插入方法。将元素放在最后,通过上浮的方式确定其位置 * 2. 向下过滤 : 用在删除最大值或堆排序中。 * 1)删除最大值后,将最后一个节点置于堆顶,通过和两个子节点的最大值比较,依次下沉,确定其位置 * 2)对于堆排序,从第n/2个节点到根节点,依次向下过滤每个元素,最后得到堆。 * * @TODO 应用扩展 * * 1. d-堆 每个节点有d个孩子 * 2. 最大最小堆 * 3. 两个堆的合并(merge) * 4. 左式堆(leftist heap) * 5. 斜堆(skew heap) * 6. 二项队列(binomial queue) */ public class BinaryHeap<AnyType extends Comparable<? super AnyType>> { private static final int DEFAULT_CAPACITY = 10; private int currentSize; private AnyType[] array; public BinaryHeap() { currentSize = 0; array = (AnyType[]) new Comparable[DEFAULT_CAPACITY]; } public BinaryHeap(int capacity) { currentSize = 0; array = (AnyType[]) new Comparable[capacity]; } /** * 递归方式初始化 * * @param items */ public BinaryHeap(AnyType[] items) { this(items, true); } /** * 采用递归方式或者迭代(循环加动态步长)方式初始化 * * @param items * @param useRecurse */ public BinaryHeap(AnyType[] items, boolean useRecurse) { currentSize = items.length; array = (AnyType[]) new Comparable[items.length + DEFAULT_CAPACITY]; int i = 1; for (AnyType item : items) { array[i++] = item; } if (useRecurse) { for (i = currentSize / 2; i > 0; i--) { percolateDownByRecurse(i); } } else { for (i = currentSize / 2; i > 0; i--) { percolateDown(i); } } } /** * 采用递归方式 * * @param hole */ private void percolateDownByRecurse(int hole) { int childLeft = hole * 2; if (childLeft > currentSize) { return; } // 先和左节点比,再和右节点比,一下实际上是取左节点,右节点和自身节点得最大值放入自身节点,左右节点再往下比 // 更好的做法是现将左节点和右节点比较,然后选取较大的和自身节点比较。这样只递归一个分支就可以了,可以用循环实现,见percolateDown的实现 if (array[hole].compareTo(array[childLeft]) < 0) { AnyType temp = array[hole]; array[hole] = array[childLeft]; array[childLeft] = temp; percolateDownByRecurse(childLeft); } // int childRight = childLeft + 1; if (childRight > currentSize) { return; } if (array[hole].compareTo(array[childRight]) < 0) { AnyType temp = array[hole]; array[hole] = array[childRight]; array[childRight] = temp; percolateDownByRecurse(childRight); } } /** * (最优解) 采用迭代(循环加动态步长)方式(下滤) * * @param hole */ private void percolateDown(int hole) { while (hole*2 <= currentSize) { int child = hole * 2; // 取左右节点的最大节点的索引 if (child != currentSize && array[child + 1].compareTo(array[child]) > 0) { child++; } // 过滤下沉方式确定每个元素位置 if (array[hole].compareTo(array[child]) < 0) { AnyType temp = array[hole]; array[hole] = array[child]; array[child] = temp; hole = child; } else { break; } } } public void insert(AnyType[] items) { for (AnyType item : items) { insert(item); } } /** * (最优解)采用迭代(循环加动态步长)的方式实现插入 * * @param x */ public void insert(AnyType x) { // 自动增长 autoEnlargeArray(); // 将新插入元素放入最后(这里只是拿到新加入的最后一个位置,把这个位置想象成洞,并没有实际放入该元素) int hole = ++currentSize; // 这个循环的逻辑是每次和父节点比较,如果比父节点大,将父节点放入洞中,将洞上浮到父节点位置,然后继续循环,直到根节点退出 // 这个循环的步长为动态的 hole/2 while (hole > 1) { int parent = hole / 2; if (x.compareTo(array[parent]) > 0) { array[hole] = array[parent]; hole = parent; } else { break; } } // 最后将待插入元素插入选好的洞中 array[hole] = x; } /** * 采用递归的形式实现插入 * * @param x */ public void insertByRecurse(AnyType x) { autoEnlargeArray(); array[++currentSize] = x; percolateUp(currentSize); } /** * 将插入元素保持或上浮到合适位置(上滤) * @param index */ private void percolateUp(int index) { int parent = index / 2; if (parent == 0) { return; } if (array[index].compareTo(array[parent]) > 0) { AnyType temp = array[index]; array[index] = array[parent]; array[parent] = temp; percolateUp(parent); } } public AnyType findMax() { return array[1]; } // extract max public AnyType deleteMax() { if(isEmpty()) { return null; } //提出根节点元素 AnyType max= findMax(); //将最后一个元素置于根节点,并将size减一 array[1]=array[currentSize--]; // percolateDown(1); return max; } public boolean isEmpty() { return currentSize==0; } public void makeEmpty() { currentSize=0; array=null; } private void autoEnlargeArray() { if (currentSize >= array.length + 1) { AnyType[] temp = (AnyType[]) new Comparable[array.length * 2 + 1]; for (int i = 1; i < array.length; i++) { temp[i] = array[i]; } array = temp; } } /* * 降低某节点优先级 * 简单设计,仅供参考,当AnyType为Integer时有效 */ public void decreaseKey(int index,int value){ if(index>currentSize) { return ; } if(array[index] instanceof Integer) { Integer current = (Integer) array[index]; Integer now= current-value; array[index]= (AnyType) now; } percolateDown(index); } /* * 增加某节点优先级 * 简单设计,仅供参考,当AnyType为Integer时有效 */ public void increaseKey(int index,int value){ if(index>currentSize) { return ; } if(array[index] instanceof Integer) { Integer current = (Integer) array[index]; Integer now= current+value; array[index]= (AnyType) now; } percolateUp(index); } /** * 删除某节点 * 简单设计,仅供参考,当AnyType为Integer时有效 * @param index */ public void delete(int index) { increaseKey(index,Integer.MAX_VALUE/2); deleteMax(); } @Override public String toString() { String value="["; for(int i=1;i<=currentSize;i++) { if(i!=1) { value+=", "; } value+= array[i]; } value+="]"; return value; } }
测试代码 :
import java.util.List; import java.util.PriorityQueue; public class BinaryHeapTest { public static void main(String[] args) { BinaryHeapTest test = new BinaryHeapTest(); log("----------测试插入方法开始----------"); test.testinsert(); log("----------测试插入方法结束----------"); log("----------测试数据初始化方法开始----------"); test.testInitialize(); log("----------测试数据初始化方法结束----------"); log("----------测试提取并删除最大节点方法开始----------"); test.testDeleteMax(); log("----------测试提取并删除最大节点方法结束----------"); log("----------测试调整优先级相关方法开始----------"); test.testChangeKey(); log("----------测试调整优先级相关方法结束----------"); log("----------测试Java自带优先队列PriorityQueue开始----------"); test.testJavaDefault(); log("----------测试Java自带优先队列PriorityQueue结束----------"); } public void testinsert() { BinaryHeap<Integer> queue1 = new BinaryHeap<>(); BinaryHeap<Integer> queue2 = new BinaryHeap<>(); int[] data = { 10, 3, 5, 12, 44, 9 }; for (int value : data) { queue1.insert(value); queue2.insertByRecurse(value); } log("循环加动态步长的插入算法 : " + queue1); log("递归的插入算法 : " + queue2); } public void testInitialize() { Integer[] data = { 10, 3, 5, 12, 44, 9 }; BinaryHeap<Integer> queue1 = new BinaryHeap<>(data, false); BinaryHeap<Integer> queue2 = new BinaryHeap<>(data, true); BinaryHeap<Integer> queue3 = new BinaryHeap<>(); queue3.insert(data); log("循环加动态步长方式,先填充数组,然后将 size/2 ~ 1 的顶点元素逐一过滤下沉 : " + queue1); log("递归方式,先填充数组,然后将 size/2 ~ 1 的顶点元素逐一过滤下沉 : " + queue2); log("先置空数组,然后逐个插入 : " + queue3); } public void testDeleteMax() { Integer[] data = { 10, 3, 5, 12, 44, 9 }; BinaryHeap<Integer> queue = new BinaryHeap<>(data, false); log("初始堆 : " + queue); while (!queue.isEmpty()) { int item = queue.deleteMax(); log("当前删除的最大值 : " + item + " , 剩余堆 : " + queue); } } public void testJavaDefault() { Integer[] data = { 10, 3, 5, 12, 44, 9 }; //此默认为最小堆 PriorityQueue<Integer> queue= new PriorityQueue(); queue.addAll(List.of(data)); while (!queue.isEmpty()) { int item = queue.poll(); log("当前删除的最小值 : " + item + " , 剩余堆 : " + queue); } } public void testChangeKey() { Integer[] data = { 10, 3, 5, 12, 44, 9 }; BinaryHeap<Integer> queue = new BinaryHeap<>(data, false); log("初始堆 : " + queue); //提高最后一个元素优先级 queue.increaseKey(6, 100); log("将最后元素(5)优先级增加100 : " + queue); queue.decreaseKey(3, 100); log("将第三个元素(44)优先级降低100 : " + queue); queue.delete(2); log("删除第三个元素(12) : " + queue); } public static void log(String message) { System.out.println(message); } }
程序输出 :
———-测试插入方法开始———-
循环加动态步长的插入算法 : [44, 12, 9, 3, 10, 5]
递归的插入算法 : [44, 12, 9, 3, 10, 5]
———-测试插入方法结束———-
———-测试数据初始化方法开始———-
循环加动态步长方式,先填充数组,然后将 size/2 ~ 1 的顶点元素逐一过滤下沉 : [44, 12, 9, 10, 3, 5]
递归方式,先填充数组,然后将 size/2 ~ 1 的顶点元素逐一过滤下沉 : [44, 12, 9, 3, 10, 5]
先置空数组,然后逐个插入 : [44, 12, 9, 3, 10, 5]
———-测试数据初始化方法结束———-
———-测试提取并删除最大节点方法开始———-
初始堆 : [44, 12, 9, 10, 3, 5]
当前删除的最大值 : 44 , 剩余堆 : [12, 10, 9, 5, 3]
当前删除的最大值 : 12 , 剩余堆 : [10, 5, 9, 3]
当前删除的最大值 : 10 , 剩余堆 : [9, 5, 3]
当前删除的最大值 : 9 , 剩余堆 : [5, 3]
当前删除的最大值 : 5 , 剩余堆 : [3]
当前删除的最大值 : 3 , 剩余堆 : []
———-测试提取并删除最大节点方法结束———-
———-测试调整优先级相关方法开始———-
初始堆 : [44, 12, 9, 10, 3, 5]
将最后元素(5)优先级增加100 : [105, 12, 44, 10, 3, 9]
将第三个元素(44)优先级降低100 : [105, 12, 9, 10, 3, -56]
删除第三个元素(12) : [105, 10, 9, -56, 3]
———-测试调整优先级相关方法结束———-
———-测试Java自带优先队列PriorityQueue开始———-
当前删除的最小值 : 3 , 剩余堆 : [5, 10, 9, 12, 44]
当前删除的最小值 : 5 , 剩余堆 : [9, 10, 44, 12]
当前删除的最小值 : 9 , 剩余堆 : [10, 12, 44]
当前删除的最小值 : 10 , 剩余堆 : [12, 44]
当前删除的最小值 : 12 , 剩余堆 : [44]
当前删除的最小值 : 44 , 剩余堆 : []
———-测试Java自带优先队列PriorityQueue结束———-
GitHub 源码:
https://github.com/wodong/java-learning-stack/tree/master/algorithm/src/main/java/com/guyuesoft/structure/priorityqueue
堆算法的动态可视化展示:
http://zh.visualgo.net/en/heap
相关文章推荐
- 数据结构之优先队列--二叉堆(Java实现)
- DataStructure之最大优先队列的java实现
- (数据结构与算法分析 七)------优先队列中的二叉堆的实现( Java语言描述)
- 数据结构之优先队列--二叉堆(Java实现)
- 数据结构之优先队列--二叉堆(Java实现)
- 优先队列之二叉堆(JAVA实现)
- 优先队列-二叉堆Java实现
- 数据结构Java实现07----队列:顺序队列&顺序循环队列、链式队列、顺序优先队列
- 优先队列二叉堆 C语言实现
- 算法学习 - 优先队列的二叉堆实现
- Java实现最大二叉堆中找min<=x<max的x们
- Python实现最大优先队列
- 堆数据结构+堆排序+最大优先队列的堆的实现
- 算法(第四版)学习笔记之java实现基于堆的优先队列
- 用最大堆实现优先队列
- 数据结构_使用二叉堆实现优先队列
- 算法笔记(堆实现的最大优先队列)
- 用java实现二叉查找树、堆和优先队列
- java最小堆实现优先权队列和求最大的n个数问题
- 优先队列的实现--二叉堆