繁体   English   中英

迭代器Java中LinkedList的时间复杂度?

[英]Iterator Time complexity for a LinkedList in Java?

下面的代码将调用迭代器并将整数发送回调用它的方法。 我只想知道通过使用Iterator是否会使我的时间复杂度变得不变? (即O(1))。 因为如果我使用带有LinkedList的get,它将给出线性时间(即O(n))。

protected int least(int i) {
    if(i >= digits.size()) {
        return 0;           
    }

    // temp++;
    // System.out.println("Time taken=" + temp);
    // long start = System.nanoTime();

    ListIterator<Integer> itr1 = digits.listIterator(digits.size() - i);

    // System.out.println("Time elapsed:" + (System.nanoTime() - start));

    return itr1.previous();
}

迭代器从第i个元素开始

如果您创建一个直接从LinkedListi个索引处开始的Iterator ,您需要知道这也需要O(n) LinkedList查找元素总是很慢

LinkedList 只记忆列表head (和tail )元素。 遍历整个列表需要找到每个其他元素。

这是一个双向链表的例证(Javas LinkedList也是双重链接):

LinkedList的结构

因此,如果从第i个元素开始创建一个Iterator ,它将从head (或tail )开始,并按照指针一直到第i个元素。 这就像打电话:

list.get(i);

这显然花费了O(n)


替代方案:ArrayList

如果您需要基于索引的快速访问 (也称为随机访问) ,则可以考虑使用ArrayList 它的结构允许在O(1)访问(它可以通过start + i * sizeof(type)直接计算元素在内存中的位置)。

提供这种快速随机访问的数据结构通常将接口RandomAccess文档和实现类实现为指示符。


如何迭代

如上所述,迭代LinkedList应该不是通过list.get(i)通过基于索引的访问来完成的。 因此,如果需要在迭代时修改列表,则应使用Iterator (或ListIterator )。

以下是使用Iterator的常用方法:

Iterator<E> iter = list.iterator();
while (iter.hasNext()) {
    E element = iter.next();
    ...
}

或者你也可以使用增强的for循环 ,它在内部做同样的,但看起来更紧凑:

for (E element : list) {
    ...
}

由于Javas LinkedList是一个双向链表,您也可以从尾部开始并反向迭代列表。 因此,只需使用LinkedList#descendingIterator方法( 文档 )而不是LinkedList#iterator

最后一个示例演示如何在迭代时使用ListIterator修改列表:

ListIterator<E> listIter = list.listIterator(0);
while (listIter.hasNext()) {
    E element = listIter.next();
    ...
    // Remove the element last polled
    listIter.remove();
    // Inserts an element right before the last polled element
    listIter.add(new Element());
}

您也可以向前和向后遍历与列表中ListIterator使用hasPrevious()previous()方法。 这是它的文档

没有。

链接列表不能在O(1)时间内随机访问。 使用迭代器来访问它仍然是O(n)

要理解为什么,我们需要深入研究listIterator(int)

public ListIterator<E> listIterator(int index) {
    checkPositionIndex(index);
    return new ListItr(index);
}

这个方法返回一个新的ListItr ,让我们看看它是如何创建的:

    ListItr(int index) {
        // assert isPositionIndex(index);
        next = (index == size) ? null : node(index);
        nextIndex = index;
    }

构造函数调用node ,其实现方式如下:

Node<E> node(int index) {
    // assert isElementIndex(index);

    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++) <<<<< HERE!
            x = x.next;
        return x;
    } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--) <<<<<< HERE!
            x = x.prev;
        return x;
    }
}

你看到了吗? for循环遍历节点一直到索引index处的循环。 这意味着它是O(n) 获取迭代器的速度随着链表中的元素和index增加而线性增加。

如果你的目的是获取last-one-one元素,那么由于LinkedList实现是一个双向链表,那么应该有一个对实例可用的尾部和头部的引用,以及大小。

这意味着List.descendingIterator()可能是一个更好的起点,如果你必须从最后开始,但你总是要在O(n)时间内索引到LinkedList结构。

如果您经常通过索引解除引用,则通常应使用随机访问结构,例如ArrayList

如果您实际上是在任一方向上遍历列表,那么您应该返回迭代器,而不是值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM