「Day5」 玩转递归链表「hot 100之leetcode206」

65 阅读3分钟

一、题目

206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

image.png

``` 输入: head = [1,2,3,4,5] 输出: [5,4,3,2,1] ```

更多细节请参看官方题目详情

二、题解

2.1 迭代法

1 -->2-->3 -->4-->5
head
head->next

对于一个链表,已知头节点,我们只知道头节点和头节点的下一个节点。

现在要实现链表的反转, 我们可以先创建一个链表 ListNode *pre = nullptr;,然后不断遍历当前链表的节点,使其指向 pre

1 -->2-->3 -->4-->5-->nullptr
headhead->next
ListNode *pre = nullptr<-- cur
<-- (cur->next)

链表反转通常需要用到三个指针:

  • 当前节点指针 cur,需要连接前一个节点,原来的首节点应该连接 nullptr 节点
  • 前驱节点指针 pre,上一步连接完成,应该让pre指针指向cur
  • 后继节点指针 在上两个步骤开始之前应该最先记录当前节点的后继节点,便于找到更新前的cur-->next,看下面的图,我们就会清楚的知道一旦当前节点指向前一个节点,当前节点和原来的下一个节点的连接就断了,所以我们应该提前记录一下当前节点的下一个节点,以便cur节点重新指向pre的时候,还能找到之后要遍历的节点。

image.png

简单写一下
//初始化
pre = nullptr;
cur = head;
//记录后继系节点
after = cur->next;

在反转链表的过程中,当前节点指针指向的节点的 next 指针需要指向其前驱节点,因此需要在遍历链表的同时记录当前节点指针和前驱节点指针。由于链表是单向的,需要用到后继节点指针来记录下一个要遍历的节点。

image.png

在迭代的过程中指针需要后移,如上图所示。
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        
        ListNode* pre = nullptr;
        ListNode* cur = head;
       
        while(cur != nullptr)
        {
            ListNode* after = cur->next;
            cur->next = pre;
            pre = cur;
            cur = after;
        }
        return pre;
    }
};

2.2 递归法

我理解的递归法的思路就是找一样的,可以重复处理的步骤,再确定好终止条件就可以了。

image.png

每个节点的反转思路都是一样的,头节点特殊一点,反转后应该指向nullptr; head->next = nullptr;

其他节点的操作都一样只要当前节点,和下一个节点不为空,就可以继续操作;也就是只要当前节点,和下一个节点为空就终止。否则递归处理当前节点的下一个节点recursive(head->next)。

或者可以理解为,head指针到达链表的尾部,开始向前逐个反转,但是过程中需要 如果nk以后的节点都已经完成反转操作,那么该如何处理,nk到nk-1以前的节点呢?

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        
    }
};

三、总结

题目知识点进度
136数组、位运算(异或)、哈希表1
121数组、动态规划2
3字符串、滑动窗口、哈希表3
131字符串、回溯、动态规划4
206递归、链表6