diff --git a/codes/kotlin/chapter_heap/heap.kt b/codes/kotlin/chapter_heap/heap.kt new file mode 100644 index 000000000..08eef31be --- /dev/null +++ b/codes/kotlin/chapter_heap/heap.kt @@ -0,0 +1,66 @@ +/** + * File: heap.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_heap + +import utils.printHeap +import java.util.* + +fun testPush(heap: Queue, value: Int) { + heap.offer(value) // 元素入堆 + print("\n元素 $value 入堆后\n") + printHeap(heap) +} + +fun testPop(heap: Queue) { + val value = heap.poll() // 堆顶元素出堆 + print("\n堆顶元素 $value 出堆后\n") + printHeap(heap) +} + +/* Driver Code */ +fun main() { + /* 初始化堆 */ + // 初始化小顶堆 + val minHeap: PriorityQueue + + // 初始化大顶堆(使用 lambda 表达式修改 Comparator 即可) + val maxHeap = PriorityQueue { a: Int, b: Int -> b - a } + + println("\n以下测试样例为大顶堆") + + /* 元素入堆 */ + testPush(maxHeap, 1) + testPush(maxHeap, 3) + testPush(maxHeap, 2) + testPush(maxHeap, 5) + testPush(maxHeap, 4) + + /* 获取堆顶元素 */ + val peek = maxHeap.peek() + print("\n堆顶元素为 $peek\n") + + /* 堆顶元素出堆 */ + testPop(maxHeap) + testPop(maxHeap) + testPop(maxHeap) + testPop(maxHeap) + testPop(maxHeap) + + /* 获取堆大小 */ + val size = maxHeap.size + print("\n堆元素数量为 $size\n") + + /* 判断堆是否为空 */ + val isEmpty = maxHeap.isEmpty() + print("\n堆是否为空 $isEmpty\n") + + /* 输入列表并建堆 */ + // 时间复杂度为 O(n) ,而非 O(nlogn) + minHeap = PriorityQueue(mutableListOf(1, 3, 2, 5, 4)) + println("\n输入列表并建立小顶堆后") + printHeap(minHeap) +} \ No newline at end of file diff --git a/codes/kotlin/chapter_heap/my_heap.kt b/codes/kotlin/chapter_heap/my_heap.kt new file mode 100644 index 000000000..9d0ad0c73 --- /dev/null +++ b/codes/kotlin/chapter_heap/my_heap.kt @@ -0,0 +1,157 @@ +/** + * File: my_heap.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_heap + +import utils.printHeap +import java.util.* + +/* 大顶堆 */ +class MaxHeap(nums: List?) { + // 使用列表而非数组,这样无须考虑扩容问题 + // 将列表元素原封不动添加进堆 + private val maxHeap = ArrayList(nums!!) + + /* 构造函数,根据输入列表建堆 */ + init { + // 堆化除叶节点以外的其他所有节点 + for (i in parent(size() - 1) downTo 0) { + siftDown(i) + } + } + + /* 获取左子节点的索引 */ + private fun left(i: Int): Int { + return 2 * i + 1 + } + + /* 获取右子节点的索引 */ + private fun right(i: Int): Int { + return 2 * i + 2 + } + + /* 获取父节点的索引 */ + private fun parent(i: Int): Int { + return (i - 1) / 2 // 向下整除 + } + + /* 交换元素 */ + private fun swap(i: Int, j: Int) { + maxHeap[i] = maxHeap[j].also { maxHeap[j] = maxHeap[i] } + } + + /* 获取堆大小 */ + fun size(): Int { + return maxHeap.size + } + + /* 判断堆是否为空 */ + fun isEmpty(): Boolean { + /* 判断堆是否为空 */ + return size() == 0 + } + + /* 访问堆顶元素 */ + fun peek(): Int { + return maxHeap[0] + } + + /* 元素入堆 */ + fun push(value: Int) { + // 添加节点 + maxHeap.add(value) + // 从底至顶堆化 + siftUp(size() - 1) + } + + /* 从节点 i 开始,从底至顶堆化 */ + private fun siftUp(it: Int) { + // Kotlin的函数参数不可变,因此创建临时变量 + var i = it + while (true) { + // 获取节点 i 的父节点 + val p = parent(i) + // 当“越过根节点”或“节点无须修复”时,结束堆化 + if (p < 0 || maxHeap[i] <= maxHeap[p]) break + // 交换两节点 + swap(i, p) + // 循环向上堆化 + i = p + } + } + + /* 元素出堆 */ + fun pop(): Int { + // 判空处理 + if (isEmpty()) throw IndexOutOfBoundsException() + // 交换根节点与最右叶节点(交换首元素与尾元素) + swap(0, size() - 1) + // 删除节点 + val value = maxHeap.removeAt(size() - 1) + // 从顶至底堆化 + siftDown(0) + // 返回堆顶元素 + return value + } + + /* 从节点 i 开始,从顶至底堆化 */ + private fun siftDown(it: Int) { + // Kotlin的函数参数不可变,因此创建临时变量 + var i = it + while (true) { + // 判断节点 i, l, r 中值最大的节点,记为 ma + val l = left(i) + val r = right(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, ma) + // 循环向下堆化 + i = ma + } + } + + /* 打印堆(二叉树) */ + fun print() { + val queue = PriorityQueue { a: Int, b: Int -> b - a } + queue.addAll(maxHeap) + printHeap(queue) + } +} + +/* Driver Code */ +fun main() { + /* 初始化大顶堆 */ + val maxHeap = MaxHeap(mutableListOf(9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2)) + println("\n输入列表并建堆后") + maxHeap.print() + + /* 获取堆顶元素 */ + var peek = maxHeap.peek() + print("\n堆顶元素为 $peek\n") + + /* 元素入堆 */ + val value = 7 + maxHeap.push(value) + print("\n元素 $value 入堆后\n") + maxHeap.print() + + /* 堆顶元素出堆 */ + peek = maxHeap.pop() + print("\n堆顶元素 $peek 出堆后\n") + maxHeap.print() + + /* 获取堆大小 */ + val size = maxHeap.size() + print("\n堆元素数量为 $size\n") + + /* 判断堆是否为空 */ + val isEmpty = maxHeap.isEmpty() + print("\n堆是否为空 $isEmpty\n") +} diff --git a/codes/kotlin/chapter_heap/top_k.kt b/codes/kotlin/chapter_heap/top_k.kt new file mode 100644 index 000000000..208ede653 --- /dev/null +++ b/codes/kotlin/chapter_heap/top_k.kt @@ -0,0 +1,38 @@ +/** + * File: top_k.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_heap + +import utils.printHeap +import java.util.* + +/* 基于堆查找数组中最大的 k 个元素 */ +fun topKHeap(nums: IntArray, k: Int): Queue { + // 初始化小顶堆 + val heap = PriorityQueue() + // 将数组的前 k 个元素入堆 + for (i in 0.. heap.peek()) { + heap.poll() + heap.offer(nums[i]) + } + } + return heap +} + +/* Driver Code */ +fun main() { + val nums = intArrayOf(1, 7, 6, 3, 2) + val k = 3 + val res = topKHeap(nums, k) + println("最大的 $k 个元素为") + printHeap(res) +} \ No newline at end of file