堆, 堆树(最小堆/最大堆) - heap, treap

概念

  1. 堆是一种逻辑结构,树是一种存储结构,两者是不同层面的东西,就像“中国人"和“成年人”一样不矛盾。

heap和tree结合,得了treap堆树。

  1. 堆树和二叉排序树
    以小根堆为例,堆的特点是双亲结点的关键字必然小于等于孩子结点的关键字,而两个孩子结点的关键字没有次序规定
    而二叉排序树中,每个双亲结点的关键字均大于左子树结点的关键字,均小于右子树j结点的关键字,也就是说,每个双亲结点的左右孩子的关键字有次序关系
    这样,当对两种树执行中序遍历后,二叉排序树会得到一个有序的序列,而堆不一定。

堆树的构造

堆其实是一个完全二叉树,可以用数组来表示。

  • 根节点下标为1, 0为temp空间
  • 若某个节点的下标为 i,则:
    • 左孩子的下标为 2 * i
    • 右孩子的下标为 2 * i + 1
    • 父节点的下标为 i/2

如何保持最小堆性质:往下调整 shiftDown,将较大的数往下移动,示例如下:
如何保持最大堆性质:往下调整 shiftUp,将较大的数往上移动

// 将 start 号节点向下调整直到 end
void ShiftDown(int start, int end) {
  heap[0] = heap[start];
  
  int i = start;
  int j = 2 * start; // 左孩子

  while(j <= end) {
    // 如果有右孩子且右孩子比左孩子小,将 j 保存至右孩子
    if(j < end && heap[j] > heap[j + 1]) {
      j++;
    }
    
    // 如果 start 号节点比孩子小,则无需调整
    if(heap[0] <= heap[j]) {
      break;
    }
    // 往下调整,将较大的数往下移动
    else {
      heap[i] = heap[j];
      i = j;
      j = 2 * j;
    }
  }
  
  heap[i] = heap[0]
}

// n / 2 为开始调整的位置,即最后一个双亲节点的位置
for(int i = n / 2; i > 0; i--) {
  shiftDown(i, n);
}

实践应用

347. Top K Frequent Elements Medium
Given a non-empty array of integers, return the k most frequent elements.

Example 1:
Input: nums = [1,1,1,2,2,3], k = 2
Output: [1,2]

Example 2:
Input: nums = [1], k = 1
Output: [1]
Note:

You may assume k is always valid, 1 ≤ k ≤ number of unique elements.
Your algorithm's time complexity must be better than O(n log n), where n is the array's size.

class Solution {
  public List topKFrequent(int[] nums, int k) {
    // build hash map : character and how often it appears
    HashMap count = new HashMap();
    for (int n: nums) {
      count.put(n, count.getOrDefault(n, 0) + 1);
    }

    // init heap 'the less frequent element first'
    PriorityQueue heap =
            new PriorityQueue((n1, n2) -> count.get(n1) - count.get(n2));

    // keep k top frequent elements in the heap
    for (int n: count.keySet()) {
      heap.add(n);
      if (heap.size() > k)
        heap.poll();
    }

    // build output list
    List top_k = new LinkedList();
    while (!heap.isEmpty())
      top_k.add(heap.poll());
    Collections.reverse(top_k);
    return top_k;
  }
}
  1. 692. Top K Frequent Words Medium
    Given a non-empty list of words, return the k most frequent elements.

Your answer should be sorted by frequency from highest to lowest. If two words have the same frequency, then the word with the lower alphabetical order comes first.

Example 1.

Input: ["i", "love", "leetcode", "i", "love", "coding"], k = 2
Output: ["i", "love"]
Explanation: "i" and "love" are the two most frequent words.
    Note that "i" comes before "love" due to a lower alphabetical order.
class Solution {
    public List topKFrequent(String[] words, int k) {
        Map count = new HashMap();
        for (String word: words) {
            count.put(word, count.getOrDefault(word, 0) + 1);
        }
        List candidates = new ArrayList(count.keySet());
        Collections.sort(candidates, (w1, w2) -> count.get(w1).equals(count.get(w2)) ?
                w1.compareTo(w2) : count.get(w2) - count.get(w1));

        return candidates.subList(0, k);

Ref:

  1. 数学lover知乎回答
  2. 专职跑龙套
  3. leetcode原题

你可能感兴趣的:(堆, 堆树(最小堆/最大堆) - heap, treap)