反转链表是常见题。最基本的就是给一个链表,然后反转。如果稍微带点花样的话,LeetCode上有一些题,比如Reverse Linked List II,是叫你反转在一定范围内的nodes,比如从第二个node开始到第四个node结束,反转之间的子链表;再有比如Reverse Nodes in k-Group,是叫你每个长度为k的子链表反转。反转链表的题并没有什么高深的算法,考验的基本上就是纯粹的编程基础,链表操作。到底如何优雅地反转一个链表呢?方法可能有不止一种,这里提供一种笔者觉得比较易懂的方法。先上代码:
struct ListNode{
int val;
ListNode *next;
ListNode (int v) : val(v), next(NULL) {}
};
ListNode *reverse(ListNode *head) {
if (!head)
return NULL;
ListNode dummy(-1);
dummy.next = head;
ListNode *preM = &dummy;
ListNode *pre = head;
head = head->next;
while (head) {
pre->next = head->next;
head->next = preM->next;
preM->next = head;
head = pre->next;
}
return dummy.next;
}
看到这里保存了几个指针:
- preM指向的是,需要反转的部分之前的一个节点。如果是从头反转,则需要一个fakeHead;
- pre永远都是需要翻转部分的第一个node。在整个过程中,这个指针虽然不变,但它所指向的那个node一直在向后移动,直到移动到list末尾;
- head指向的是pre后面一个,也就是下一个需要被翻转的node。翻转的原理是,每次都把head所指向的这个node放到最前面去,然后head和pre都依次向后移动一个。
举个例子:比如我们有一个链表1---2---3,我们现在需要翻转整个链表。首先来一个fake head,链表变成了#---1---2---3,那么preM就一直指向#这个node不变;而pre永远指向1这个node不变;head一开始指向的是2。接着,把head所指向的这个node放在preM后面,链表变成了#---2---1---3,然后让head回到pre后面。于是这个时候,三个指针分别指向的是:preM: #; pre: 1; head: 3。而链表变成了: #---2---1---3。下一步链表变成了:#---3---2---1,翻转完成。