【问题标题】:Concat linked lists in constant time complexity O(1) in Kotlin or JavaKotlin 或 Java 中恒定时间复杂度 O(1) 的 Concat 链表
【发布时间】:2019-03-01 14:02:45
【问题描述】:

我正在做的是连接动态生成的链表,一次只有 2 个。如何在 Kotlin 或 Java 中以恒定时间复杂度 O(1) 做到这一点?

This similar question in Java 告诉我java.util.LinkedList 不支持添加恒定时间。并且 Google Guava Iterators.concat 只能在一次调用中组合 2 个或更多迭代器,这会导致多层包装并在我的情况下迭代时增加复杂性。

【问题讨论】:

  • 您真的要返回 LinkedList 吗?还是可以实现一个新的 List 来包装几个 LinkedList 并在这些底层 LinkedList 中获取元素?
  • 我认为两者都可以。只要不增加非常量的复杂性,包装就可以了。

标签: java kotlin linked-list concatenation concat


【解决方案1】:

在 Kotlin 中,您可以像这样使用 iterator {...} 函数组合多个 Iterators:

fun <T> combine(a: Iterator<T>, b: Iterator<T>, c: Iterator<T>): Iterator<T> {
  return iterator {
    yieldAll(a)
    yieldAll(b)
    yieldAll(c)
  }
}

此函数返回T 类型的Iterator,它会延迟消耗a,然后是b,最后是c

解决方案是这样的:

fun <T> combine(vararg iterators: Iterator<T>): Iterator<T> {
  return iterator {
    iterators.forEach { yieldAll(it) }
  }
}

此实现采用n 迭代器并将它们组合成一个。

【讨论】:

  • 我明白了,但这个函数的作用与 Google Guava Iterators.concat 相同。它创建了一个包含多个迭代器的迭代器。在我动态生成此类列表的情况下,我一次连接 2 个列表。这可能会导致多层包装并在迭代时增加复杂性。
  • 我认为我对这个迭代器包装范式是错误的。如果假设一个组合迭代器包装器至少包含 2 个迭代器,并且每个链表迭代器至少有 1 个元素,那么它形成一个树数据结构,并且迭代也就是遍历可以在 O(n) 时间内完成,因为数量所有元素的数量 >= 叶节点(链表迭代器)的数量 > 1/2 所有节点的数量(所有迭代器实例)。也许您应该将您的答案编辑为 1. 过滤掉空迭代器和 2. 如果只剩下一个,则返回原始迭代器,那么我会接受您的答案。
【解决方案2】:

我已经实现了一个基于 Java 的LinkedList 的简单版本的单链表,只是为了支持这个 concat 函数。为简单起见,它只实现了Iterable,而不是List

Java 实现:

import java.util.Iterator;
import java.util.NoSuchElementException;

public class SimpleLinkedList<E> implements Iterable<E> {
    Node<E> first;
    Node<E> last;

    static class Node<E> {
        E item;
        Node<E> next;

        Node(E item, Node<E> next) {
            this.item = item;
            this.next = next;
        }
    }

    static class NodeIterator<E> implements Iterator<E> {
        private Node<E> node;

        NodeIterator(Node<E> node) {
            this.node = node;
        }

        public boolean hasNext() {
            return node != null;
        }

        public E next() {
            Node<E> currentNode = node;
            if (currentNode == null) throw new NoSuchElementException();
            node = currentNode.next;
            return currentNode.item;
        }
    }

    public Iterator<E> iterator() {
        return new NodeIterator<>(first);
    }

    public void add(E element) {
        // Copied from java.util.LinkedList
        Node l = last;
        Node<E> newNode = new Node<>(element, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
    }

    public void concatWith(SimpleLinkedList other) {
        if (last != null) last.next = other.first;
        else first = other.first;

        if (other.last != null) last = other.last;
    }
}

Kotlin 实现:

class SimpleLinkedList<E> : Iterable<E> {
    var first: Node<E>? = null
    var last: Node<E>? = null

    class Node<E>(var item: E, var next: Node<E>? = null)
    class NodeIterator<E>(var node: Node<E>?) : Iterator<E> {
        override fun hasNext(): Boolean = node != null
        override fun next(): E {
            val currentNode = node
            if (currentNode === null) throw NoSuchElementException()
            node = currentNode.next
            return currentNode.item
        }
    }

    override fun iterator(): Iterator<E> = NodeIterator(first)

    fun add(element: E) {
        // Copied from java.util.LinkedList
        val l = last
        val newNode = Node(element, null)
        last = newNode
        if (l == null)
            first = newNode
        else
            l.next = newNode
    }

    infix fun concatWith(other: SimpleLinkedList<E>) {
        last.run {
            if (this !== null) next = other.first
            else first = other.first
        }
        other.last?.let { last = it }
    }
}

Kotlin 实现实际上比 Java 慢一点,因为 getter 和 setter 用于访问属性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-04
    • 1970-01-01
    • 2015-05-25
    • 2022-01-20
    • 1970-01-01
    • 1970-01-01
    • 2017-04-06
    相关资源
    最近更新 更多