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)
}
}
}