Nil's Blog +

LeetCode 703 数据流中的第K大元素 [Swift]

题目

设计一个找到数据流中第 K 大元素的类(class)。注意是排序后的第 K 大元素,不是第 K 个不同的元素。

你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含数据流中的初始元素。每次调用 KthLargest.add,返回当前数据流中第K大的元素。

示例:

int k = 3;

int[] arr = [4,5,8,2];

KthLargest kthLargest = new KthLargest(3, arr);

kthLargest.add(3);   // returns 4

kthLargest.add(5);   // returns 5

kthLargest.add(10);  // returns 5

说明:

你可以假设 nums 的长度≥ k-1 且k ≥ 1。

思路

本题求解思考过程中,我们很容易想到维护一个 size 为 k 的有序数组,每次向数组中添加元素时,如果该元素比数组中的最小值小,跳过;如果比最小值还大,移除最小值,然后将元素加入有序数组。

我们把这个问题转换成了一个排序问题,最容易想到的插入排序,但时间复杂度为 O(n^2)。

我们仔细阅读本题的要求,本题只是需要求解第 K 大元素,另外 K - 1 个元素的排序其实并无要求,只需要比第 K 个元素大即可,我们想到了数据结构:(这里我们有必要回归一下堆的相关基础知识),维护一个 size 为 K 的小根堆即可。

现在解题的思路我们已经初见雏形,落地到代码上,我们需要借助利用堆的数据结构实现的 ”优先队列“,优先队列的几个关键方法实现如下:

Swift 基础库并没有像 Java,C++ 提供优先队列的数据结构,我们需要基于数组进行封装,具体代码实现如下:

struct Heap <Element> {
    var elements : [Element]
    let priorityFunction: (Element, Element) -> Bool
    
    init(elements : [Element] = [], priorityFunction : @escaping (Element, Element) -> Bool) {
        self.elements = elements
        self.priorityFunction = priorityFunction
        buildHeap()
    }
    
    var isEmpty : Bool {
        return elements.isEmpty
    }
    
    var count : Int {
        return elements.count
    }
    
    mutating func buildHeap() {
        for index in (0 ..< self.elements.count / 2).reversed() {
            siftDown(index)
        }
    }
    
    func peek() -> Element? {
        return elements.first
    }
    
    func isRoot(_ index: Int) -> Bool {
        return (index == 0)
    }
    
    func leftChildIndex(of index: Int) -> Int {
        return (2 * index) + 1
    }
    
    func rightChildIndex(of index: Int) -> Int {
        return (2 * index) + 2
    }
    
    func parentIndex(of index: Int) -> Int {
        return (index - 1) / 2
    }
    
    mutating func equeue(_ element: Element) {
        elements.append(element)
        siftUP(elementAtIndex: elements.count - 1)
    }
    
    mutating func dequeue() -> Element? {
        guard !isEmpty else { return nil }
        
        swapElement(at: 0, with: count - 1)
        let pop = elements.popLast()
            defer {
                if !isEmpty {
                    siftDown(0)
                }
            }
        return pop
    }
}

private extension Heap {
    
    func isHigherPriority(firstIndex: Int, secondIndex: Int) -> Bool{
        return priorityFunction(elements[firstIndex], elements[secondIndex])
    }
    
    func highestPriorityIndex(of parentIndex: Int, and childIndex: Int) -> Int {
        guard childIndex < count && isHigherPriority(firstIndex: childIndex, secondIndex: parentIndex)
            else { return parentIndex }
        return childIndex
    }
    
    func highestPriorityIndex(for parent: Int) -> Int {
        return highestPriorityIndex(of: highestPriorityIndex(of: parent, and: leftChildIndex(of: parent)), and: rightChildIndex(of: parent))
    }
    
    mutating func siftDown(_ elementIndex: Int) {
        let highestIndex = highestPriorityIndex(for : elementIndex)
        
        if highestIndex == elementIndex {
            return
        }
        swapElement(at: elementIndex, with: highestIndex)
        siftDown(highestIndex)
    }
    
    mutating func swapElement(at firstIndex: Int, with secondIndex: Int) {
        guard firstIndex != secondIndex
            else { return }
        elements.swapAt(firstIndex, secondIndex)
    }
    
    mutating func siftUP(elementAtIndex: Int)  {
        let parentIndex = self.parentIndex(of: elementAtIndex)
        guard !isRoot(elementAtIndex), isHigherPriority(firstIndex: elementAtIndex, secondIndex: parentIndex) else {
            return
        }
        swapElement(at: elementAtIndex, with: parentIndex)
        siftUP(elementAtIndex: parentIndex)
    }
}

实现优先队列后,本题可以迎刃而解了,建立堆的时间复杂度为 O(n),每次入堆的时间复杂度为 O(log N)

class KthLargest {
    var heap : Heap<Int>
    let k : Int
    
    init(_ k : Int, _ nums : [Int]) {
        self.heap = Heap<Int>(elements: nums, priorityFunction : <)
        self.k = k
        
        while heap.count > k {
            heap.dequeue()
        }
    }
    
    func add(_ val : Int) -> Int {
        if heap.count == k {
            if val > heap.peek()! {
                heap.dequeue()
                heap.equeue(val)
            }
        } else {
            heap.equeue(val)
        }
        return heap.peek()!
    }
}

技术

算法

项目