在Swift 3.0中使用索引访问字符串的大O.

时间:2016-08-06 04:50:00

标签: swift

String 3.0中使用index访问swift的复杂性是多少?

复杂性是否与数组访问或O(N)或其他东西相同?

来自“字符串索引”下的documentation

let greeting = "Guten Tag!"
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index]
.
.
.
for index in greeting.characters.indices {
    print("\(greeting[index]) ", terminator: "")
}
// Prints "G u t e n   T a g ! "

如果索引访问是O(N),那么最后一个例子(迭代字符)会非常糟糕,因为只需迭代字符就行O(n ^ 2)

我不确定的原因是以下声明:“不同的字符[...]可能需要不同数量的内存来存储”。

如果复杂性不是O(n),那么它是如何工作的,因为人们不能将偏移量乘以常数来得到内存中的字符?

1 个答案:

答案 0 :(得分:3)

除非另有说明,否则Collection的{​​{3}}要求的实现总是具有O(1)时间复杂度。这是与Collection本身签订合同的一部分。

正如subscript所述(强调我的):

  

符合Collection的类型应提供start​Indexend​Index属性和下标对元素的访问权限,作为O(1)操作。无法保证预期性能的类型必须记录出发[...]

当你来到推进指数时会发生潜在的昂贵部分,例如使用the documentation。对于RandomAccessCollection,其复杂度为O(1),否则为O(n),其中n是偏移量的大小。

index(_:offsetBy:)不是RandomAccessCollection,因此推进指数是O(n)。如你所说,原因是字符可以有不同的字节长度。但是一旦你有索引(String.CharacterView只是字符串的unicode标量的偏移值以及UTF-16代码单元中给定的扩展字形集群的长度),你可以在恒定时间下标。

因此

for index in greeting.characters.indices {
    print("\(greeting[index]) ", terminator: "")
}

是O(n)。循环的每次迭代只是将当前索引提前1,并且下标使用索引的偏移量跳转到扩展字形集群的开头,从而获得该给定索引的字符。

但是,如果我们说:

for offset in 0..<greeting.characters.count {
    let index = greeting.index(greeting.startIndex, offsetBy: offset)
    print("\(greeting[index]) ", terminator: "")
}

那个将是O(n 2 ),因为我们现在正在循环的每次迭代中从起始索引推进索引(更不用说做一个O(n)步行只是为了得到count开头的字符。)