From 1023845d74a518ec84a22ade5279f53aa0f4f5a7 Mon Sep 17 00:00:00 2001 From: ranwenjie Date: Tue, 9 Nov 2021 14:21:46 +0800 Subject: [PATCH] top-k --- 4 Tree/8-堆/Top-K 问题.md | 20 +++++++++ 4 Tree/8-堆/堆.md | 80 ++++++++++++++++++++++++++++++--- 8 Algorithms Analysis/回溯法.md | 2 +- 3 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 4 Tree/8-堆/Top-K 问题.md diff --git a/4 Tree/8-堆/Top-K 问题.md b/4 Tree/8-堆/Top-K 问题.md new file mode 100644 index 0000000..326ca38 --- /dev/null +++ b/4 Tree/8-堆/Top-K 问题.md @@ -0,0 +1,20 @@ +# Top-K 问题 + + +问题描述:从arr[1, n]这n个数中,找出最大的k个数,这就是经典的TopK问题 + + +思路 + +1. 排序,全局都排序了,这也是这个方法复杂度非常高的原因 ; [排序算法参考这里](../../6%20Sort/REAME.md) +2. 冒泡排序,局部排序徐,只对最大的k个数排序 +3. [堆排序](./堆.md), 只找最大的k个数,这k个数不需要排序; top-k大 问题就是用 小根堆, 小根堆 固定为 k 个元素大小 , 遍历 k-N (N为所有数据的个数),插入小根堆并调整堆,以保证堆内的k个元素,总是当前最大的k个元素。 + + + + + + + + + diff --git a/4 Tree/8-堆/堆.md b/4 Tree/8-堆/堆.md index 73f9773..8877e6f 100644 --- a/4 Tree/8-堆/堆.md +++ b/4 Tree/8-堆/堆.md @@ -1,40 +1,106 @@ # 大顶堆 和 小顶堆 -先来了解下 `堆` 结构; 堆也被称为`优先队列` ; +先来了解下 `堆` 结构; 堆也被称为`优先队列`,`二叉堆` ; 特点 -* 堆总是一颗完全二叉树树, 使用数组作为其存储结构; +* 堆总是一颗完全二叉树树, 使用数组作为其存储结构,因此也叫 `二叉堆`; 用链表存的就叫二叉树了 * 任一节点小于(或大于)其所有的孩子节点; -堆分小根堆和大根堆; 如果是大于所有孩子节点,这就是一颗大根堆,也就是根节点是堆上的最大值;如果节点小于所有的子节点,这就是一颗小跟堆,也即是根节点是堆上所有节点的最小值。 +堆分小根堆和大根堆; 如果根节点大于所有孩子节点,这就是一颗大根堆,也就是根节点是堆上的最大值;如果节点小于所有的子节点,这就是一颗小跟堆,也即是根节点是堆上所有节点的最小值。 +`堆用数组来存储`,因为是一颗完全二叉树,i节点的父节点索引就是(i-1)/2, 左右子节点小标是 2i+1,2i+2。 + +比如大根堆存储示例 + +``` +10, 7, 6 , 3, 4, 1, 5 +``` ### 应用场景 -* 优先队列 如iOS中的NSOperationQueue 就是维护一个优先队列 +* 优先队列 如iOS中的 NSOperationQueue 就是维护一个优先队列 * 堆排序 -* top-K 大(小) +* top-K 大(小) , top-k大 问题就是用 小根堆, 小根堆 固定为 k 个元素大小 , 遍历 k-N (N为所有数据的个数),插入小根堆并调整堆,以保证堆内的k个元素,总是当前最大的k个元素。 # 堆存储结构 -`堆用数组来存储`,因为是一颗完全二叉树,i节点的父节点索引就是(i-1)/2, 左右子节点小标是 2i+1,2i+2。 - 堆的操作有: * 建堆 * 插入:都是插入到数组最后,然后再调整满足堆次序 * 删除:删除总是发生在 A[0]处,也就是只删除根节点 + 这样难怪`堆`被称为 `优先队列`。 插入和删除分别在 数组尾部和头部,只是需要再次调整以满足堆次序。 +优先级队列的代码实现 + + +```java +public class MaxPQ + > { + // 存储元素的数组 + private Key[] pq; + // 当前 Priority Queue 中的元素个数 + private int N = 0; + + public MaxPQ(int cap) { + // 索引 0 不用,所以多分配一个空间 + pq = (Key[]) new Comparable[cap + 1]; + } + + /* 返回当前队列中最大元素 */ + public Key max() { + return pq[1]; + } + + /* 插入元素 e */ + public void insert(Key e) {...} + + /* 删除并返回当前队列中最大元素 */ + public Key delMax() {...} + + /* 上浮第 k 个元素,以维护最大堆性质 */ + private void swim(int k) {...} + + /* 下沉第 k 个元素,以维护最大堆性质 */ + private void sink(int k) {...} + + /* 交换数组的两个元素 */ + private void exch(int i, int j) { + Key temp = pq[i]; + pq[i] = pq[j]; + pq[j] = temp; + } + + /* pq[i] 是否比 pq[j] 小? */ + private boolean less(int i, int j) { + return pq[i].compareTo(pq[j]) < 0; + } + + /* 还有 left, right, parent 三个方法 */ +} + +``` + + + + + + + + + + + diff --git a/8 Algorithms Analysis/回溯法.md b/8 Algorithms Analysis/回溯法.md index f4a12fd..9275b8a 100644 --- a/8 Algorithms Analysis/回溯法.md +++ b/8 Algorithms Analysis/回溯法.md @@ -23,7 +23,7 @@ ## 代码框架 -``` +```python result = [] def backtrack(路径, 选择列表): if 满足结束条件: