返回
40 道最小的 k 个数:剖析四种解法,助力算法新突破
IOS
2023-09-18 23:50:20
- 暴力法:简单直接,但效率低下
func findKthSmallest(_ nums: [Int], _ k: Int) -> [Int] {
// 对数组进行排序
let sortedNums = nums.sorted()
// 获取前 k 个元素
return Array(sortedNums[0..<k])
}
暴力法是最直接的解法,它通过对数组进行排序,然后取前 k 个元素即可。这种方法实现简单,但由于需要对整个数组进行排序,时间复杂度为 O(n log n),其中 n 为数组的长度。
2. 快速排序:效率较高,但存在最坏情况
func findKthSmallest(_ nums: [Int], _ k: Int) -> [Int] {
// 数组拷贝
var numsCopy = nums
// 快速排序
quickSort(&numsCopy, 0, numsCopy.count - 1)
// 获取前 k 个元素
return Array(numsCopy[0..<k])
}
func quickSort(_ nums: inout [Int], _ low: Int, _ high: Int) {
if low < high {
let pivotIndex = partition(&nums, low, high)
quickSort(&nums, low, pivotIndex - 1)
quickSort(&nums, pivotIndex + 1, high)
}
}
func partition(_ nums: inout [Int], _ low: Int, _ high: Int) -> Int {
let pivot = nums[high]
var i = low - 1
for j in low..<high {
if nums[j] <= pivot {
i += 1
nums.swapAt(i, j)
}
}
nums.swapAt(i + 1, high)
return i + 1
}
快速排序是一种高效的排序算法,其平均时间复杂度为 O(n log n),最坏情况下的时间复杂度为 O(n^2)。快速排序通过选择一个枢轴元素,将数组划分为两部分,然后递归地对这两部分进行排序。
3. 优先队列:时间复杂度更优,但实现难度较高
func findKthSmallest(_ nums: [Int], _ k: Int) -> [Int] {
// 创建优先队列,最大堆
var pq = PriorityQueue<Int>(sort: >)
// 将前 k 个元素加入优先队列
for i in 0..<k {
pq.enqueue(nums[i])
}
// 将剩余元素依次加入优先队列,并弹出最大元素
for i in k..<nums.count {
pq.enqueue(nums[i])
pq.dequeue()
}
// 将优先队列中的元素取出并返回
var result = [Int]()
while !pq.isEmpty {
result.append(pq.dequeue())
}
return result
}
class PriorityQueue<T: Comparable> {
private var heap: [T] = []
private let sort: (T, T) -> Bool
init(sort: @escaping (T, T) -> Bool) {
self.sort = sort
}
func enqueue(_ element: T) {
heap.append(element)
heapifyUp(heap.count - 1)
}
func dequeue() -> T? {
guard !heap.isEmpty else {
return nil
}
let first = heap[0]
heap[0] = heap.removeLast()
heapifyDown(0)
return first
}
private func heapifyUp(_ index: Int) {
var childIndex = index
var parentIndex = (childIndex - 1) / 2
while childIndex > 0 && sort(heap[childIndex], heap[parentIndex]) {
heap.swapAt(childIndex, parentIndex)
childIndex = parentIndex
parentIndex = (childIndex - 1) / 2
}
}
private func heapifyDown(_ index: Int) {
var parentIndex = index
while true {
let leftChildIndex = 2 * parentIndex + 1
let rightChildIndex = 2 * parentIndex + 2
var largestIndex = parentIndex
if leftChildIndex < heap.count && sort(heap[leftChildIndex], heap[largestIndex]) {
largestIndex = leftChildIndex
}
if rightChildIndex < heap.count && sort(heap[rightChildIndex], heap[largestIndex]) {
largestIndex = rightChildIndex
}
if largestIndex == parentIndex {
break
}
heap.swapAt(parentIndex, largestIndex)
parentIndex = largestIndex
}
}
}
优先队列是一种支持快速查找最大或最小元素的数据结构,其时间复杂度为 O(log n)。优先队列通过维护一个有序的堆结构,并在每次操作时调整堆的结构,从而实现快速查找。
4. 冒泡排序:简单易懂,但效率低下
func findKthSmallest(_ nums: [Int], _ k: Int) -> [Int] {
// 冒泡排序
var numsCopy = nums
for i in 0..<numsCopy.count {
for j in i+1..<numsCopy.count {
if numsCopy[j] < numsCopy[i] {
numsCopy.swapAt(i, j)
}
}
}
// 获取前 k 个元素
return Array(numsCopy[0..<k])
}
冒泡排序是一种简单的排序算法,其时间复杂度为 O(n^2)。冒泡排序通过反复比较相邻元素的大小,并交换顺序不正确