1.数据结构堆

1.Swift中没有像Java中的堆数据结构

在这里插入图片描述

2. 思路

这就是用典型的用小顶堆思路来解决

class Solution {
    func topKFrequent(_ nums: [Int], _ k: Int) -> [Int] {
        let minQ = PriorityQueue<(Int, Int)>.init { arr1, arr2 in
            return arr1.1  < arr2.1
        }
        
        let dict = nums.reduce(into: [:]) { partialResult, curVal in
            partialResult[curVal, default: 0] += 1
        }
        
        for key in dict.keys {
            let count = dict[key]!
            if minQ.count < k {
                minQ.push((key, count))
            } else {
                if minQ.peek()!.1 <= count {
                    minQ.poll()
                    minQ.push((key, count))
                }
            }
        }
        
        var arr = [Int]()
        for (i,_) in minQ.elements {
            arr.append(i)
        }
        
        return arr
    }
    
}
typealias PriorityBlock<T> = (T, T) -> Bool

class PriorityQueue<T> {
    
    var elements = [T]()
    var count: Int {
        return elements.count
    }
    
    var compartor: PriorityBlock<T>
    init(_ block: @escaping PriorityBlock<T>) {
        compartor = block
        elements = [T]()
    }
    
    public func push(_ val: T) {
        elements.append(val)
        siftUp(count - 1)
    }
    
    // 看一眼堆顶元素是什么
    public func peek() -> T? {
        if count == 0 {
            return nil
        }
        
        return elements.first!
    }
    
    // 拿出堆顶元素
    public func poll() -> T? {
        if count == 0 {
            return nil
        }
        elements.swapAt(0, count - 1)
        let ele = elements.removeLast()
        siftDown(0)
        
        return ele
    }
    
    // 上滤
    private func siftUp(_ index: Int) {
        guard index > 0 else { return }
        let parent = (index - 1) / 2
        if compartor(elements[index], elements[parent]) {
            elements.swapAt(index, parent)
            siftUp(parent)
        }
    }
    
    // 下滤
    private func siftDown(_ index: Int) {
        var parent = index
        let left = index * 2 + 1
        let right = index * 2 + 2
        
        if left < count && compartor(elements[left], elements[parent]) {
            parent = left
        }
        
        if right < count && compartor(elements[right], elements[parent]) {
            parent = right
        }
        
        if parent != index {
            elements.swapAt(index, parent)
            siftDown(parent)
        }
    }
    
}

Swfit封装的堆详解:

typealias PriorityBlock<T> = (T, T) -> Bool

class PriorityQueue<T> {
    
    var elements = [T]()
    var count: Int {
        return elements.count
    }
    
    var compartor: PriorityBlock<T>
    init(_ block: @escaping PriorityBlock<T>) {
        compartor = block
        elements = [T]()
    }
    
    public func push(_ val: T) {
        elements.append(val)
        siftUp(count - 1)
    }
    
    // 看一眼堆顶元素是什么
    public func peek() -> T? {
        if count == 0 {
            return nil
        }
        
        return elements.first!
    }
    
    // 拿出堆顶元素
    public func poll() -> T? {
        if count == 0 {
            return nil
        }
        elements.swapAt(0, count - 1)
        let ele = elements.removeLast() // 先把最后一个移除掉
        siftDown(0)                     // 再进行下滤
        
        return ele
    }
    
    // 上滤
    private func siftUp(_ index: Int) {
        guard index > 0 else { return }
        let parent = (index - 1) / 2                        // 小顶堆,小的值要在上面
        if compartor(elements[index], elements[parent]) { // 当前位置和父节点位置比较,如果比父节点位置的值小,就交换
            elements.swapAt(index, parent)
            siftUp(parent)
        }
    }
    /**
     
     下滤过程演示:
     /------------------------------------第一步状态-------------------------------------------------/
                         1
                     3               4
                   6        7      8        9
               一开始:arr = [1, 3, 4, 6, 7, 8 ,9 ]
               交换首位: arr = [9, 3, 4, 6, 7, 8, 1]
               移除最后一个: arr = [9, 3, 4, 6, 7, 8]
     /------------------------------------第二步状态-------------------------------------------------/
       9
   3               4
6        7        8
     
     parent = 0
     left = 1
     right = 2
        step1: arr[left]和arr[parent]的位置相比较,发现9比3大,父节点位置要下沉到3的位置   --> parent = 1
        step2: arr[right]和arr[parent] (此刻实际上是比较arr[2] 和 arr[1],因为parent已经变为1了)比较,发现4比3大, --> parent = 2
        step3: 最终决定让 9 和 4 交换位置
     /------------------------------------第三步状态-------------------------------------------------/
         4
    3                      9
6           7            8
     
    
      ... 同理 .. 9 和 8 交换位置
     /------------------------------------第四步状态-------------------------------------------------/
      4
  3                 8
6        7        9
         ... 完成  9 是最后一个位置,也是最大的那个了
     
     /------------------------------------分割线------------------------------------------------/
    #疑问:
     为什么要先交换首尾,再移除最后一个元素,然后再下滤操作?
     我直接移除第一个元素,再对其他元素进行上滤操作不行吗?
     #解答:
     下滤操作,如果有n层,理论上最上面的那个节点要进行 n - 1 次下滤操作(数量只有一个)
      下滤操作,最下面一层节点要进行 n - 1 次上滤操作,但是最下面一层节点(数量大约有总数的一半)
     所以下滤操作,理论上性能会更好
        
     */
    // 下滤
    private func siftDown(_ index: Int) {
        var parent = index
        let left = index * 2 + 1
        let right = index * 2 + 2
        
        if left < count && compartor(elements[left], elements[parent]) { // 如果左边的比父节点值小,就交换,父节点就准备下沉到左边去
            parent = left
        }
        
        if right < count && compartor(elements[right], elements[parent]) { // 如果右边比左边的值更小,就交换,让父节点沉到右边去
            parent = right
        }
        
        if parent != index {
            elements.swapAt(index, parent)
            siftDown(parent)
        }
    }
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值