赞
踩
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
核心思想:
设立三个指针,依次完成反转,在反转之前需要存储下一个结点的地址,否则后面的结点会丢失。
首先看一下原链表:
总共需要添加两个指针,pre 和 next 。初始化 pre 指向 NULL。
然后就是迭代的步骤,总共四步,顺序一步都不能错。
一次迭代就完成了,如果再进行一次迭代就变成下边的样子。
可以看到整个过程无非是把旧链表的 head 取下来,添加的新链表头部。接下来就是停止条件了,我们再进行一次循环:
可以发现当 head
或者 next
指向 null
的时候,我们就可以停止了。此时将 pre
返回,便是逆序了的链表了。
- /**
- * Definition for singly-linked list.
- * struct ListNode {
- * int val;
- * ListNode *next;
- * ListNode(int x) : val(x), next(NULL) {}
- * };
- */
- class Solution {
- public:
- ListNode* reverseList(ListNode* head) {
- //空链表和一个结点的链表都无需反转
- if(head == NULL || head->next == NULL) return head;
- ListNode *pre = NULL,*cur = head, *next = head;
- while(cur != NULL){
- next = cur->next; //保存cur的next,以防止cur改变后丢失
- cur->next = pre; //逐个结点反转
- pre = cur; //更新指针位置
- cur = next;
- }
- return pre; //返回反转后的头结点
- }
- };

关于链表的题,记住更改指向的时候要保存之前的节点,不然会丢失节点。
可参考这篇博客,和这篇。其实这种方式就是原地链表的翻转,即没有使用额外的空间。
这种模式的适用场景:
把 head
结点拿出来,剩下的部分我们调用函数 reverseList,这样剩下的部分就逆序了,接着我们把 head
结点放到新链表 的尾部就可以了,这就是整个递归的思想了。
当然就是如果结点的个数是一个,那么逆序的话还是它本身,直接 return 就够了。怎么判断结点个数是不是一个呢?它 的 next
等于 null
就说明是一个了。但如果传进来的本身就是 null
,那么直接找它的 next
会报错,所以先判断传进来的是 不是 null
,如果是,也是直接返回就可以了。
- /**
- * Definition for singly-linked list.
- * struct ListNode {
- * int val;
- * ListNode *next;
- * ListNode(int x) : val(x), next(NULL) {}
- * };
- */
- class Solution {
- public:
- ListNode* reverseList(ListNode* head) {
- //第一个条件是判断递归开始,传入的参数的合法性。第二个是递归的终止条件
- if(head == NULL || head->next == NULL)
- return head;
- ListNode* newhead = reverseList(head->next); //开始进行递归,先反转后面的链表,走到链表的末端
- head->next->next = head; //第二个结点的 next 指向第一个结点
- head->next = NULL; //第一个结点的 next 指向NULL
- return newhead;
- }
- };

总结:
对于递归算法,最重要的就是明确递归函数的定义。具体来说,我们的 reverseList
函数定义是这样的:
输入一个节点 head
,将「以 head
为起点」的链表反转,并返回反转之后的头结点。
明白了函数的定义,在来看这个问题。比如说我们想反转这个链表:
那么输入 reverseList(head)后 ,会在这里进行递归:
ListNode* newhead = reverseList(head->next);
不要跳进递归(你的脑袋能压几个栈呀?),而是要根据刚才的函数定义,来弄清楚这段代码会产生什么结果:
这个 reverseList(head)执行完成后,整个链表就成了这样:
并且根据函数定义,reverseList 函数会返回反转之后的头结点,我们用变量 newhead(在图中是 last)接收了。
现在再来看下面的代码:
- head->next->next = head;
- head->next = NULL;
- return newhead;
神不神奇,这样整个链表就反转过来了!递归代码就是这么简洁优雅,不过其中有两个地方需要注意:
1、递归函数要有 递归基,也就是这句:
- if(head == NULL || head->next == NULL)
- return head;
意思是如果链表只有一个节点的时候反转也是它自己,直接返回即可。
2、当链表递归反转之后,新的头结点是 last
,而之前的 head
变成了最后一个节点,别忘了链表的末尾要指向 null:
head->next = NULL;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。