前K个高频元素3种解题方法

题目

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:

输入: nums = [1], k = 1
输出: [1]

提示:

你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
你可以按任意顺序返回答案。

方法一 优先队列

Swift代码

final class FixedSizePriorityQueue<Element> {
    
    let capacity: Int
    
    fileprivate(set) var count: Int
    
    var isEmpty: Bool { count == 0 }
    
    private var pq, qp: [Int]
    
    var elements: [Element?]
    
    let comparator: ((Element, Element)-> Bool)
    
    public init(_ capacity: Int, comparator: @escaping ((Element, Element)-> Bool)) {
        self.capacity = capacity
        count = 0
        pq = Array(repeating: 0, count: capacity + 1)
        qp = Array(repeating: -1, count: capacity + 1)
        elements = Array(repeating: nil, count: capacity + 1)
        self.comparator = comparator
    }
    
    public func contains(_ i: Int) -> Bool {
        return qp[i] != -1
    }
    
    public func insert(_ i: Int, _ element: Element) {
        count += 1
        qp[i] = count
        pq[count] = i
        elements[i] = element
        swim(count)
    }
    
    public func topIndex() -> Int {
        return pq[1]
    }
    
    public func topElement() -> Element? {
        return elementOf(topIndex())
    }
    
    public func deleteTop() -> Element? {
        if isEmpty {
            return nil
        }
        let element = topElement()
        let min = pq[1]
        exchange(1, count)
        count -= 1
        sink(1)
        qp[min] = -1
        elements[min] = nil
        pq[count + 1] = -1
        return element
    }
    
    public func elementOf(_ i: Int) -> Element? {
        return elements[i]
    }
    
    public func changeKey(_ i: Int, _ key: Element) {
        elements[i] = key
        swim(qp[i])
        sink(qp[i])
    }
    
    public func compare(_ i: Int, _ j: Int) -> Bool {
        return comparator(elements[pq[i]]!, elements[pq[j]]!)
    }
    
    public func delete(_ i: Int) {
        let index = qp[i]
        exchange(index, count)
        count -= 1
        swim(index)
        sink(index)
        elements[i] = nil
        qp[i] = -1
    }
    
    public func exchange(_ i: Int, _ j: Int) {
        pq.swapAt(i, j)
        qp[pq[i]] = i
        qp[pq[j]] = j
    }
    
    private func swim(_ k: Int) {
        var k = k
        while k > 1 && compare(k >> 1, k) {
            pq.swapAt(k >> 1, k)
            k >>= 1
        }
    }

    private func sink(_ k: Int) {
        var k = k, j = k << 1
        while j <= count {
            if j < count && compare(j, j + 1) { j += 1 }
            if !compare(k, j) { break }
            pq.swapAt(k, j)
            k = j
            j = k << 1
        }
    }
    
}

class Solution {
    func topKFrequent(_ nums: [Int], _ k: Int) -> [Int] {
    var dic = [Int: Int]()
    for item in nums {
        if let value = dic[item] {
            dic[item] = value + 1
        } else {
            dic[item] = 1
        }
    }
    let queue = FixedSizePriorityQueue<(Int, Int)>(dic.count, comparator: { $0.1 < $1.1 })
    for item in dic.enumerated() {
        queue.insert(item.offset, item.element)
    }
    var result = [Int]()
    while result.count < k && queue.count > 0 {
        result.append(queue.deleteTop()!.0)
    }
    
    return result
}
}

方法二 红黑树

Swift代码

class RedBlackTree<Key: Comparable, Value> {
    
    final class Node {
        
        public var item: (Key, Value)
        
        public var left: Node?
        
        public var right: Node?
        
        public var count: Int
        
        public var color: Color
        
        public init(
            key: Key,
            value: Value,
            left: Node? = nil,
            right: Node? = nil,
            count: Int = 1,
            color: Color = .red
        ) {
            self.item = (key, value)
            self.left = left
            self.right = right
            self.count = count
            self.color = color
        }
        
        public func rotateLeft() -> Node? {
            guard let x = right else {
                return nil
            }
            right = x.left
            x.left = self
            x.color = x.left?.color ?? .black
            x.left?.color = .red
            x.count = count
            count = (left?.count ?? 0) + (right?.count ?? 0) + 1
            return x
        }
        
        public func rotateRight() -> Node? {
            guard let x = left else {
                return nil
            }
            left = x.right
            x.right = self
            x.color = x.right?.color ?? .black
            x.right?.color = .red
            x.count = count
            count = (left?.count ?? 0) + (right?.count ?? 0) + 1
            return x
        }
        
        public func flipColors() {
            color.toggle()
            left?.color.toggle()
            right?.color.toggle()
        }
        private var stop = false
        func reverseMiddleOrder(_ block: @escaping ((Key, Value), inout Bool) -> Void) {
            stop = false
            right?.reverseMiddleOrder(block)
            block(item, &stop)
            if !stop {
                left?.reverseMiddleOrder(block)
            }
        }
    }
    
    var root: Node? = nil
    
    var count: Int { size(root) }
    
    subscript(key: Key) -> Value? {
        set {
            put(key, newValue)
        }
        get {
            get(key)
        }
    }
    
    private func isRed(_ node: Node?) -> Bool {
        node?.color == .red
    }
    
    private func size(_ node: Node?) -> Int {
        node?.count ?? 0
    }
    
    public var isEmpty: Bool {
        root == nil
    }
    
    public func get(_ key: Key) -> Value? {
        return get(root, key)
    }
    
    private func get(_ node: Node?, _ key: Key) -> Value? {
        var node = node
        while node != nil {
            if key < node!.item.0 {
                node = node?.left
            } else if key > node!.item.0 {
                node = node?.right
            } else {
                return node?.item.1
            }
        }
        return nil
    }
    
    public func contains(_ key: Key) -> Bool {
        get(key) != nil
    }
    
    public func put(_ key: Key, _ value: Value?) {
        if value == nil {
            return
        }
        root = put(root, key, value!)
        root?.color = .black
    }
    
    private func put(_ node: Node?, _ key: Key, _ value: Value) -> Node? {
        guard node != nil else {
            return Node(key: key, value: value)
        }
        var node = node
        if key < node!.item.0 {
            node?.left = put(node?.left, key, value)
        } else if key > node!.item.0 {
            node?.right = put(node?.right, key, value)
        } else {
            node?.item.1 = value
        }
        
        if isRed(node?.right) && !isRed(node?.left) {
            node = node?.rotateLeft()
        }
        if isRed(node?.left) && isRed(node?.left?.left) {
            node = node?.rotateRight()
        }
        if isRed(node?.left) && isRed(node?.right) {
            node?.flipColors()
        }
        
        node?.count = size(node?.left) + size(node?.right) + 1
        return node
    }
    
    func balance(_ node: Node?) -> Node? {
        var node = node
        if isRed(node?.right) {
            node = node?.rotateLeft()
        }
        if isRed(node?.left) && isRed(node?.left?.left) {
            node = node?.rotateRight()
        }
        if isRed(node?.left) && isRed(node?.right) {
            node?.flipColors()
        }
        node?.count = size(node?.left) + size(node?.right) + 1
        return node
    }
    
    enum Color {
        
        case red
        
        case black
        
        mutating func toggle() {
            switch self {
            case .red:
                self = .black
            case .black:
                self = .red
            }
        }
    }
}

class Solution {
    func topKFrequent(_ nums: [Int], _ k: Int) -> [Int] {
        var counter = Dictionary<Int, Int>()
        for item in nums {
            if let value = counter[item] {
                counter[item] = value + 1
            } else {
                counter[item] = 1
            }
        }
        let tree = RedBlackTree<Int, [Int]>()
        for item in counter {
            var items = tree[item.1]
            if items == nil {
                items = [item.0]
            } else {
                items?.append(item.0)
            }
            tree[item.1] = items
        }
        var items = [Int]()
        tree.root?.reverseMiddleOrder { (item, stop)  in
            if items.count < k {
                for element in item.1 {
                    items.append(element)
                    if items.count == k { break }
                }
            } else {
                stop = true
            }
        }
        return items
    }
}

方法三 排序

排序并选取前k元素。该版本的快速排序比系统排序快一些。

extension Int {
    mutating func increasing() -> Int {
        self += 1
        return self
    }
    mutating func decreasing() -> Int {
        self -= 1
        return self
    }
}
extension Array {
    mutating func quickSort(by comparator: @escaping (Element, Element) -> Bool ) {
        quickSort(low: 0, high: count - 1, by: comparator)
    }
    mutating func quickSort(low: Int, high: Int, by comparator: @escaping (Element, Element) -> Bool ) {
        if high <= low { return }
        let j = partition(low: low, high:high, by: comparator)
        quickSort(low: low, high: j - 1, by: comparator)
        quickSort(low: j + 1, high: high, by: comparator)
    }

    mutating func partition(low: Int, high: Int, by comparator: @escaping (Element, Element) -> Bool) -> Int {
        var i = low
        var j = high + 1
        let v = self[low]
        while true {
            while comparator(self[i.increasing()], v) && i != high {}
            while comparator(v, self[j.decreasing()]) && i != low {}
            if i >= j { break }
            swapAt(i, j)
        }
        swapAt(low, j)
        return j
    }
}

class Solution {
    func topKFrequent(_ nums: [Int], _ k: Int) -> [Int] {
        var items = nums.reduce(into: [Int: Int]()) { (counter, item) in
            counter[item] = (counter[item] ?? 0) + 1
        }.map { $0 }
        items.quickSort {
            $0.1 > $1.1
        }
        return items[0..<k].map { $0.0 }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 226,728评论 6 526
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 97,623评论 3 411
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 174,371评论 0 372
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,174评论 1 306
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,014评论 6 405
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 54,532评论 1 319
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,641评论 3 433
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 41,793评论 0 283
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,294评论 1 329
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,276评论 3 352
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,423评论 1 364
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,980评论 5 354
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,678评论 3 342
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,087评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,307评论 1 279
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,997评论 3 385
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,404评论 2 370

推荐阅读更多精彩内容