在算法系列(九)平衡二叉查找树AVL树中介绍了AVL树。这篇文章主要讲解优先队列。
一般都使用二叉堆来实现优先队列。暂时之说这种实现。
堆的定义和性质
堆实际上是一棵完全二叉树,其任何一非叶节点满足性质:
Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]或者Key[i]>=Key[2i+1]&&key>=key[2i+2]
即任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。
堆分为大顶堆和小顶堆,满足Key[i]>=Key[2i+1]&&key>=key[2i+2]称为大顶堆,
满足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]称为小顶堆。
由上述性质可知大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的。
堆的存储
一般都用数组来表示堆,i结点的父结点下标就为(i – 1) / 2。它的左右子结点下标分别为2 * i + 1和2 * i + 2。如第0个结点左右子结点下标分别为1和2。
堆的基本操作--插入删除
插入的策略叫做上滤,删除的策略叫做下滤。
堆排序
首先可以看到堆建好之后堆中第0个数据是堆中最小的数据。取出这个数据再执行下堆的删除操作。这样堆中第0个数据又是堆中最小的数据,重复上述步骤直至堆中只有一个数据时就直接取出这个数据。
代码实现
// 向堆中插入元素 public static void insert(List heap, int value) { heap.add(value); // 开始上升操作 heapUp2(heap, heap.size() - 1); // heapUp(heap, heap.size() - 1); }
// 非递归实现 public static void heapUp2(List heap, int index) { int parent = 0; for (; index > 1; index /= 2) { // 获取index的父节点的下标 parent = index / 2; // 获得父节点的值 int parentValue = (Integer) heap.get(parent); // 获得index位置的值 int indexValue = (Integer) heap.get(index); // 如果大于就交换 if (parentValue > indexValue) { swap(heap, parent, index); } } }
/** * 删除堆中最小的值,也就是删除位置是1的值,也就是根节点的值 操作原理是:当删除根节点的数值时,原来的位置就会出现一个孔 * 填充这个孔的方法就是,把最后的叶子的值赋给该孔,最后把该叶子删除 * * @param heap */ public static void deleteMin(List heap) { // 把最后的一个叶子的数值赋值给1个位置 heap.set(1, heap.get(heap.size() - 1)); // 下滤操作 heapDown2(heap, 1); // heapDown(heap, 1); // 把最后一个位置的数字删除 heap.remove(heap.size() - 1); }
// 非递归实现 public static void heapDown2(List heap, int index) { int child = 0;// 存储左儿子的位置 int temp = (Integer) heap.get(index); int n = heap.size() - 2; // 如果有儿子的话 for (; 2 * index <= n; index = child) { // 获取左儿子的位置 child = 2 * index; // 如果只有左儿子 if (child == n) { child = 2 * index; } // 如果右儿子比左儿子的数值小 else if ((Integer) heap.get(child) > (Integer) heap.get(child + 1)) { child++; } // 如果数值最小的儿子比temp的值小 if ((Integer) heap.get(child) < temp) { // 交换堆中的child,和index位置的值 swap(heap, child, index); } else { break; } } }
算法实现代码github地址为https://github.com/robertjc/simplealgorithm
后续会不断补充,有些地方写的可能有问题,请多指教。
欢迎扫描二维码,关注我的公众账号
时间: 2024-10-17 20:44:57