Swift报错:无法对不可变值使用可变成员,求解决方案
问题:使用Swift Algorithm Club的Heap结构体解决「Heaps: Find the Running Median」时出错
嘿,我看到你在用Swift Algorithm Club的Heap实现来搞定Hackerrank的「Heaps: Find the Running Median」挑战,但遇到了问题,而且你的代码片段还没写完(buildHea...这里明显截断了)。先把你给出的代码整理出来,然后咱们聊聊这类问题里最容易踩的几个坑:
你提供的代码片段
import Foundation // Enter your code here struct Heap<Element> { var elements : [Element] let priorityFunction : (Element, Element) -> Bool init(elements: [Element] = [], priorityFunction: @escaping (Element, Element) -> Bool) { self.elements = elements self.priorityFunction = priorityFunction buildHea... // 这里代码未完成 } }
常见问题分析&解决方案
求动态中位数的核心是用两个堆配合:一个最大堆存较小的一半元素,一个最小堆存较大的一半元素。结合你的代码情况,大概率是以下几个地方出了问题:
1. Heap核心方法未完整实现
你的buildHeap方法只写了一半,这个方法是用来把初始数组堆化的关键步骤,没实现的话堆的结构完全不对,后续的入队、出队操作都会失效。我给你补全一个完整的Heap实现,包含所有必要的核心方法:
struct Heap<Element> { var elements: [Element] let priorityFunction: (Element, Element) -> Bool init(elements: [Element] = [], priorityFunction: @escaping (Element, Element) -> Bool) { self.elements = elements self.priorityFunction = priorityFunction buildHeap() } // 堆化初始数组 mutating func buildHeap() { for index in (0..<elements.count/2).reversed() { siftDown(from: index) } } var isEmpty: Bool { elements.isEmpty } var count: Int { elements.count } func peek() -> Element? { elements.first } // 入队:添加元素后上浮调整 mutating func enqueue(_ element: Element) { elements.append(element) siftUp(from: elements.count - 1) } // 出队:取出堆顶后下沉调整 mutating func dequeue() -> Element? { guard !isEmpty else { return nil } elements.swapAt(0, elements.count - 1) let element = elements.removeLast() siftDown(from: 0) return element } // 上浮操作:维护堆的优先级 private mutating func siftUp(from index: Int) { var child = index var parent = parentIndex(of: child) while child > 0 && priorityFunction(elements[child], elements[parent]) { elements.swapAt(child, parent) child = parent parent = parentIndex(of: child) } } // 下沉操作:维护堆的优先级 private mutating func siftDown(from index: Int) { var parent = index while true { let left = leftChildIndex(of: parent) let right = rightChildIndex(of: parent) var candidate = parent if left < count && priorityFunction(elements[left], elements[candidate]) { candidate = left } if right < count && priorityFunction(elements[right], elements[candidate]) { candidate = right } if candidate == parent { return } elements.swapAt(parent, candidate) parent = candidate } } // 辅助方法:计算父节点索引 private func parentIndex(of index: Int) -> Int { (index - 1) / 2 } // 辅助方法:计算左子节点索引 private func leftChildIndex(of index: Int) -> Int { index * 2 + 1 } // 辅助方法:计算右子节点索引 private func rightChildIndex(of index: Int) -> Int { index * 2 + 2 } }
2. 堆的优先级函数设置错误
最大堆的优先级函数应该是$0 > $1(保证堆顶是最大元素),最小堆是$0 < $1(保证堆顶是最小元素),如果搞反了,堆的逻辑会完全颠倒,导致中位数计算错误。
3. 两个堆的平衡维护不当
每次插入元素后,必须保证:
- 两个堆的大小差不超过1
- 最大堆的堆顶元素 ≤ 最小堆的堆顶元素
我给你写一个完整的中位数计算逻辑,包含堆的平衡维护:
func findRunningMedian(_ numbers: [Double]) -> [Double] { // 最大堆:存储较小的一半元素,堆顶是最大的那个 var maxHeap = Heap<Double>(priorityFunction: >) // 最小堆:存储较大的一半元素,堆顶是最小的那个 var minHeap = Heap<Double>(priorityFunction: <) var medians: [Double] = [] for number in numbers { // 把元素插入到合适的堆 if maxHeap.isEmpty || number <= maxHeap.peek()! { maxHeap.enqueue(number) } else { minHeap.enqueue(number) } // 平衡两个堆的大小 if maxHeap.count > minHeap.count + 1 { // 最大堆太大,把堆顶移到最小堆 if let element = maxHeap.dequeue() { minHeap.enqueue(element) } } else if minHeap.count > maxHeap.count { // 最小堆太大,把堆顶移到最大堆 if let element = minHeap.dequeue() { maxHeap.enqueue(element) } } // 计算当前中位数 let median: Double if maxHeap.count == minHeap.count { median = (maxHeap.peek()! + minHeap.peek()!) / 2.0 } else { // 元素总数为奇数时,最大堆的堆顶就是中位数 median = maxHeap.peek()! } medians.append(median) } return medians } // 示例测试 let input = [1, 2, 3, 4, 5] let result = findRunningMedian(input.map { Double($0) }) print(result) // 输出 [1.0, 1.5, 2.0, 2.5, 3.0]
4. 注意Hackerrank的输入输出格式
最后还要注意Hackerrank的要求:比如输入是多行数字,输出需要保留指定的小数位数(通常是1位),你需要把计算出的中位数按格式输出。
内容的提问来源于stack exchange,提问作者Rick Clark
相关产品推荐
相关产品推荐

