题目:
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}.
思路(1):喜欢效率较高的盆友直接看思路(2)
此题要求在不改变节点value值的情况下,写出完整代码,给我的第一感觉就是又是乱七八糟的指针操作(对于指针操作,一般需要缓存当前访问的节点,当前访问节点的next节点等等)。看到这道题,给我的第一直觉就是如果能用栈去缓存,然后一边出栈,一边移动链表指针,这样问题就解决了,于是第一版的思路出来了。
public void reorderList(ListNode head) {
if(head == null)
return;
Stack<ListNode> stack = new Stack<ListNode>();
ListNode node = head;
int elementNum = 0;
//将所有元素入栈
while(node !=null){
stack.push(node);
node = node.next;
elementNum++;
}
ListNode nowPos = head;
ListNode nextPos = null;
ListNode tailPos = null;
for(int i = 0;i<elementNum/2;i++){
if(tailPos != null)
tailPos.next = nowPos;
nextPos = nowPos.next;
tailPos = stack.peek();
nowPos.next = stack.pop();
nowPos= nextPos;
}
}
但这套代码在LeetCode显示Memory Limit Exceeded,也就是说,此种方法内存开销会相当大,那么有没有另外的解决办法呢?
思路(2):
其实细心的盆友就可以看出来,将所有元素入栈是极大的浪费,因为我们需要反转的就是后半部分,但不是用Stack去缓存链表,而是直接操作链表反转,这样第二种思路就出现了。
public void reorderList(ListNode head) {
if (head == null || head.next == null) return;
//把整个链表划分成2个等长的子链表,如果原链表长度为奇数,那么第一个子链表的长度多1
ListNode slow = head, fast = head;
while (fast.next != null) {
fast = fast.next;
if (fast.next != null) fast = fast.next;
else break;
slow = slow.next;
}
ListNode head1 = head, head2 = slow.next;
slow.next = null;
//翻转第二个子链表
ListNode cur = head2, post = cur.next;
cur.next = null;
while (post != null) {
ListNode tmp = post.next;
post.next = cur;
cur = post;
post = tmp;
}
head2 = cur;
//将两个子链表合并
ListNode node1 = head1, node2 = head2;
while (node2 != null) {
ListNode tmp1 = node1.next;
ListNode tmp2 = node2.next;
node1.next = node2;
node2.next = tmp1;
node1 = tmp1;
node2 = tmp2;
}
}
LeetCode上显示Runtime: 384 ms,Your runtime beats 97.45% of java coders.性能相当不错了。其实这题还有其它的思路,有兴趣的可以思索一下。