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}
.
题目如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
不同于一般的链表翻转,这里要求是链表要交叉着进行倒叙。
我的思路是:先寻找链表的中间节点,然后在此处对后半段的链表进行翻转,这里就得到前后两段顺序正常的链表,然后进行合并。程序用到了3个最
基本的链表操作,寻找中间节点findMiddle(), 链表翻转reverseList(),以及最终的合并reorderList()。
(1)寻找中间节点findMiddle()
给定一个链表*head, 利用一个first和second指针,前者每次前进一步,后者每次前进2步,当遍历完链表之后,first指针就是需要找的中间节点。但在leetcode上提交时几次出现了Runtime error 的问题。因为大多数寻找链表中间节点的操作只需要返回中间节点就够了,而在此处,除了找到中间节点first,还需要在此处将前后断开。在找到中间点first之后,要断开链表需要知道first的前驱,由于是单链表,无法直接得到前驱,所以我用了一个pre来保存。调用该函数后,first和second分别指向链表前一半和后一半的表头,代码如下:
struct Node* findMiddle(struct Node* head)
{
struct Node* first, *second ,*pre;
first = second = head;
if(NULL== head | NULL == head ->next)
return head;
else
{
while(second != NULL && second -> next != NULL )
{
pre = first;
first = first -> next;
second = second -> next->next;
}
second = first;
pre -> next = NULL;
first = head;
return second;
}
}
(2)翻转链表操作 reverse().给定一个链表*head,对其翻转操作。这里我用的是原址翻转,也就是不增加额外空间,在原地对链表进行操作,需要的是改变原链表的指针指向,用到3个变量pre, cur, nex,代码如下:
//reverse the totall Linklist 'head' without extra space
struct Node* reverse(struct Node* head)
{
struct Node* pre = NULL;
struct Node* cur= head;
struct Node* nex= NULL;
while(cur != NULL)
{
nex = cur -> next;
cur -> next = pre;
pre = cur;
cur = nex;
}
head -> next = NULL;
head = pre;
return pre;
}
(3)在归并之前,首先调用寻找终点函数, mid = findMiddle(*head), 得到前后两段链表,然后对后半段链表调用上个函数mid = reverse(mid)得到后半段已经逆序后的mid链表。至此,已经有了两段链表head和mid,现在需要对他们进行挨个插入排序,即在head的第1,2,3,4,...个节点之后分别插入mid的第1,2,3,...个节点。
这里有个细节需要注意,就是对于两段链表节点个数不一样的问题的边界处理。由于mid包含了原链表的中点,所以如果总节点个数为奇数个,那么必然是mid链表比head链表多出1个。每次进行处理的时候,需要先用head_nex和mid_nex来保存当前两段链表的后驱节点,然后插入过程需要进行判断,如果head_nex为空,则插入mid_nex,否则插入head_nex(这段逻辑大家好好体会下),代码如下:
void reorderList(ListNode *head)
{
if(head==NULL)
return;
else
{
struct ListNode* mid = findMiddle(head);
mid = reverse(mid); // return the List reversed from the middle
struct ListNode* head_nex, *mid_nex;
head_nex = mid_nex = NULL;
struct ListNode* newlist = head;
if(head == mid)
;
else
while(head !=NULL && mid != NULL)
{
head_nex = head -> next;
mid_nex = mid-> next; //insert the second element into the first Linklist
mid -> next = (head -> next == NULL)?mid->next:head->next;
head -> next = mid;
head = head_nex;
mid = mid_nex;
}
}
//
}