how to reverse a list with O(1) space and O(n) time?

前端 未结 8 1551
梦毁少年i
梦毁少年i 2020-12-28 19:56

I am looking for a method that reverses the same instance of a given list, with O(1) additional space and O(n) time.
this is not HW nor I am looking for some library me

相关标签:
8条回答
  • 2020-12-28 20:19

    Just read one of the following. It is the thing you're talking about.

    Please note that we're talking about singly 'linked' lists.

    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

    Plus an extra question for you:

    How would you find Nth element from the tail of a linked list assuming it is singly linked and you have only head pointer with O(1) space and O(N) time?

    0 讨论(0)
  • 2020-12-28 20:19

    You already know the length. So just use 1 temporary variable and start at index 0 and go on swapping list[0] and list[length -1], then list[1] and list[length-2], and so on. O(n) time and O(1) space for 1 temporary variable.

    EDIT: Just noticed you assume O(n) for accessing the middle of the list. oh well. nevermind.

    alternatively, store the next/previous pointers of the two elements you swapped to move towards the middle (assuming it's a doubly linked list). Then you get O(n) time.

    0 讨论(0)
  • 2020-12-28 20:23

    Here is a solution in Java, with O(n) time complexity (just a single pass) and O(1) space complexity (Using just two temporary variables):

        private static void reverseLinkedList() {//O(n) Time complexity, O(1) space complexity 
    
        //two temp pointers
        Node next = null, previous = null;
    
        while(head.next != null){
            next = head.next;//move next to next address
            head.next = previous;   //previous node will be the next node for head, so that head will point reverse         
            previous = head; //incrementing previous to the current node
            head = next; //incrementing head 
    
        }
        //at this point head points to last node and previous has the remaining reversed array
        head.next = previous;
        System.out.println("\nReversed");
    
    }
    

    Full code goes like:

      package com.test;
    
    public class LinkedListReverse {
        private static Node  head;
        public static void main(String[] args) {
    
            for(int i = 0 ; i< 10 ; i++){
                addToLinkedList(i);
            }
            System.out.println("Added Data");
    
            printLinkedList();
            reverseLinkedList();
            printLinkedList();
    
    
        }
    
    
    
        private static void reverseLinkedList() {//O(n) Time complexity, O(1) space complexity 
    
            //two temp pointers
            Node next = null, previous = null;
    
            while(head.next != null){
                next = head.next;//move next to next address
                head.next = previous;   //previous node will be the next node for head, so that head will point reverse         
                previous = head; //incrementing previous to the current node
                head = next; //incrementing head 
    
            }
            //at this point head points to last node and previous has the remaining reversed array
            head.next = previous;
            System.out.println("\nReversed");
    
        }
    
    
        /* Logic for adding and printing linked list*/
        private static void printLinkedList() {
            System.out.println("Printing linked list");
            Node temp = head;
            while(temp.next != null){
                System.out.print(temp.value+" ");
                temp = temp.next;
            }
            System.out.print(temp.value+" ");//print the value at the last node
    
    
        }
    
        private static void addToLinkedList(int value){
            if(head == null){
                head = new Node(value, null);
            }else{
                Node temp = head;
                while(temp.next != null){
                    temp = temp.next;
                }
                temp.next = new  Node(value, null);
            }
        }
    
    }
    
    //Linked List definition 
    class Node{
        int value;
        Node next;
        public Node(int value, Node next){
            this.value = value;
            this.next = next;
        }
    }
    

    Program's Output:

    Added Data
    Printing linked list
    0 1 2 3 4 5 6 7 8 9 
    Reversed
    Printing linked list
    9 8 7 6 5 4 3 2 1 0 
    

    Hope it helps :)

    0 讨论(0)
  • 2020-12-28 20:28

    The ListIterator interface is what you're looking for (under the reasonable assumption that the list in question fully supports it; both ArrayList and LinkedList do):

    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);
    }
    

    Even on linked lists, this should be linear in time.

    0 讨论(0)
  • 2020-12-28 20:30

    As discussed, in the general case this is not doable, you need to assume something about the complexity of the individual operations. If you have constant-time next() and previous() for the iterators, use the solution already given. It should work for both LinkedList and ArrayList.

    I thought about a solution which would work for a singly-linked list (but not for something like ArrayList), but sadly the ListIterators add method inserts the element before the cursor instead of after it, thus it is not doable with the List + ListIterator interfaces (if we can't patch the ListIterator implementation to cache the pre-insert element to allow a single previous() after add in O(1)).

    Here, assuming a simple Node class with next-pointer:

    /**
     * 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;
       }
    }
    

    In index terms, this would be something like this:

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

    In ListIterator terms, this would be something like this:

    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();
        }
    }
    

    Of course, usual singly linked list implementations will not have a O(1) previous implementation, thus it will not work there, as said before. (And they might throw a ConcurrentModificationException, or return erronous previousIndex.)

    0 讨论(0)
  • 2020-12-28 20:33

    The best performance you can get from comparison sorts like merge sort or quick sort is O(nlogn). You can get O(n) performance from non-comparison sorts like radix sort.

    If you are reversing a linked-list, then you can reverse the list in O(n) time with using just 3 extra items. You need 3 pointers to keep track of what you're currently pointing to, what is before your current item and what is after your current item. The code is:

    Node current = head;
    Node next = null;
    Node prev = null;
    while (current != null) {
        next = current.next;
        current.next = prev;
        prev = current;
        current = next;
    }
    return prev;
    
    0 讨论(0)
提交回复
热议问题