Reorder List及细节归纳

给定一个单链表 LL0L1→…→Ln-1Ln ,
将其重新排列后变为: L0LnL1Ln-1L2Ln-2→…

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:

给定链表 1->2->3->4, 重新排列为 1->4->2->3.

示例 2:

给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3.

这道题的思想有两种:

第一种是找到链表中点,把链表中点前的节点放到队列中(先进先出),把中点后的链表节点放到栈中(先进后出),这样每次从队列或者栈中交替取出元素重新构建链表即可。这样的时间复杂度是O(n),空间复杂度是O(n),题目中建议使空间复杂度为O(1),并且第一种方法代码写的比较复杂,要考虑的细节和边界条件比较多(这个后面说)。

第二种在discuss部分看到的思路,我们找到中点节点后对后面的节点进行翻转,然后前面的链表和中点后翻转的链表交替链接形成新链表。这道题不难,主要是作者的思路和一些细节的trick上代码写的比较精巧,思路值得我们借鉴。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void reorderList(ListNode* head) {
	 if (!head || !head->next || !head->next->next) {
		 return;
	 }
	 ListNode* slow;
	 ListNode* fast;
	 slow = head;
	 fast = head->next;
	 while (fast && fast->next) {
		 slow = slow->next;
		 fast = fast->next->next;
	 }

	 ListNode* pre_head = slow->next;
	 ListNode* tmp = pre_head;
	 //断链接
	 slow->next = nullptr;

	 ListNode* pre = tmp;
	 ListNode* after = tmp;
	 tmp = tmp->next;
	 pre->next = nullptr;
	 //从slow->next开始反转链表
	 while (tmp) {
		 after = tmp->next;
		 tmp->next = pre;
		 pre = tmp;
		 tmp = after;
	 }
	 //tmp表示第一个链表头结点,after表示第二个链表头结点
	 for (tmp = head, after = pre; tmp;) {
		 auto t = tmp->next;
		 tmp->next = after;
		 tmp = tmp->next;
		 after = t;
	 }
    }
};

细节归纳:

1:在用快慢指针找中点的时候,如果快慢指针初始化为fast=head->next且slow=head,且快指针判断的条件为:

 while (fast && fast->next) 

那么如果是奇数个节点,最后slow指针的位置为第(n+1)/2个节点,刚好是中点,如果是偶数个节点,那么slow指针最后的位置为n/2。

如果快慢指针初始化为fast=head->next且slow=head,且快指针判断的条件为:

 while (fast->next && fast->next->next) 

那么如果是奇数个节点,最后slow指针的位置为第(n)/2个节点,刚好是中点前一个节点,如果是偶数个节点,那么slow指针最后的位置为n/2。

这里为什么要强调这一点,因为我们是以slow->next作为第二段链表的头结点,所以第二种方法就需要考虑边界情况,把遗漏的中点节点加进去。

2:我们以这点代码为例来讲解如何交替链接两段链表

 //tmp表示第一个链表头结点,after表示第二个链表头结点
	 for (tmp = head, after = pre; tmp;) {
		 auto t = tmp->next;
		 tmp->next = after;
		 tmp = tmp->next;
		 after = t;
	 }
常规思想是通过如下条件判断,并通过两个指针分别指向两段链表来遍历。
while(first_head && second_head)
但是作者的想法更精巧,思想值得我们借鉴,通过一个中间变量t来不断循环链接两段链表:

第一次循环:提前把tmp的本身的下一个p节点找出,在处理完成后把after赋值给tmp,然后状态变为第二次循环:


第二次循环:


作者便是通过类似“之”子步且通过一个临时变量来实现了两个链表的交替循环构造。

(路线为蓝->红->黑)


猜你喜欢

转载自blog.csdn.net/qq_26410101/article/details/80425128