题目:
Given a singly linked list L: L0→L1→…→Ln-1→Ln,
reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→…
You must do this in-place without altering the nodes' values.
For example,
Given {1,2,3,4}
, reorder it to {1,4,2,3}
.
题目分析:题干给出了一个链表,需要重新排列顺序。要求,将倒数第一个元素插到第一个元素后面,倒数第二个元素插入到第二个元素后面。。。。。。以此类推。另外,题干要求原地算法(in-place),即需要通过移动原来链表中的节点而不能另创新的链表来完成任务。
思路:
- 将链表分为两个部分,前一部分是待插入的链表前部分,后一部分是等待被插入的链表后部分
- 遍历链表,统计元素个数n,如果个数小于3,则无需调整,直接调用return结束
- 前半部分长度应该为[n/2]+1,其余为后半部分(长度为[n/2]或者[n/2]-1)
- 这样分,无论n为奇数还是偶数,全部需要插入的元素即是后部分的全部元素
- nextHead标记待插入地方的前一个元素,即被插入的元素要插到nextHead.next
- pos标记等待被插入的元素,pre记录pos的前一个元素,即pre.next==pos
- 从后往前,递归地找到正确的被插入元素,插入,并且返回下一个正确的nextHead
- 先递归地调用nextHead = doInsert (nextHead, pre.next, pos.next)
- 即待插入的地方nextHead不变,(pre.next和pos.next)每次向后查找正确的被插入元素及其前一个元素
- 正确的被插入元素即是每次被插入列表的最右端元素
- 返回一个新的nextHead,即是上一层递归中,pre和pos对应的应该被插入的地方
注意点:
- 之前写了一个类似思想的算法,但没有用递归。而是,每一次都遍历链表,找到正确的pre和pos。
- LeetCode上没有通过,TimeLimitedExceed错误。原因是,每次都遍历链表找被插入元素太费时间
- 利用递归,上一层递归自动记下了被插入元素的pre和pos,只需要每次返回正确的待插入地方nextHead即可
代码:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public void reorderList(ListNode head) {
ListNode tag = head;
ListNode pre, pos;
int len=0;
while (tag != null){
len++;
tag = tag.next;
}
if (len <= 2){
return;
}
pre = head;
for (int i=1; i <= len/2; i++){
pre = pre.next;
}
pos = pre.next;
ListNode nextHead = head;
doInsert(nextHead, pre, pos);
}
static ListNode doInsert(ListNode nextHead, ListNode pre, ListNode pos){
if (pos.next == null){
pre.next = null;
pos.next = nextHead.next;
nextHead.next = pos;
nextHead = pos.next;
return nextHead;
}
nextHead = doInsert(nextHead,pre.next,pos.next);
pre.next = null;
pos.next = nextHead.next;
nextHead.next = pos;
nextHead = pos.next;
return nextHead;
}
}