From 08bfa0669a1767c65380c8682fe6bfdace8cd962 Mon Sep 17 00:00:00 2001 From: nuomi1 Date: Sun, 29 Jan 2023 14:03:30 +0800 Subject: [PATCH] feat: add Swift codes for heap article (#306) * feat: add Swift codes for heap article * Update heap.md --------- Co-authored-by: Yudong Jin --- codes/swift/Package.swift | 2 + codes/swift/chapter_heap/my_heap.swift | 167 +++++++++++++++++++++++++ codes/swift/utils/PrintUtil.swift | 8 ++ docs/chapter_heap/heap.md | 99 ++++++++++++++- 4 files changed, 273 insertions(+), 3 deletions(-) create mode 100644 codes/swift/chapter_heap/my_heap.swift diff --git a/codes/swift/Package.swift b/codes/swift/Package.swift index ad035475..31ef3b33 100644 --- a/codes/swift/Package.swift +++ b/codes/swift/Package.swift @@ -27,6 +27,7 @@ let package = Package( .executable(name: "binary_tree_dfs", targets: ["binary_tree_dfs"]), .executable(name: "binary_search_tree", targets: ["binary_search_tree"]), .executable(name: "avl_tree", targets: ["avl_tree"]), + .executable(name: "my_heap", targets: ["my_heap"]), ], targets: [ .target(name: "utils", path: "utils"), @@ -52,5 +53,6 @@ let package = Package( .executableTarget(name: "binary_tree_dfs", dependencies: ["utils"], path: "chapter_tree", sources: ["binary_tree_dfs.swift"]), .executableTarget(name: "binary_search_tree", dependencies: ["utils"], path: "chapter_tree", sources: ["binary_search_tree.swift"]), .executableTarget(name: "avl_tree", dependencies: ["utils"], path: "chapter_tree", sources: ["avl_tree.swift"]), + .executableTarget(name: "my_heap", dependencies: ["utils"], path: "chapter_heap", sources: ["my_heap.swift"]), ] ) diff --git a/codes/swift/chapter_heap/my_heap.swift b/codes/swift/chapter_heap/my_heap.swift new file mode 100644 index 00000000..de49add4 --- /dev/null +++ b/codes/swift/chapter_heap/my_heap.swift @@ -0,0 +1,167 @@ +/** + * File: my_heap.swift + * Created Time: 2023-01-28 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +class MaxHeap { + private var maxHeap: [Int] + + /* 构造函数,建立空堆 */ + init() { + maxHeap = [] + } + + /* 构造函数,根据输入列表建堆 */ + init(nums: [Int]) { + // 将列表元素原封不动添加进堆 + maxHeap = nums + // 堆化除叶结点以外的其他所有结点 + for i in stride(from: parent(i: size() - 1), through: 0, by: -1) { + siftDown(i: i) + } + } + + /* 获取左子结点索引 */ + private func left(i: Int) -> Int { + 2 * i + 1 + } + + /* 获取右子结点索引 */ + private func right(i: Int) -> Int { + 2 * i + 2 + } + + /* 获取父结点索引 */ + private func parent(i: Int) -> Int { + (i - 1) / 2 // 向下整除 + } + + /* 交换元素 */ + private func swap(i: Int, j: Int) { + maxHeap.swapAt(i, j) + } + + /* 获取堆大小 */ + func size() -> Int { + maxHeap.count + } + + /* 判断堆是否为空 */ + func isEmpty() -> Bool { + size() == 0 + } + + /* 访问堆顶元素 */ + func peek() -> Int { + maxHeap[0] + } + + /* 元素入堆 */ + func push(val: Int) { + // 添加结点 + maxHeap.append(val) + // 从底至顶堆化 + siftUp(i: size() - 1) + } + + /* 从结点 i 开始,从底至顶堆化 */ + private func siftUp(i: Int) { + var i = i + while true { + // 获取结点 i 的父结点 + let p = parent(i: i) + // 当“越过根结点”或“结点无需修复”时,结束堆化 + if p < 0 || maxHeap[i] <= maxHeap[p] { + break + } + // 交换两结点 + swap(i: i, j: p) + // 循环向上堆化 + i = p + } + } + + /* 元素出堆 */ + func poll() -> Int { + // 判空处理 + if isEmpty() { + fatalError("堆为空") + } + // 交换根结点与最右叶结点(即交换首元素与尾元素) + swap(i: 0, j: size() - 1) + // 删除结点 + let val = maxHeap.remove(at: size() - 1) + // 从顶至底堆化 + siftDown(i: 0) + // 返回堆顶元素 + return val + } + + /* 从结点 i 开始,从顶至底堆化 */ + private func siftDown(i: Int) { + var i = i + while true { + // 判断结点 i, l, r 中值最大的结点,记为 ma + let l = left(i: i) + let r = right(i: i) + var ma = i + if l < size(), maxHeap[l] > maxHeap[ma] { + ma = l + } + if r < size(), maxHeap[r] > maxHeap[ma] { + ma = r + } + // 若结点 i 最大或索引 l, r 越界,则无需继续堆化,跳出 + if ma == i { + break + } + // 交换两结点 + swap(i: i, j: ma) + // 循环向下堆化 + i = ma + } + } + + /* 打印堆(二叉树) */ + func print() { + let queue = maxHeap + PrintUtil.printHeap(queue: queue) + } +} + +@main +enum MyHeap { + /* Driver Code */ + static func main() { + /* 初始化大顶堆 */ + let maxHeap = MaxHeap(nums: [9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]) + print("\n输入列表并建堆后") + maxHeap.print() + + /* 获取堆顶元素 */ + var peek = maxHeap.peek() + print("\n堆顶元素为 \(peek)") + + /* 元素入堆 */ + let val = 7 + maxHeap.push(val: val) + print("\n元素 \(val) 入堆后") + maxHeap.print() + + /* 堆顶元素出堆 */ + peek = maxHeap.poll() + print("\n堆顶元素 \(peek) 出堆后") + maxHeap.print() + + /* 获取堆大小 */ + let size = maxHeap.size() + print("\n堆元素数量为 \(size)") + + /* 判断堆是否为空 */ + let isEmpty = maxHeap.isEmpty() + print("\n堆是否为空 \(isEmpty)") + } +} diff --git a/codes/swift/utils/PrintUtil.swift b/codes/swift/utils/PrintUtil.swift index 5dc718df..24b0fe84 100644 --- a/codes/swift/utils/PrintUtil.swift +++ b/codes/swift/utils/PrintUtil.swift @@ -74,4 +74,12 @@ public enum PrintUtil { print("\(key) -> \(value)") } } + + public static func printHeap(queue: [Int]) { + print("堆的数组表示:", terminator: "") + print(queue) + print("堆的树状表示:") + let root = TreeNode.listToTree(list: queue) + printTree(root: root) + } } diff --git a/docs/chapter_heap/heap.md b/docs/chapter_heap/heap.md index e1d1544e..0458448a 100644 --- a/docs/chapter_heap/heap.md +++ b/docs/chapter_heap/heap.md @@ -200,7 +200,7 @@ comments: true === "Swift" ```swift title="heap.swift" - + // Swift 未提供内置 heap 类 ``` ## 堆的实现 @@ -317,7 +317,27 @@ comments: true === "Swift" ```swift title="my_heap.swift" + var maxHeap: [Int] + /* 构造函数,建立空堆 */ + init() { + maxHeap = [] + } + + /* 获取左子结点索引 */ + func left(i: Int) -> Int { + 2 * i + 1 + } + + /* 获取右子结点索引 */ + func right(i: Int) -> Int { + 2 * i + 2 + } + + /* 获取父结点索引 */ + func parent(i: Int) -> Int { + (i - 1) / 2 // 向下整除 + } ``` ### 访问堆顶元素 @@ -381,7 +401,10 @@ comments: true === "Swift" ```swift title="my_heap.swift" - + /* 访问堆顶元素 */ + func peek() -> Int { + maxHeap[0] + } ``` ### 元素入堆 @@ -504,7 +527,30 @@ comments: true === "Swift" ```swift title="my_heap.swift" + /* 元素入堆 */ + func push(val: Int) { + // 添加结点 + maxHeap.append(val) + // 从底至顶堆化 + siftUp(i: size() - 1) + } + /* 从结点 i 开始,从底至顶堆化 */ + func siftUp(i: Int) { + var i = i + while true { + // 获取结点 i 的父结点 + let p = parent(i: i) + // 当“越过根结点”或“结点无需修复”时,结束堆化 + if p < 0 || maxHeap[i] <= maxHeap[p] { + break + } + // 交换两结点 + swap(i: i, j: p) + // 循环向上堆化 + i = p + } + } ``` ### 堆顶元素出堆 @@ -670,7 +716,46 @@ comments: true === "Swift" ```swift title="my_heap.swift" + /* 元素出堆 */ + func poll() -> Int { + // 判空处理 + if isEmpty() { + fatalError("堆为空") + } + // 交换根结点与最右叶结点(即交换首元素与尾元素) + swap(i: 0, j: size() - 1) + // 删除结点 + let val = maxHeap.remove(at: size() - 1) + // 从顶至底堆化 + siftDown(i: 0) + // 返回堆顶元素 + return val + } + /* 从结点 i 开始,从顶至底堆化 */ + func siftDown(i: Int) { + var i = i + while true { + // 判断结点 i, l, r 中值最大的结点,记为 ma + let l = left(i: i) + let r = right(i: i) + var ma = i + if l < size(), maxHeap[l] > maxHeap[ma] { + ma = l + } + if r < size(), maxHeap[r] > maxHeap[ma] { + ma = r + } + // 若结点 i 最大或索引 l, r 越界,则无需继续堆化,跳出 + if ma == i { + break + } + // 交换两结点 + swap(i: i, j: ma) + // 循环向下堆化 + i = ma + } + } ``` ### 输入数据并建堆 * @@ -747,7 +832,15 @@ comments: true === "Swift" ```swift title="my_heap.swift" - + /* 构造函数,根据输入列表建堆 */ + init(nums: [Int]) { + // 将列表元素原封不动添加进堆 + maxHeap = nums + // 堆化除叶结点以外的其他所有结点 + for i in stride(from: parent(i: size() - 1), through: 0, by: -1) { + siftDown(i: i) + } + } ``` 那么,第二种建堆方法的时间复杂度时多少呢?我们来做一下简单推算。