链表3:反转链表(206)
本题如下:(链接:https://leetcode.cn/problems/reverse-linked-list/)
题目:给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
思路:对于这道题只需要改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表,如下图所示:
可见,之前链表的头节点是元素1, 反转之后头结点就是元素5 ,这里并没有添加或者删除节点,仅仅是改变next指针的方向。
那么具体的实施步骤就是,首先要定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。
在反转前要先把 cur->next 节点用tmp指针保存一下,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。
接下来,就是循环走如下代码逻辑了,继续移动pre和cur指针。
最后,cur 指针已经指向了null,循环结束,链表也反转完毕了。 此时return pre指针就可以了,pre指针就指向了新的头结点。
具体的C++代码如下:
双指针法:
1 class Solution { 2 public: 3 ListNode* reverseList(ListNode* head) { 4 ListNode* temp; // 保存cur的下一个节点 5 ListNode* cur = head; 6 ListNode* pre = NULL; 7 while(cur) { 8 temp = cur->next; // 保存一下 cur的下一个节点,因为接下来要改变cur->next 9 cur->next = pre; // 翻转操作 10 // 更新pre 和 cur指针 11 pre = cur; 12 cur = temp; 13 } 14 return pre; 15 } 16 };
递归法1:
递归法虽然看起来相对抽象一些,但是其实和双指针法是一样的逻辑,同样是当cur为空的时候循环结束,不断将cur指向pre的过程。
关键是在于初始化的地方,可以看到双指针法中初始化 cur = head,pre = NULL,在递归法中可以从如下代码看出初始化的逻辑也是一样的,只不过写法变了。
1 class Solution { 2 public: 3 ListNode* reverse(ListNode* pre,ListNode* cur){ 4 if(cur == NULL) return pre; 5 ListNode* temp = cur->next; 6 cur->next = pre; 7 // 可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步 8 // pre = cur; 9 // cur = temp; 10 return reverse(cur,temp); 11 } 12 ListNode* reverseList(ListNode* head) { 13 // 和双指针法初始化是一样的逻辑 14 // ListNode* cur = head; 15 // ListNode* pre = NULL; 16 return reverse(NULL, head); 17 } 18 19 };
递归法2:
我们可以发现,上面的递归写法和双指针法实质上都是从前往后翻转指针指向,其实还有另外一种与双指针法不同思路的递归写法:从后往前翻转指针指向。
1 class Solution { 2 public: 3 ListNode* reverseList(ListNode* head) { 4 // 边缘条件判断 5 if(head == NULL) return NULL; 6 if (head->next == NULL) return head; 7 8 // 递归调用,翻转第二个节点开始往后的链表 9 ListNode *last = reverseList(head->next); 10 // 翻转头节点与第二个节点的指向 11 head->next->next = head; 12 // 此时的 head 节点为尾节点,next 需要指向 NULL 13 head->next = NULL; 14 return last; 15 } 16 };
下面给出Java版本分别采用这三种方法的代码:
1 // 双指针 2 class Solution { 3 public ListNode reverseList(ListNode head) { 4 ListNode prev = null; 5 ListNode cur = head; 6 ListNode temp = null; 7 while (cur != null) { 8 temp = cur.next;// 保存下一个节点 9 cur.next = prev; 10 prev = cur; 11 cur = temp; 12 } 13 return prev; 14 } 15 }
1 // 递归 2 class Solution { 3 public ListNode reverseList(ListNode head) { 4 return reverse(null, head); 5 } 6 7 private ListNode reverse(ListNode prev, ListNode cur) { 8 if (cur == null) { 9 return prev; 10 } 11 ListNode temp = null; 12 temp = cur.next;// 先保存下一个节点 13 cur.next = prev;// 反转 14 // 更新prev、cur位置 15 // prev = cur; 16 // cur = temp; 17 return reverse(cur, temp); 18 } 19 }
1 // 从后向前递归 2 class Solution { 3 ListNode reverseList(ListNode head) { 4 // 边缘条件判断 5 if(head == null) return null; 6 if (head.next == null) return head; 7 8 // 递归调用,翻转第二个节点开始往后的链表 9 ListNode last = reverseList(head.next); 10 // 翻转头节点与第二个节点的指向 11 head.next.next = head; 12 // 此时的 head 节点为尾节点,next 需要指向 NULL 13 head.next = null; 14 return last; 15 } 16 }
本文来自博客园,作者:Ricentch,转载请注明原文链接:https://www.cnblogs.com/cnwsh/p/16572543.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了