[英]Prepend complexity of scala vector
我指的是官方文件
這表明將Vector的復雜性作為“有效常數”(eC)。 但我的理解是,對於一個向量,前置意味着所有其他索引也需要調整,這將使操作O(n)或L(線性)。 任何人都可以解釋如何在矢量eC(有效常數)前置。
找到了前置操作的可視化解釋,其中每個步驟中都有一個字符。 圖片僅顯示每個塊2個插槽以便於解釋,但是在矢量的情況下,每個塊將有32個插槽。 Vector維護起始索引(或圖片中的偏移量)以跟蹤空位。
以下是Vector.scala的源代碼。 由於它不移動所有元素,因此它不是O(n)。
override def prepended[B >: A](value: B): Vector[B] = {
if (endIndex != startIndex) {
val blockIndex = (startIndex - 1) & ~31
val lo = (startIndex - 1) & 31
if (startIndex != blockIndex + 32) {
val s = new Vector(startIndex - 1, endIndex, blockIndex)
s.initFrom(this)
s.dirty = dirty
s.gotoPosWritable(focus, blockIndex, focus ^ blockIndex)
s.display0(lo) = value.asInstanceOf[AnyRef]
s
} else {
val freeSpace = (1 << (5 * depth)) - endIndex // free space at the right given the current tree-structure depth
val shift = freeSpace & ~((1 << (5 * (depth - 1))) - 1) // number of elements by which we'll shift right (only move at top level)
val shiftBlocks = freeSpace >>> (5 * (depth - 1)) // number of top-level blocks
if (shift != 0) {
// case A: we can shift right on the top level
if (depth > 1) {
val newBlockIndex = blockIndex + shift
val newFocus = focus + shift
val s = new Vector(startIndex - 1 + shift, endIndex + shift, newBlockIndex)
s.initFrom(this)
s.dirty = dirty
s.shiftTopLevel(0, shiftBlocks) // shift right by n blocks
s.gotoFreshPosWritable(newFocus, newBlockIndex, newFocus ^ newBlockIndex) // maybe create pos; prepare for writing
s.display0(lo) = value.asInstanceOf[AnyRef]
s
} else {
val newBlockIndex = blockIndex + 32
val newFocus = focus
val s = new Vector(startIndex - 1 + shift, endIndex + shift, newBlockIndex)
s.initFrom(this)
s.dirty = dirty
s.shiftTopLevel(0, shiftBlocks) // shift right by n elements
s.gotoPosWritable(newFocus, newBlockIndex, newFocus ^ newBlockIndex) // prepare for writing
s.display0(shift - 1) = value.asInstanceOf[AnyRef]
s
}
} else if (blockIndex < 0) {
// case B: we need to move the whole structure
val move = (1 << (5 * (depth + 1))) - (1 << (5 * depth))
val newBlockIndex = blockIndex + move
val newFocus = focus + move
val s = new Vector(startIndex - 1 + move, endIndex + move, newBlockIndex)
s.initFrom(this)
s.dirty = dirty
s.gotoFreshPosWritable(newFocus, newBlockIndex, newFocus ^ newBlockIndex) // could optimize: we know it will create a whole branch
s.display0(lo) = value.asInstanceOf[AnyRef]
s
} else {
val newBlockIndex = blockIndex
val newFocus = focus
val s = new Vector(startIndex - 1, endIndex, newBlockIndex)
s.initFrom(this)
s.dirty = dirty
s.gotoFreshPosWritable(newFocus, newBlockIndex, newFocus ^ newBlockIndex)
s.display0(lo) = value.asInstanceOf[AnyRef]
s
}
}
} else {
// empty vector, just insert single element at the back
val elems = new Array[AnyRef](32)
elems(31) = value.asInstanceOf[AnyRef]
val s = new Vector(31, 32, 0)
s.depth = 1
s.display0 = elems
s
}
}
不需要調整索引,因為Scala的Vector是作為樹實現的。 更具體地說,一個32路樹,意味着每個父母有32個孩子。
http://www.scala-lang.org/api/2.12.0/scala/collection/immutable/Vector.html
它由一個小端序位映射矢量trie支持,分支因子為32。
這意味着所有操作都需要執行O(log32(n))
時間。 如果這對您來說不是很明顯,只需遵循更熟悉的二叉樹中的邏輯,這些樹在所有操作中都具有O(log2(n))
復雜度。
然而,對於這種復雜性是應該被視為對數還是有效不變,存在一些爭議。 從理論上講,它是一個很好的舊O(log n)
,但是對數的基數為32,結合其他一些實現細節(例如緩存)這一事實使得它在實踐中看起來幾乎不變(或者,正如他們所說的那樣, “有效地不變”)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.