You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中如何避免数组查找的O(n)复杂度?赛车场景优化

优化赛车超车逻辑的性能(避免多次O(n)查找)

原代码中overtake和use wormhole按钮每次点击都会执行两次firstIndex(where:)操作,每次操作的时间复杂度为O(n)。当数组规模较大时,两次遍历会带来明显的性能开销,且数组顺序不能修改(排序会破坏原有顺序并消耗CPU),因此需要在保持数组顺序的前提下优化查找效率。

方案一:单次遍历完成双重查找

将两次O(n)的遍历合并为一次,在遍历数组时同时定位"Me"的位置和目标车辆的位置,将时间复杂度从O(2n)降低到O(n)。

修改后的按钮代码示例:

Button("overtake") {
    var myIndex: Int?
    var targetRank: Int?
    var overtakeIndex: Int?
    
    // 一次遍历同时找到自身位置和目标车辆位置
    for (index, car) in cars.enumerated() {
        if car.name == "Me" {
            myIndex = index
            targetRank = car.rank - 1
        } else if let rank = targetRank, car.rank == rank {
            overtakeIndex = index
        }
    }
    
    guard let idx = myIndex, let overIdx = overtakeIndex, cars[idx].rank != 1 else {
        return
    }
    
    cars[idx].rank -= 1
    cars[overIdx].rank += 1
}

Button("use wormhole") {
    var myIndex: Int?
    var overtakeIndex: Int?
    
    for (index, car) in cars.enumerated() {
        if car.name == "Me" {
            myIndex = index
        } else if car.rank == 1 {
            overtakeIndex = index
        }
    }
    
    guard let idx = myIndex, let overIdx = overtakeIndex, cars[idx].rank != 1 else {
        return
    }
    
    let originalMyRank = cars[idx].rank
    cars[idx].rank = 1
    cars[overIdx].rank = originalMyRank
}

方案二:维护缓存映射实现O(1)查找

利用rank的唯一性(每个车辆rank不重复),维护一个rank -> 数组索引的字典,以及记录自身的索引。仅在数组变化时更新缓存,后续所有查找操作都为O(1)复杂度,适合数组规模大、按钮点击频繁的场景。

完整修改后的代码:

struct ContentView: View {
    
    @State private var cars: [CarType] = [CarType("A", rank: 5), CarType("B", rank: 3), CarType("C", rank: 1), CarType("D", rank: 2), CarType("E", rank: 4), CarType("Me", rank:6)]
    @State private var rankToIndex: [Int: Int] = [:]
    @State private var myCurrentIndex: Int?
    
    var body: some View {
        
        VStack(spacing: 10.0) {
            
            ForEach(cars, id:\.id) { car in
                Text(car.name)
                    .frame(width: 25, height: 25)
                    .background(Color.gray.opacity(0.5).cornerRadius(5.0))
                    .offset(x: 100 - Double(car.rank)*20)
            }
            .animation(.default, value: cars)
            
            Button("overtake") {
                guard let myIdx = myCurrentIndex, cars[myIdx].rank != 1 else {
                    return
                }
                let targetRank = cars[myIdx].rank - 1
                guard let overtakeIdx = rankToIndex[targetRank] else {
                    return
                }
                
                cars[myIdx].rank -= 1
                cars[overtakeIdx].rank += 1
            }
            
            Button("use wormhole") {
                guard let myIdx = myCurrentIndex, cars[myIdx].rank != 1 else {
                    return
                }
                guard let overtakeIdx = rankToIndex[1] else {
                    return
                }
                
                let originalMyRank = cars[myIdx].rank
                cars[myIdx].rank = 1
                cars[overtakeIdx].rank = originalMyRank
            }
            
            Button("reset") {
                cars = [CarType("A", rank: 5), CarType("B", rank: 3), CarType("C", rank: 1), CarType("D", rank: 2), CarType("E", rank: 4), CarType("Me", rank:6)]
            }
            
        }
        .frame(width: 200)
        .padding()
        .overlay(Text("🏁"), alignment: .trailing)
        .onChange(of: cars) { newCars in
            // 更新缓存映射
            var map: [Int: Int] = [:]
            var meIdx: Int?
            for (index, car) in newCars.enumerated() {
                map[car.rank] = index
                if car.name == "Me" {
                    meIdx = index
                }
            }
            rankToIndex = map
            myCurrentIndex = meIdx
        }

    }
}

struct CarType: Equatable {
    
    let id: UUID = UUID()
    var name: String
    var rank: Int
    
    init(_ name: String, rank: Int) {
        self.name = name
        self.rank = rank
    }
}

方案对比

  • 方案一:实现简单,无需额外状态维护,适合数组规模中等的场景,将时间复杂度从O(2n)优化为O(n)。
  • 方案二:需要维护缓存状态,但查找操作降为O(1),数组变化时仅需一次O(n)遍历更新缓存,性能最优,适合数组规模大、交互频繁的场景。

内容的提问来源于stack exchange,提问作者swiftPunk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 21:27:28