关于单链表的题目,最重要的便是指针的操作。我近期涉及到的单链表的题目对我最大的感想就是:无论是对单链表的任何操作,一个单链表的标识便是它的头指针。所以很多的操作无非就是一个“新”的头节点带领着一列元素罢了。
对于上述的题目,可能刚开始的时候会无从下手,至少我是这样,我在考虑怎样将Ln移动到L1之后,怎样得到Ln-1的指针呢。其实分开来看,无非就是两个链表的merge, L = L0 , L1 , ... L(n/2) ; R = Ln, Ln-1, ... , L(n/2+1). 怎样得到这两个链表呢?
对于L, 直接截取链表的前 n/2 个元素即可。分为两种方法,
1 首先计算整个链表的长度n, 然后 将头指针移动 n/2 次,便得到了 L:
2 可以使用快慢指针的方式,快的指针每次移动两步,慢的指针每次移动一步,直到快的指针到达链表尾部时,此时的慢指针就是要找的分界点。
对于 R,由于在寻找L的过程中,将整个链表分成了前后断,后一段为 L(n/2+1), L (n/2+2), ... , Ln, 但是我们需要的R刚好是它的reverse。这里便涉及到链表的reverse的操作,其实很简单,只需要遍历一遍,将每个节点依次插入到 “新的” 链表的表头即可。
比如: L1,L2,L3 ,现要将这个链表reverse。设新的链表头为RR:
则RR = NULL;对于节点L1, 将L1插入到链表的表头,RR = L1,依次有 RR = L2,L1; RR = L3,L2,L1,
,因此达到了上述的reverse的目的。
最后一步直接merge L 和 R 即可。代码大致为:
void reorderList(ListNode *head) {
if(head == NULL) return;
ListNode *tail = new ListNode(0);
ListNode *t1 = head;
int len = 0 ;
while(t1)
{
len++;
t1 = t1->next;
}
int mid = len/2;
t1 = head;
while(mid--) t1 = t1->next;
ListNode *t2 = t1->next;
t1->next = NULL;
ListNode *t3 = NULL;
//reverse
while(t2)
{
t1 = tail->next;
t3 = t2->next;
tail->next = t2;
t2->next = t1;
t2 = t3;
}
t2 = tail->next;
t1 = head;
ListNode *t4 = NULL;
while(t2)
{
t3 = t1->next;
t4 = t2->next;
t1->next = t2;
t2->next = t3;
t1 = t3;
t2 = t4;
}
}
总结: 链表的操作主要是头节点的重要性。