題目:
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.性能相當不錯了。其實這題還有其它的思路,有興趣的可以思索一下。