每日一省————使用二叉堆实现优先队列
来源:互联网 发布:spring 源码阅读建议 编辑:程序博客网 时间:2024/05/22 11:52
今天,本人主要想复习优先队列这种数据结构。虽然标题号称“每日一省“,但是工作的人身不由己,已经很多天没有更新了。哈哈,废话不多说,直接进入正题吧。
1 优先队列的概念:
优先队列是一种抽象的数据结构,队列中的每个元素都有一个优先级值。优先级值用来表示该元素的出对的先后次序。
优先队列是至少允许插入操作和删除最小(或者最大)元素这两种操作的数据结构。通常,优先队列通过二叉堆来实现。
2 二叉堆的概念:
堆是一棵被完全填满的二叉树,底层例外,底层上的元素从左到右依次填入。如果使用数组表示二叉堆,那么对于数组上的任意位置i上的元素,其左子元素索引是2i,右子元素的索引是(2i +1),其父节点索引是[i/2]。
堆的性质,在堆中,每个元素都要保证大于等于另两个特定位置的元素。所以,在堆有序的二叉树中,每个节点又都小于等于它的父节点。所以,根节点是堆有序的二叉树中的最大节点。
当然,对的性质也可以反过来,也即:每个元素都要保证小于等于另两个特定位置的元素。所以,在堆有序的二叉树中,每个节点又都大于等于它的父节点。所以,根节点是堆有序的二叉树中的最小节点。
3 使用二叉堆实现的优先队列的代码示例
import java.util.Comparator;import java.util.Iterator;import java.util.NoSuchElementException;public class MaxFirstQueue<T> implements Iterable<T> { private T[] queue; // 本列子中优先队列的底层实现是二叉堆,而二叉堆又用数组来表示 private int size = 0; // 表示队列中的元素个数,注意不是表示数组的容量 private Comparator<T> comparator; // 可选的比较器,可以作为该类的构造函数参数传入 public MaxFirstQueue(int max) { queue = (T[]) new Comparable[max + 1]; } public MaxFirstQueue() { this(1); } public MaxFirstQueue(int initCapacity, Comparator<T> comparator) { this.comparator = comparator; queue = (T[]) new Object[initCapacity + 1]; size = 0; } public MaxFirstQueue(Comparator<T> comparator) { this(1, comparator); } public MaxFirstQueue(T[] values) { size = values.length; queue = (T[]) new Object[values.length + 1]; for (int i = 0; i < size; i++) { // 用数组表示二叉堆,数组索引为0的位置不存放元素,该位置元素值永远为空 queue[i + 1] = values[i]; } for (int k = size / 2; k >= 1; k--) { sinkDown(k); } assert isValidHeap(); } public int size() { return size; } public boolean isEmpty() { return size == 0; } /* * 判断数组queue中的所有元素构成的二叉堆是否满足二叉堆的基本性质,也即父节点元素最大, 然后每个子节点比其更下面的子节点大 */ private boolean isValidHeap() { return isValidSubHeap(1); } /* * 判断以queue[k]位置为元素的子堆是否满足二叉堆的基本性质:也即父节点(父节点为queue[k])元素最大, * 然后每个子节点比其更下面的子节点大 */ private boolean isValidSubHeap(int k) { if (k > size) { return true; } int left = 2 * k, right = 2 * k + 1; if (left <= size && less(k, left)) { return false; } if (right <= size && less(k, right)) { return false; } return isValidSubHeap(left) && isValidSubHeap(right); } /** * 取出最大值 */ public T max() { if (isEmpty()) { throw new NoSuchElementException("每次可以取出最大元素的优先队列已经为空,没有任何元素存储其中了"); } return queue[1]; } /** * 插入元素 */ public void add(T value) { if (size >= queue.length - 1) { resize(2 * queue.length); } queue[++size] = value; swimUP(size); assert isValidHeap(); } /** * 删除一个元素后,实际是把删除的元素的放到了索引为size的位置(再把该位置元素值设为null), 该位置已经不能算做是二叉堆的一部分了。 * 二叉堆的大小变为size-- * * 下面的三行代码可以合写为exchange(1, size--), queue[size + 1] = null,这三行代码如下: * exchange(1, size); queue[size] = null; size--; */ public T deleteMax() { if (isEmpty()) { throw new NoSuchElementException("每次可以取出最大元素的优先队列已经为空,没有任何元素存储其中了"); } T value = queue[1]; exchange(1, size); queue[size] = null; size--; sinkDown(1); if ((size > 0) && (size == (queue.length - 1) / 4)) { resize(queue.length / 2); } assert isValidHeap(); return value; } /* * 调整底层数组的大小:具体做法是创建指定大小的临时数组,然后把queue中的元素移动到这个临时数组的相应位置, * 最后再将这个临时数组设置为优先队列的底层实现。 */ private void resize(int capacity) { assert capacity > size; T[] temp = (T[]) new Object[capacity]; for (int i = 1; i <= size; i++) { temp[i] = queue[i]; } queue = temp; } private boolean less(int i, int j) { if (comparator == null) { return ((Comparable<T>) queue[i]).compareTo(queue[j]) < 0; } else { return comparator.compare(queue[i], queue[j]) < 0; } } private void exchange(int i, int j) { T temp = queue[i]; queue[i] = queue[j]; queue[j] = temp; } /* * 将元素值大于父节点的子节点与父节点交换位置,保持堆有序。交换位置后,原来的子节点可能仍然比 更上层的父节点大, * 所以整个过程需要递归进行。这样一来,原来的子节点 有可能升级为层级更高的父节点,类似于一个物体从湖底往上浮直到达到其重力与浮力相平衡的过程。 */ private void swimUP(int k) { while (k > 1 && less(k / 2, k)) { exchange(k, k / 2); k = k / 2; } } /* * 将元素值小于子节点的父节点与子节点交换位置,交换位置后, 原来的父节点仍然有可能比其下面的子节点小, * 所以还需要继续进行类相同的操作,以便保持堆的有序性。所以整个过程递归进行。 * 这类似于一个物体从湖面下沉到距离湖底的某个位置,直到达到其重力与浮力相平衡为止。 */ private void sinkDown(int k) { while (2 * k <= size) { int j = 2 * k; if (j < size && less(j, j + 1)) { j++; } if (!less(k, j)) { break; } exchange(k, j); k = j; } } @Override public Iterator<T> iterator() { return new MaxFirstQueueIterator(); } /** * 用于遍历优先队列中元素的迭代器的具体实现: */ private class MaxFirstQueueIterator implements Iterator<T> { /* * 相当于被迭代的优先队列的一个副本,遍历优先队列中的元素其实遍历的是其副本, * 所以,在多线程环境下,并发的增加、删除、遍历元素可能会导致数据错乱。 */ private MaxFirstQueue<T> copy; public MaxFirstQueueIterator() { if (comparator == null) { copy = new MaxFirstQueue<T>(size()); } else { copy = new MaxFirstQueue<T>(size(), comparator); } for (int i = 1; i <= size; i++) copy.add(queue[i]); } public boolean hasNext() { return !copy.isEmpty(); } public void remove() { throw new UnsupportedOperationException("迭代器不支持移除操作"); } public T next() { if (!hasNext()) { throw new NoSuchElementException(); } return copy.deleteMax(); } } /** * 主方法:包含有测试代码 */ public static void main(String[] args) { MaxFirstQueue<String> pq = new MaxFirstQueue<String>(); pq.add("S"); pq.add("T"); System.out.println("===================================================="); for (String e : pq) { System.out.println(e); } pq.add("C"); pq.add("A"); pq.add("M"); pq.add("B"); System.out.println("===================================================="); for (String e : pq) { System.out.println(e); } System.out.println("===================================================="); Iterator<String> iterator = pq.iterator(); iterator.forEachRemaining(t -> System.out.print("-" + t + "-")); System.out.println(); System.out.println("===================================================="); try { System.out.println(pq.deleteMax()); System.out.println(pq.deleteMax()); System.out.println(pq.deleteMax()); System.out.println(pq.deleteMax()); System.out.println(pq.deleteMax()); System.out.println(pq.deleteMax()); System.out.println(pq.deleteMax()); } catch (Exception e) { System.out.println("队列为空,大小为" + pq.size); } pq.add("C"); pq.add("A"); pq.add("M"); pq.add("B"); System.out.println("队列大小为" + pq.size); }}
0 0
- 每日一省————使用二叉堆实现优先队列
- 优先队列——二叉堆实现
- 优先队列(堆)——二叉堆的实现
- 数据结构与算法——优先队列类的C++实现(二叉堆)
- 二叉堆(binary heap)—— 优先队列的实现
- 看图说话之二叉堆(优先队列)——java实现
- 堆——优先队列
- 优先队列——二叉堆、d堆、左式堆、斜堆
- 优先队列--二叉堆实现
- 算法基础:排序(四)——二叉堆、优先队列、堆排序——Python实现
- 堆与堆排序—优先队列
- 优先队列(二叉堆实现) + 堆排序
- 数据结构_使用二叉堆实现优先队列
- C#实现优先队列 基于二叉堆 附使用案例
- ReviewForJob——二叉堆优先队列的实现(三种堆节点类型——int + struct HeapNode + struct HeapNode*)
- C++实现优先队列——最小堆,d路堆及配对堆
- 结构之美——优先队列基本结构(四)——二叉堆、d堆、左式堆、斜堆
- 结构之美——优先队列基本结构(四)——二叉堆、d堆、左式堆、斜堆
- 创建一个简单的Python服务器
- 好真实的《北京折叠》
- 大整数类biginteger
- **[Lintcode]Coins in a Line II
- hdoj-3501-Calculation 2
- 每日一省————使用二叉堆实现优先队列
- IO进化Netty_HelloWord
- centOS7中关闭firewall,并使用iptables管理防火墙
- JAVA拾遗 - 优先队列的探讨以及其在KNN算法中的应用
- 【冒泡排序】
- 关于Android开发调用系统相机拍照的 一些事
- TCP打洞和UDP打洞的区别
- Android知识要点整理(20)----Gradle 之多模块管理
- Java多线程—龟兔赛跑问题