[LeetCode] 6-chain list reordering

Keywords: Programming

subject

Given a single-chain list L:L 0_L 1..._L-n-1_L-n,

Reordering is: L 0_L n_L 1_L n-1_L 2_L n-2...

Local algorithm is required without changing the value of the node

For example:

For a given single-chain list {1,2,3,4}, reorder it to {1,4,2,3}.

Here's a good answer

Solution-Storage

The disadvantage of chain lists is that they cannot be stored randomly. When we want to retrieve the end element, we can only iterate through it from the beginning, which is time consuming.The second time you take the end element, you have to go through it again.

So let's start with a simple, crude idea of storing a list of chains in a linear table, then using a double pointer to pull the elements from the beginning to the end.

public void reorderList(ListNode head) {
    if (head == null) {
        return;
    }
    //Save in list
    List<ListNode> list = new ArrayList<>();
    while (head != null) {
        list.add(head);
        head = head.next;
    }
    //Head and tail pointers take elements in turn
    int i = 0, j = list.size() - 1;
    while (i < j) {
        list.get(i).next = list.get(j);
        i++;
        //Even number of nodes will meet ahead of time
        if (i == j) {
            break;
        }
        list.get(j).next = list.get(i);
        j--;
    }
    list.get(i).next = null;
}

Solution 2 recursion

As mentioned in Solution 1, our problem is that when we take the tail element, we need to traverse the list once.

It would be easier if our recursive function could return the tail element corresponding to the current header element and complete the list of chains between the header and tail elements as required.

As shown above, we only need to point the header to tail and tail to the processed chain header.

img

And then we'll put the previousTail.nextBack is the tail for the outer head er.

If there is only one node, then we only need toHead.nextReturn.

if (len == 1) {
    ListNode outTail = head.next;
    head.next = null;
    return outTail;
}

If it's two nodes, we need toHead.next.nextReturn.

if (len == 2) {
    ListNode outTail = head.next.next;
    head.next.next = null;
    return outTail;
}

Then the overall code is what it looks like below

public void reorderList(ListNode head) {

    if (head == null || head.next == null || head.next.next == null) {
        return;
    }
    int len = 0;
    ListNode h = head;
    //Find out the number of nodes
    while (h != null) {
        len++;
        h = h.next;
    }

    reorderListHelper(head, len);
}

private ListNode reorderListHelper(ListNode head, int len) {
    if (len == 1) {
        ListNode outTail = head.next;
        head.next = null;
        return outTail;
    }
    if (len == 2) {
        ListNode outTail = head.next.next;
        head.next.next = null;
        return outTail;
    }
    //Gets the corresponding end node and recursively processes the list of chains between the head and end nodes
    ListNode tail = reorderListHelper(head.next, len - 2);
    ListNode subHead = head.next;//Head Node of Intermediate Chain List
    head.next = tail;
    ListNode outTail = tail.next;  //tail corresponding to the previous head er
    tail.next = subHead;
    return outTail;
}

Solution 3

It mainly takes advantage of the characteristics of the elements from end to end.

There are three main steps, for example.

1 -> 2 -> 3 -> 4 -> 5 -> 6
 The first step is to divide the list equally into two halves
1 -> 2 -> 3
4 -> 5 -> 6
    
Step 2, reverse the second list
1 -> 2 -> 3
6 -> 5 -> 4
    
Step 3, Join two linked lists in turn
1 -> 6 -> 2 -> 5 -> 3 -> 4

If the first step is to find the midpoint, you can apply the fast or slow pointer.The fast pointer takes two steps at a time and the slow pointer takes one step at a time. When the fast pointer reaches the end, the slow pointer will just reach the midpoint.If the number of nodes is even, slow goes to the left endpoint, which allows us to combine odd and even cases without having to consider them separately.

In the second step, there are two ways of iteration and recursion if the chain list is in reverse order. In the case of iteration, two pointers are used to reverse the order.

The third step is very simple. Move the two pointers back separately.

public void reorderList(ListNode head) {
    if (head == null || head.next == null || head.next.next == null) {
        return;
    }
    //Find the midpoint and divide the list into two
    ListNode slow = head;
    ListNode fast = head;
    while (fast.next != null && fast.next.next != null) {
        slow = slow.next;
        fast = fast.next.next;
    }

    ListNode newHead = slow.next;
    slow.next = null;
    
    //Second Chain List Inverted
    newHead = reverseList(newHead);
    
    //Link List Nodes Connect Sequentially
    while (newHead != null) {
        ListNode temp = newHead.next;
        newHead.next = head.next;
        head.next = newHead;
        head = newHead.next;
        newHead = temp;
    }

}

private ListNode reverseList(ListNode head) {
    if (head == null) {
        return null;
    }
    ListNode tail = head;
    head = head.next;

    tail.next = null;

    while (head != null) {
        ListNode temp = head.next;
        head.next = tail;
        tail = head;
        head = temp;
    }

    return tail;
}

summary

Solution 1 uses space to store is simple, solution 2 recursion is also classical, judging that the current length definition of recursive outlets is clever.Solution three is mainly to understand the topic, the key is to use the characteristics of the elements from end to end.

Posted by bo0 on Mon, 25 May 2020 10:22:24 -0700