如何用O(1)空间和O(n)时间反转列表?

时间:2011-05-12 22:43:35

标签: java algorithm list

我正在寻找一种方法来反转给定列表的相同实例,具有O(1)额外空间和O(n)时间。
这不是硬件,也不是我正在寻找一些图书馆方法来为我完成这项工作,因为这只是我自己的练习,而且出于纯粹的好奇心。

有任何想法如何用O(1)额外空间和O(n)时间来做? (如果可能的话,没有反思)? 签名是public <T> void reverse(List<T> list)

(*)假设列表的头部和尾部的get()是O(1),但是它的中间是O(n)。

我想出了一个递归解决方案,但它是O(n)空间,O(n)时间

public <T> void reverseAux(List<T> list,int size) {
    if (size == 0) return;
    T elem = list.remove(size-1);
    reverseAux(list,size-1);
    list.add(0,elem);
}
public <T> void reverse(List<T> list) {
    reverseAux(list, list.size());
}

编辑:我正在为List<T>寻找一个java解决方案,只有实现的假设是头部和尾部的访问时间O(1),并使用List<T>接口

7 个答案:

答案 0 :(得分:15)

请阅读以下其中一项。这是你在谈论的事情。

请注意,我们正在谈论单独的'链接'列表。

http://www.teamten.com/lawrence/writings/reverse_a_linked_list.html

http://www.mytechinterviews.com/reverse-a-linked-list

http://www.geekpedia.com/code48_Reverse-a-linked-list.html

http://www.codeproject.com/KB/recipes/ReverseLinkedList.aspx

另外还有一个问题:

  

你如何从链表的尾部找到N元素,假设它是单链接的,你只有头指针有O(1)空格和O(N)时间?

答案 1 :(得分:4)

使用ListIterators:

ListIterator<T> head = list.listIterator();
ListIterator<T> tail = list.listIterator(size);//assuming this can be done in O(1) though O(n) doesn't hurt that much and total is still O(n)
while(head.nextIndex()<tail.previousIndex()){
    T tmp = head.next();
    head.set(tail.previous());
    tail.set(tmp);
}

答案 2 :(得分:2)

你已经知道了长度。因此,只需使用1个临时变量并从索引0开始,然后继续交换列表[0]并列出[length -1],然后列出[1]并列出[length-2],依此类推。 O(n)时间和O(1)空间为1个临时变量。

编辑:刚刚注意到你假设O(n)访问列表的中间位置。那好吧。没关系。

或者,存储你交换的两个元素的下一个/前一个指针向中间移动(假设它是一个双向链表)。然后你得到O(n)时间。

答案 3 :(得分:0)

ListIterator界面是您正在寻找的(在合理的假设下,相关列表完全支持它; ArrayListLinkedList都这样做):

ListIterator<T> fwd = list.listIterator();
ListIterator<T> back = list.listIterator(list.size());
while (fwd.nextIndex() < back.previousIndex()) {
    T tmp = fwd.next();
    fwd.set(back.previous());
    back.set(tmp);
}

即使在链表上,这也应该是线性的。

答案 4 :(得分:0)

如上所述,在一般情况下,这是不可行的,您需要假设某些操作的复杂性。如果迭代器有常量next()previous(),请使用已经给出的解决方案。它应该适用于LinkedList和ArrayList。

我想到了一个适用于单链表的解决方案(但不适用于像ArrayList这样的东西),但遗憾的是ListIterators add方法在光标之前而不是之后插入元素,因此它是不适用于List + ListIterator接口(如果我们无法修补ListIterator实现以缓存插入前元素以允许在O(1)中previous()之后的单个add

这里,假设一个带有next-pointer的简单Node类:

/**
 * reverses a singly linked list.
 * @param first the fist node. This will be the new last node.
 * @param last the last node. This will be the new first node.
 */
void reverseList(Node first, Node last) {
   while(first != last) {
      Node temp = first;
      first = temp.next;
      temp.next = last.next;
      last.next = temp;
   }
}

在索引术语中,这将是这样的:

public void reverseList(List<T> list) {
    int index = list.size() -1;
    while(n > 0) {
       T element = list.remove(0);
       list.add(n, element);
       n--;
    }
}

在ListIterator术语中,这将是这样的:

public void reverseList(List<T> list) {
    ListIterator<T> it = list.listIterator(list.size());
    while(it.previousIndex() > 0) { // we could count ourself here, too
       T element = list.remove(0);
       it.add(element);
       it.previous();
    }
}

当然,通常的单链表实现不会有O(1)previous实现,因此它不会在那里工作,如前所述。 (他们可能会抛出ConcurrentModificationException,或者返回错误的previousIndex。)

答案 5 :(得分:0)

从合并排序或快速排序等比较排序中获得的最佳性能是O(nlogn)。您可以从基数排序等非比较排序中获得O(n)性能。

如果要反转链接列表,则可以使用仅3个额外项目在O(n)时间内反转列表。您需要3个指针来跟踪您当前指向的内容,当前项目之前的内容以及当前项目之后的内容。代码是:

Node current = head;
Node next = null;
Node prev = null;
while (current != null) {
    next = current.next;
    current.next = prev;
    prev = current;
    current = next;
}
return prev;

答案 6 :(得分:-1)

   public LinkedList Reverse(LinkedList head)
{
    if (head == null) return null; // first question

    if (head.Next == null) return head; // second question

    // third question
    // so we grab the second element (which will be the last after we reverse it)

    LinkedList secondElem = head.Next;

    // bug fix - need to unlink list from the rest or you will get a cycle
    head.Next = null;

    // then we reverse everything from the second element on
    LinkedList reverseRest = Reverse(secondElem);

    // then we join the two lists
    secondElem.Next = head;

    return reverseRest;
}
相关问题