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.
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.