【问题标题】:How to Return subscript in Constant Time?如何在恒定时间内返回下标?
【发布时间】:2018-01-18 19:31:16
【问题描述】:

按照Swift Documentation当符合Collection协议时:

符合 Collection 的类型应提供 startIndex 和 endIndex 属性以及对元素的下标访问作为 O(1) 操作。

如何在恒定时间内返回下标?它不需要遍历集合,直到正确的索引,然后返回该值吗?

这是我用来符合 Collection 的 LinkedList:

indirect enum LinkedList<T> {
    case value(element: T, next: LinkedList<T>)
    case end
}

extension LinkedList: Sequence {
    func makeIterator() -> LinkedListIterator<T> {
        return LinkedListIterator(current: self)
    }
    var underestimatedCount: Int {
        var count = 0
        for _ in self {
            count += 1
        }
        return count
    }
}

struct LinkedListIterator<T>: IteratorProtocol {
    var current: LinkedList<T>
    mutating func next() -> T? {
        switch current {
        case let .value(element, next):
            current = next
            return element
        case .end:
            return nil
        }
    }
}

这是我真正遵守协议的地方:

extension LinkedList: Collection {

    typealias Index = Int
    typealias Element = T

    var startIndex: Index {
        return 0
    }
    var endIndex: Index {
        return underestimatedCount
    }
    func index(after i: Index) -> Index {
        return (i < endIndex) ? i + 1 : endIndex
    }
    subscript (position: Index) -> Element {
        precondition(position < endIndex && position >= startIndex)
        var iterator = makeIterator()
        for i in 0 ..< position {
            iterator.next()
            if i + 1 == position {
                return iterator.next()!
            }
        }
        var zero = makeIterator()
        return zero.next()!
    }

}

let test = LinkedList<Int>.value(element: 2, next: LinkedList<Int>.value(element: 4, next: LinkedList<Int>.value(element: 7, next: LinkedList<Int>.value(element: 9, next: LinkedList<Int>.end))))

【问题讨论】:

  • 您可以查看Collection's source code 以了解它是如何在 Swift 本身中实现的。有关该主题的更实用,更少理论的教程,您还可以查看this tutorial by raywenderlich
  • 请注意,underestimatedCount 也应该是 O(1)。
  • Collection 方法的默认实现是假设 O(1) 下标,所以如果你让你的 LinkedList 符合 Collection,你会发现其中一些太慢了。跨度>
  • 我认为Collections 类似于数组,其元素可以随机访问,而LinkedList 对我来说是Sequence 而不是Collection
  • @NandiinBao 你想的是RandomAccessCollection;一个集合,它的索引可以在恒定时间内偏移,以及在恒定时间内测量两个索引之间的距离。 Collection 只是一个 Sequence 可以非破坏性地迭代,并且有一个可以下标的索引。将索引偏移n 个位置可以在 O(n) 时间内发生(但 subscript 预计会有 O(1) 实现)。

标签: swift collections time-complexity


【解决方案1】:

集合的Index 不必是Int。一种可能的方法 是使用一个自定义索引类型,它有一个 reference 到相应的 元素。但是,这要求列表节点是类的实例。

这是我想出来的。应该可以改进吧 但希望能证明这个想法。

class ListNode 店铺 元素和指向下一个节点的指针,此外,增加 整数ordinal,用于生成struct ListIndex 采用Comparable协议。

struct ListIndex 包含对列表节点的引用,或nilendIndex

struct LinkedListCollection<T>: Collection {

    class ListNode {
        let element: T
        let next: ListNode?
        let ordinal: Int

        init(element: T, next: ListNode?, ordinal: Int) {
            self.element = element
            self.next = next
            self.ordinal = ordinal
        }

        // Create ListNode as the head of a linked list with elements from an iterator.
        convenience init?<I: IteratorProtocol>(it: inout I, ordinal: Int = 0) where I.Element == T {
            if let el = it.next() {
                self.init(element: el, next: ListNode(it: &it, ordinal: ordinal + 1), ordinal: ordinal)
            } else {
                return nil
            }
        }
    }

    struct ListIndex: Comparable {
        let node: ListNode?

        static func <(lhs: ListIndex, rhs: ListIndex) -> Bool {
            // Compare indices according to the ordinal of the referenced
            // node. `nil` (corresponding to `endIndex`) is ordered last.

            switch (lhs.node?.ordinal, rhs.node?.ordinal) {
            case let (r?, l?):
                return r < l
            case (_?, nil):
                return true
            default:
                return false
            }
        }

        static func ==(lhs: ListIndex, rhs: ListIndex) -> Bool {
            return lhs.node?.ordinal == rhs.node?.ordinal
        }
    }

    let startIndex: ListIndex
    let endIndex: ListIndex

    // Create collection as a linked list from the given elements.
    init<S: Sequence>(elements: S) where S.Iterator.Element == T {
        var it = elements.makeIterator()
        startIndex = ListIndex(node: ListNode(it: &it))
        endIndex = ListIndex(node: nil)
    }

    func index(after i: ListIndex) -> ListIndex {
        guard let next = i.node?.next else {
            return endIndex
        }
        return ListIndex(node: next)
    }

    subscript (position: ListIndex) -> T {
        guard let node = position.node else {
            fatalError("index out of bounds")
        }
        return node.element
    }
}

示例用法:

let coll = LinkedListCollection(elements: [1, 1, 2, 3, 5, 8, 13])
for idx in coll.indices {
    print(coll[idx])
}

【讨论】:

    猜你喜欢
    • 2013-10-20
    • 1970-01-01
    • 1970-01-01
    • 2016-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-13
    相关资源
    最近更新 更多