当前位置:   article > 正文

LeetCode 206. 反转链表(C++实现)_206. 反转链表c+

206. 反转链表c+

一、题目描述

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL


进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

 

二、迭代法

核心思想:

设立三个指针,依次完成反转,在反转之前需要存储下一个结点的地址,否则后面的结点会丢失。

  • head 指针:待反转的结点。
  • next 指针 :后继节点即 next = head ->next。
  • pre 指针:前驱结点,初始化为 NULL,因为头结点的 next 指针最终指向 NULL。

 

首先看一下原链表:     

          

 

总共需要添加两个指针,prenext 。初始化 pre 指向 NULL。

                     

然后就是迭代的步骤,总共四步,顺序一步都不能错。

  •  next 指向 head 的 next,防止原链表丢失

  • head next 从原来链表脱离,指向 pre

  • pre 指向 head

  • head 指向 next

 

一次迭代就完成了,如果再进行一次迭代就变成下边的样子。

可以看到整个过程无非是把旧链表的 head 取下来,添加的新链表头部。接下来就是停止条件了,我们再进行一次循环:

                                

可以发现当 head 或者 next 指向 null 的时候,我们就可以停止了。此时将 pre 返回,便是逆序了的链表了。

  1. /**
  2. * Definition for singly-linked list.
  3. * struct ListNode {
  4. * int val;
  5. * ListNode *next;
  6. * ListNode(int x) : val(x), next(NULL) {}
  7. * };
  8. */
  9. class Solution {
  10. public:
  11. ListNode* reverseList(ListNode* head) {
  12. //空链表和一个结点的链表都无需反转
  13. if(head == NULL || head->next == NULL) return head;
  14. ListNode *pre = NULL,*cur = head, *next = head;
  15. while(cur != NULL){
  16. next = cur->next; //保存cur的next,以防止cur改变后丢失
  17. cur->next = pre; //逐个结点反转
  18. pre = cur; //更新指针位置
  19. cur = next;
  20. }
  21. return pre; //返回反转后的头结点
  22. }
  23. };

关于链表的题,记住更改指向的时候要保存之前的节点,不然会丢失节点。

可参考这篇博客和这篇。其实这种方式就是原地链表的翻转,即没有使用额外的空间。

这种模式的适用场景:

  • 翻转链表中的一段(中等)
  • 翻转每k个元素为一组的子链表段(中等)

 

三、递归法

  • 首先假设我们实现了将单链表逆序的函数,ListNode* reverseList(ListNode* head) ,传入链表头,返回逆序后的链表头。
  • 接着我们确定如何把问题一步一步的化小,我们可以这样想。

     把 head 结点拿出来,剩下的部分我们调用函数 reverseList,这样剩下的部分就逆序了,接着我们把 head 结点放到新链表       的尾部就可以了,这就是整个递归的思想了。

  •  head 结点拿出来,对子链表进行递归:即 ListNode* newList = reverseList(head->next);递归结果如下:

 

  • 此时第一个结点和第二个结点间还没有链接好(也没有断开,只是还未反转),让2结点的 next 指向1,1的 next 指向 null 就ok了。

  • 找到递归出口

     当然就是如果结点的个数是一个,那么逆序的话还是它本身,直接 return 就够了。怎么判断结点个数是不是一个呢?它               的 next 等于 null 就说明是一个了。但如果传进来的本身就是 null,那么直接找它的 next 会报错,所以先判断传进来的是     不是 null ,如果是,也是直接返回就可以了。

  1. /**
  2. * Definition for singly-linked list.
  3. * struct ListNode {
  4. * int val;
  5. * ListNode *next;
  6. * ListNode(int x) : val(x), next(NULL) {}
  7. * };
  8. */
  9. class Solution {
  10. public:
  11. ListNode* reverseList(ListNode* head) {
  12. //第一个条件是判断递归开始,传入的参数的合法性。第二个是递归的终止条件
  13. if(head == NULL || head->next == NULL)
  14. return head;
  15. ListNode* newhead = reverseList(head->next); //开始进行递归,先反转后面的链表,走到链表的末端
  16. head->next->next = head; //第二个结点的 next 指向第一个结点
  17. head->next = NULL; //第一个结点的 next 指向NULL
  18. return newhead;
  19. }
  20. };

 

总结:

对于递归算法,最重要的就是明确递归函数的定义。具体来说,我们的 reverseList 函数定义是这样的:

    输入一个节点 head,将「以 head 为起点」的链表反转,并返回反转之后的头结点

明白了函数的定义,在来看这个问题。比如说我们想反转这个链表:

那么输入 reverseList(head)后 ,会在这里进行递归:

 ListNode* newhead = reverseList(head->next); 

不要跳进递归(你的脑袋能压几个栈呀?),而是要根据刚才的函数定义,来弄清楚这段代码会产生什么结果:

这个  reverseList(head)执行完成后,整个链表就成了这样:

并且根据函数定义,reverseList 函数会返回反转之后的头结点,我们用变量  newhead(在图中是 last)接收了。

现在再来看下面的代码:

  1. head->next->next = head;
  2. head->next = NULL;
  3. return newhead;

神不神奇,这样整个链表就反转过来了!递归代码就是这么简洁优雅,不过其中有两个地方需要注意:

1、递归函数要有 递归基,也就是这句:

  1. if(head == NULL || head->next == NULL)
  2. return head;

意思是如果链表只有一个节点的时候反转也是它自己,直接返回即可。

2、当链表递归反转之后,新的头结点是 last,而之前的 head 变成了最后一个节点,别忘了链表的末尾要指向 null:

head->next = NULL;  

来自文章1,参考链接1

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/编程领航者/article/detail/62882
推荐阅读
相关标签
  

闽ICP备14008679号