LeetCode之路:206. Reverse Linked List
来源:互联网 发布:淘宝双十一购物节方案 编辑:程序博客网 时间:2024/06/06 09:48
一、引言
这道题折磨了我很久呢 T_T
不得不说,这道题的递归解法着实让人有些不能理解(即使亲手写出来了,仔细钻研仍然难以理解自己的代码)。
先来看看题目吧:
Reverse a singly linked list.
Hint:
A linked list can be reversed either iteratively or recursively. Could you implement both?
这道题的题意非常简单:
逆置单向链表。
提示:
一个单向链表可以循环、递归逆置。那么你能使用这两种方法实现它吗?
这道题其实不难,难就难在了需要用两种方法实现它。
另外,或许也是 LeetCode 上面的一道题的思路启发了我,逆置单链表本身就又有两种思路:
1.逆置结构:改变指针指向构成单链表逆置
2.逆置值:改变值构成单链表逆置
不能理解的话,我这里画了一个图:
二、逆置值:循环和递归的实现
逆置值的思路,主要是需要两次遍历列表,第一次将所有的数据全部获取,第二次将获取到的数据逆向放置。
以下是逆置循环版本代码:
// my solution 1 iteratively change value , runtime = 9 msclass Solution1 {public: ListNode* reverseList(ListNode* head) { vector<int> value; for (auto p = head; p != nullptr; p = p->next) value.push_back(p->val); int i = 0; for (auto p = head; p != nullptr; p = p->next, ++i) p->val = value[value.size() - i - 1]; return head; }};
这里使用了 vector<int>
来存储链表中的值集合,最后一次遍历中,使用了 value[value.size() - i - 1]
来实现了值的逆置设入。
这个思路的递归版本会比较复杂,需要对递归有深刻的理解,想想如何实现两次遍历呢?
// my solution 2 recursively change value , runtime = 9 msclass Solution2 {public: ListNode* reverseList(ListNode* head) { if (head == nullptr) return head; value.push(head->val); reverseList(head->next); head->val = value.front(); value.pop(); return head; }private: queue<int> value;};
这份代码会比较生涩,详细解释下:
首先,我使用了
std::queue
队列来存储链表中的值。使用队列的好处不言而喻,正好实现了逆置需求。其次,让我们看看递归过程:
1. 首先,参数节点判空处理
2. 然后,在 reverseList() 递归调用前,实现队列值的压入;这样的话,直到找到单向链表的末尾,都在进行值的压入操作
3. 再然后,我们结束了递归调用的那一刻,也就是我们递归遍历到了单向链表结尾的时候,此时我们将队列的值逆向设置在每层递归的head->val
中去,也就刚好实现了值的逆置
这个方法非常巧妙,即使是我已经写出来了,再来看看这个方法依然觉得非常简洁优雅。
三、逆置结构:循环和递归的实现
接下来,让我们看看逆置结构的思路:这条思路就是要处理指针指向了。比之逆置值的思路而言,这一思路能够少遍历一次。
以下是逆置结构循环版本代码:
// my solution 3 iteratively change structure , runtime = 9 msclass Solution3 {public: ListNode* reverseList(ListNode* head) { ListNode *pre = nullptr; while (head != nullptr) { auto temp = head->next; head->next = pre; pre = head; head = temp; } return pre; }};
这份代码并不像表面上看到的那么易懂:
首先,我们定义了一个 pre 变量用来记录 head 节点之前的节点值(初始化为空指针)
然后,我们进入循环。要知道我们要干什么,我们要将 head(也就是当前节点)的 next 指针指向 pre,仅此而已。但是为了实现 head 向
head->next
的递进不与head->next
的赋值相冲突,我们需要定义一个 temp 临时变量记录其值。最后,我们需要知道返回的是什么。我们需要返回新的链表的头结点,而这个节点又刚好是 head 递进到空指针后的 pre 节点
可见,处理指针远比处理值的思路的代码逻辑复杂。
不过更难以理解的还是下面的逆置结构递归版本代码:
// my solution 4 recursively change structure , runtime = 6 msclass Solution {public: ListNode* reverseList(ListNode* head) { if (!head || !head->next) return head; auto newHead = reverseList(head->next); head->next->next = head; head->next = nullptr; return newHead; }};
这个方法可不像它表面上那么简单,让我们好好研究下:
首先,我进行了判空处理。一般的判空只判空 head 即可,而这里为什么还要判空
head->next
呢?这是因为我们需要通过head->next
为空的情况下,返回此时的head
来给出我们最后需要返回的新的链表的头节点(这是这个思路的最难的地方也是精髓所在)然后,我们开始递归调用本函数,并且接受递归的返回(指向新链表的头节点用于返回)。递归调用结束之后,我们需要处理指针。刚递归结束时,我们正好处于链表的结尾处,此时我们只需要将当前的
head->next
的 next 指针指向head
即可,并且将head->next
指针置空。最后,经过层层递归返回,我们一次又一次从尾巴到开头设置了新的指针指向,并且还保存了新的链表的头节点,直接返回即可。
那么,这里有个难以理解的地方:
head->next->next = head;head->next = nullptr;
我们每层递归中,都将 head->next->next
指向了 head,这我们可以理解,这是反向指向;但是 head->next
修改为 nullptr,这就有点说不通了,每次都修改为空指针,不会影响下一层递归的 head->next->next
的赋值吗?
哈哈 ^_^
这个问题我也想了好久,最后发现,其实是我多虑了。
我们在每次递归中,处理的对象都是 head->next
这个对象,那么这个对象在这层递归过程中,就是一个局部变量而已,我们设置 head->next->next
的指向,并没有修改 head->next
的值哦,而是通过我们存储的局部变量 head->next
来寻找到我们需要改变的对象而已。
所以说,因为每层递归之中的 head->next
的值都是相互不会影响的,所以 head->next = nullptr
的运行结果,就是我们期望看到的,整个链表的第一个节点的 next 指针被设置为空,此时返回正好达成题意。
可见,逆置结构的方法远远要比逆置值的方法复杂 T_T,不过确实挺能锻炼人的(比如说我就纠结了小半个下午)。
四、总结
这是一道比较锻炼人的题目,如果真的可以思路清晰的不费吹灰之力就写出以上四个方法的话,那真的是大神了,请收下我的膝盖 :)
所以说,不要小看任何自以为会很简单的题目哦~~~
To be Stronger!
- LeetCode之路:206. Reverse Linked List
- leetcode 之 Reverse Linked List
- leetcode 之 Reverse Linked List
- LeetCode之Reverse Linked List
- leetcode之Reverse Linked List
- leetcode之 Reverse Linked List
- leetCode之 Reverse Linked List
- leetcode之reverse Linked List
- 【LeetCode】206.reverse linked list
- [leetcode] 206.Reverse Linked List
- [leetcode] 206. Reverse Linked List
- 206. Reverse Linked List LeetCode
- leetCode 206. Reverse Linked List
- LeetCode 206. Reverse Linked List
- LeetCode 206. Reverse Linked List
- [LeetCode]206. Reverse Linked List
- 【LeetCode】206. Reverse Linked List
- LeetCode 206. Reverse Linked List
- Caffe学习:图像数据转换成db(leveldb/lmdb)文件
- 浮点数
- 百度快照实现分析
- OkhttpUtils传body
- Redis 持久化之AOF(三)
- LeetCode之路:206. Reverse Linked List
- 蛇形填数(二)--求值法
- day07
- 布隆过滤器
- 什么是搜索引擎沙盒效应,网站沙盒期是什么
- 增强学习与无人驾驶
- day08
- android后台通过View生成分享图片
- OpenGl纹理的两种方式-- 1 use点 2 use纹理对象