这道题要求将一个链表中的指定部分反转,而保持其他成分不变,题面要求如下:
记录这道题的目的在于提醒自己头结点(DummyNode)在链表问题中的重要性。头结点的存在使得首结点成为一个普通的结点,这样为统一操作和简化代码提供了大量的方便。如果不使用头结点,在一些问题中就必须要为第一个结点设置大量的条件检查和情况讨论,比如此题。我在第一次求解的时候就引入了大量的处理细节,虽然最终正确通过,但是代码逻辑性和可读性非常差,所以一定要引入链表的DummyNode来实现操作的简化。
我一开始的思路非常简单,就是将要翻转的链表段摘出来,按照一般的链表反转将其反转再接回去。所以可见我们需要下面四个关键位置(红色结点是需要翻转的链表):
predecessor和successor是位于原先链表中的位置,为了将子链表接回去。
head和tail就是将要反转的子链表的头和尾的位置。
引入头结点之后的所有代码如下:
ListNode* reverseBetween(ListNode* head, int left, int right) {
/*only 1 node needs to be reversed*/
if(left == right)
return head;
/*apply a dummy node to simplify the operation*/
ListNode* DummyNode = new ListNode(-1, head);
/*head and tail is the first and last element of the list needed to be reversed*/
/*predecessor and successor is the element ahead of head and after of tail*/
ListNode* Head = DummyNode;
ListNode* Tail = DummyNode;
ListNode* Predecessor = DummyNode;
ListNode* Successor = DummyNode;
/*adjust the position of pointer*/
/*predecessor*/
while(--left)
Predecessor = Predecessor->next;
/*Head*/
Head = Predecessor->next;
/*Tail*/
while(right--)
Tail = Tail->next;
/*Successor*/
Successor = Tail->next;
reverseList(Head, Tail);
/*connect the reverse list to original list*/
Predecessor->next = Tail;
Head->next = Successor;
return DummyNode->next;
}
其中函数reverseList就是将子链表反转的简单函数:
void reverseList(ListNode* Head, ListNode* Tail)
{
ListNode* Ahead = Head->next;
ListNode* Follow = Head;
while(Follow != Tail)
{
ListNode* Keep = Ahead->next;
Ahead->next = Follow;
/*the next node*/
Follow = Ahead;
Ahead = Keep;
}
}
这就是第一种解法,可以看到虽然复杂度是O(n),但是却不止遍历了一次。另一种进阶方法(头插法)可以让算法遍历次数降至1次,在顺序访问链表一趟的过程中完成链表局部区域的反转,这是要加以学习的方法,这种方法需要更加细致地操纵指针,示意图如下:
转换前:
转换后(动作一共三步):
按照1,2,3的顺序将指针操作翻译成代码即可:
for(int i = left ; i < right ; ++i)
{
ListNode* Next = Current->next;
ListNode* Tmp = Next->next; /*keep the next of next pointer*/
Current->next = Tmp; /*Step 1*/
Next->next = Previous->next; /*Step 2*/
Previous->next = Next; /*Step 3*/
}
完整代码如下:
ListNode* reverseBetween(ListNode* head, int left, int right) {
/*only one node to be reversed*/
if(left == right)
return head;
/*apply a dummy node as always*/
ListNode* DummyNode = new ListNode(-1, head);
int Left = left;
/*adjust the position of pointer*/
ListNode* Previous = DummyNode;
while(--Left)
Previous = Previous->next;
/*point to node to be inserted to*/
ListNode* Current = Previous->next;
for(int i = left ; i < right ; ++i)
{
ListNode* Next = Current->next;
ListNode* Tmp = Next->next; /*keep the next of next pointer*/
Current->next = Tmp; /*Step 1*/
Next->next = Previous->next; /*Step 2*/
Previous->next = Next; /*Step 3*/
}
return DummyNode->next;
}