Odersky Scala书中的队列实现。第19章

Odersky Scala书中的队列实现。第19章,scala,Scala,我在关于Scala的Odersky书的第388页上看到了这段代码: class SlowAppendQueue[T](elems: List[T]) { def head = elems.head def tail = new SowAppendQueue(elems.tail) def enqueue(x: T) = new SlowAppendQueue(elems ::: List(x)) } class SlowHeadQueue[T](smele: List[T]) {

我在关于Scala的Odersky书的第388页上看到了这段代码:

class SlowAppendQueue[T](elems: List[T]) {
  def head = elems.head
  def tail = new SowAppendQueue(elems.tail)
  def enqueue(x: T) = new SlowAppendQueue(elems ::: List(x))
}

class SlowHeadQueue[T](smele: List[T]) {
  def head = smele.last
  def tail = new SlowHeadQueue(smele.init)
  def enqueue(x: T) = new SlowHeadQueue(x :: smele)
}
以下说法是否正确:

  • tail
    的两种实现都需要与队列中元素数量成比例的时间
  • head的第二个实现比第一个慢。第二个实现所需的时间与队列的长度成比例。为什么会这样?它是如何实施的?它是否像一个链表,其中每个元素都有指向下一个元素的指针
  • 为什么奥德斯基说第二类的
    tail
    实现有问题,而不是第一类

  • 否。在第一种情况下,
    tail
    在固定时间内工作,因为
    elems.tail
    是一个固定时间操作(它只返回列表的尾部)。构造函数
    newslowappendqueue(…)
    也是一个常量时间操作,因为它只是包装列表
  • 因为如果
    smele
    N>1
    元素,那么
    smele.init
    必须从头开始用
    N-1
    元素重新构建一个新列表。这需要线性时间,因此它比第一个队列实现的
    O(1)
    操作慢得多
  • O(N)
    操作是有问题的,因为大型
    N
    操作速度较慢,而
    O(1)
    操作基本上从来没有问题
  • 我认为您应该更仔细地了解不可变的单链表是如何实现的,以及如何预先添加元素(
    O(1)
    ),追加元素(
    O(N)
    ),访问尾部(
    O(1)
    ),重建
    init
    O(N)
    )。然后其他一切都变得显而易见

  • 否。在第一种情况下,
    tail
    在固定时间内工作,因为
    elems.tail
    是一个固定时间操作(它只返回列表的尾部)。构造函数
    newslowappendqueue(…)
    也是一个常量时间操作,因为它只是包装列表
  • 因为如果
    smele
    N>1
    元素,那么
    smele.init
    必须从头开始用
    N-1
    元素重新构建一个新列表。这需要线性时间,因此它比第一个队列实现的
    O(1)
    操作慢得多
  • O(N)
    操作是有问题的,因为大型
    N
    操作速度较慢,而
    O(1)
    操作基本上从来没有问题
  • 我认为您应该更仔细地了解不可变的单链表是如何实现的,以及如何预先添加元素(
    O(1)
    ),追加元素(
    O(N)
    ),访问尾部(
    O(1)
    ),重建
    init
    O(N)
    )。然后其他一切都变得显而易见

  • 不,第一个
    tail
    实现需要固定的时间。这是因为由于结构共享,
    List.tail
    是一个固定时间操作,并且将列表包装在新的
    SlowAppendQueue
    中也是一个固定时间操作

  • 由于函数链表(包括Scala的
    List
    类)的工作方式,第二个
    head
    实现需要固定的时间。每个列表节点后面都有一个指向该节点的链接。要通过
    init
    删除最后一个元素,必须重新生成整个列表

  • 总之,
    List
    在开始操作时速度快,但在结束操作时速度慢。另见

  • 不,第一个
    tail
    实现需要固定的时间。这是因为由于结构共享,
    List.tail
    是一个固定时间操作,并且将列表包装在新的
    SlowAppendQueue
    中也是一个固定时间操作

  • 由于函数链表(包括Scala的
    List
    类)的工作方式,第二个
    head
    实现需要固定的时间。每个列表节点后面都有一个指向该节点的链接。要通过
    init
    删除最后一个元素,必须重新生成整个列表


  • 总之,
    List
    在开始操作时速度快,但在结束操作时速度慢。另请参见。

    我相信这只是在Scala中编程FP结构的一个示例。在现实世界中,队列用于绕过线程之间的消息。请参阅JCTools库,以获得不同变体的最佳实现:@andriyplokhotnuk确实这是并发队列的典型用法,但队列也用于许多算法,包括BFS和Dijkstra算法。@BrianMcCutchon Dijkstra算法需要像Fibonacci堆这样的优先级队列,这是一只完全不同的野兽。不幸的是,几十年过去了,在标准库中似乎仍然没有Fibonacci堆(Java和Scala都没有),我相信这只是在Scala中编程FP结构的一个例子。在现实世界中,队列用于绕过线程之间的消息。请参阅JCTools库,以获得不同变体的最佳实现:@andriyplokhotnuk确实这是并发队列的典型用法,但队列也用于许多算法,包括BFS和Dijkstra算法。@BrianMcCutchon Dijkstra算法需要像Fibonacci堆这样的优先级队列,这是一只完全不同的野兽。不幸的是,几十年过去了,在标准库中似乎仍然没有斐波那契堆(无论是在Java中,还是在Scala中)。tail是如何实现的?它不需要遍历整个列表才能到达尾部吗?哦它只是将列表从索引1返回到右后?@Jwan622看一看。函数式编程语言中的单链表自古以来就以这种方式实现。要访问head,只需返回第一个指针。访问
    tail