堆的使用及相关LeetCode题目

关于我的 Leetcode 题目解答,代码前往 Github:https://github.com/chenxiangcyr/leetcode-answers


堆可用于求海量数据的前 n 大或者前 n 小,且 n 比较小,能够放入内存。

  • 最大堆:求前 n 小
  • 最小堆:求前 n 大
  • 扩展:双堆:一个最大堆和一个最小堆相结合,用来维护中位数。关于求中位数Median的相关LeetCode题目

堆的构造

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

  • 根节点下标为 0
  • 若某个节点的下标为 i,则:
    • 左孩子的下标为 2 * i
    • 右孩子的下标为 2 * i + 1
    • 父节点的下标为 (i - 1)/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 = 100

  • 首先输入数组 heap[100]
  • 随后开始调整 heap,使其保持最小堆性质
// n / 2 为开始调整的位置,即最后一个双亲节点的位置
for(int i = n / 2; i > 0; i--) {
  shiftDown(i, n);
}

如何插入元素到最小堆:首先加入到该二叉树最后的一个节点,依据最小堆的定义,自底向上,递归调整。

最小堆的节点删除:删除是针对于根节点而言。对于删除操作,将二叉树的最后一个节点替换到根节点,然后自顶向下,递归调整。

使用实例

LeetCode题目:347. Top K Frequent Elements
Given a non-empty array of integers, return the k most frequent elements.
For example,
Given [1,1,1,2,2,3] and k = 2, return [1,2].

class Solution {
    public List topKFrequent(int[] nums, int k) {
        
        // key: int, value: frequency
        Map freqMap = new HashMap();
        // count the frequency
        for(int i : nums) {
            freqMap.put(i, freqMap.getOrDefault(i, 0) + 1);
        }
        
        // use TreeMap to sort
        // key: frequency, value: List of int
        Map> treeMap = new TreeMap>(Collections.reverseOrder());
        for(Map.Entry entry : freqMap.entrySet()) {
            List t = new ArrayList();
            if(treeMap.containsKey(entry.getValue())) {
                t = treeMap.get(entry.getValue());
            }
            
            t.add(entry.getKey());
            
            treeMap.put(entry.getValue(), t);
        }
        
        List result = new ArrayList();
        for(Map.Entry> entry : treeMap.entrySet()) {
            for(Integer i : entry.getValue()) {
                if(k > 0) {
                    result.add(i);
                    k--;
                }
            }
        }
        
        return result;
    }
}

LeetCode题目:692. Top K Frequent Words
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.

class Solution {
    public List topKFrequent(String[] words, int k) {
        // Count the frequence
        // key: String, value: frequency
        Map freqMap = new HashMap();
        // count the frequency
        for(String s : words) {
            freqMap.put(s, freqMap.getOrDefault(s, 0) + 1);
        }
        
        // 最小堆:求前 n 大
        PriorityQueue> minHeap = new PriorityQueue>
            (new Comparator>()
                {
                    public int compare(Map.Entry m1, Map.Entry m2) {
                        if(m1.getValue() == m2.getValue()) {
                            return m2.getKey().compareTo(m1.getKey());
                        }
                        
                        return m1.getValue() - m2.getValue();
                    }
                }
            );
        
        for(Map.Entry entry : freqMap.entrySet()) {
            minHeap.add(entry);
            
            if(minHeap.size() > k) {
                minHeap.poll();
            }
        }
        
        List result = new ArrayList();
        while(!minHeap.isEmpty()) {
            Map.Entry entry = minHeap.poll();
            
            result.add(0, entry.getKey());
        }
        
        
        return result;
    }
}

你可能感兴趣的:(堆的使用及相关LeetCode题目)