【剑指offer - C++/Java】3、从尾到头打印链表

学习交流加

  • 个人qq:
    1126137994
  • 个人微信:
    liu1126137994
  • 学习交流资源分享qq群:
    962535112

牛客网题目链接:
从尾到头打印链表

文章目录

  • 题目描述
  • 1、递归解法
    • 1.1、 递归解法一
      • java代码:
      • C++代码
      • 分析:
    • 1.2 递归解法二
      • java代码:
      • C++代码
  • 2、使用栈
    • java代码
    • C++代码
  • 总结

题目描述

输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

本题较为简单。有两种解法:递归和使用栈循环。

1、递归解法

递归解法,也可以有两种写法。

1.1、 递归解法一

先上代码,下面给解释:

java代码:

import java.util.ArrayList;
public class Solution {ArrayList<Integer> arrayList=new ArrayList<Integer>();//注意这个ArrayList必须在方法体外定义public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {if(listNode!=null){this.printListFromTailToHead(listNode.next);arrayList.add(listNode.val);}return arrayList;}
}

C++代码

class Solution {
public:vector<int> res;//注意这个vector必须在成员函数外定义vector<int> printListFromTailToHead(ListNode* head) {if(head!=nullptr){this->printListFromTailToHead(head->next);res.push_back(head->val);}return res;}
};

分析:

这种递归的写法,可以这么想:因为想要从尾到头打印链表,那么如果此链表为空,则直接返回。如果只有一个节点,则将此节点打印。否则,递归打印头结点以后的节点,直到递归到最后一个节点,那么,因为最后一个节点后面为空,所以返回上一级调用,上一级调用后的代码为res.push_back(head->val);,此时的head刚好指向的最后的节点,将最后一个节点先放到数组。然后又返回到上一级的调用,将倒数第二个节点放到数组中。以此类推,直到将第一个节点放到数组中。

我们可以看如下图示来分析上述递归的过程:

在这里插入图片描述
1.刚开始head指向节点1,不为空,将head指针指向下一个节点,然后递归调用函数到第2步

在这里插入图片描述
2.此时head依然不为空,将head指针指向下一个节点,然后递归调用函数到第3步

在这里插入图片描述
3.此时head依然不为空,将head指针指向下一个节点,然后递归调用函数到第4步

在这里插入图片描述
4.此时head依然不为空,将head指针指向下一节点,然后递归调用函数第5步

在这里插入图片描述
5.此时head指向空,递归结束,那么递归结束后,就需要返回到上一个递归的步骤,上一步骤是步骤4,那么执行递归语句的下一句:res.push_back(head->val)
(C++代码),节点4的值放到数组中。然后再返回到步骤3,将节点3的值放入数组。以此类推,最终从后往前将所有链表节点的值放到数组中。

注意:此种递归方法中,返回数组的定义一定要放在函数体外面定义,如果放在函数体内定义,那么由于递归的层级不同,就会导致每次递归的时候都会重新定义一个数组,导致最后我们只能将第一个节点的值返回,结果肯定是错的。在下一种递归的方法中,返回数组是可以定义在函数体内的,原因见下面解释。

1.2 递归解法二

java代码:

import java.util.ArrayList;
public class Solution {public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {ArrayList<Integer> resArray = new ArrayList<>();if(listNode != null){if(listNode.next != null)resArray = printListFromTailToHead(listNode.next);resArray.add(listNode.val);}return resArray;}
}

C++代码

class Solution {
public:vector<int> printListFromTailToHead(ListNode* head) {vector<int> res;if(head!=nullptr){if(head->next!=nullptr)res = printListFromTailToHead(head->next);res.push_back(head->val);}return res;}
};

分析: 这种递归,实际上更好理解。首先,如果链表位空,则直接返回空数组。如果链表只有一个节点,则直接返回这个节点的值。如果这个链表不止一个节点,那么递归调用函数得到除了头结点外后面的链表,得到后面的链表的从后往前打印(放到数组)的结果,然后再将之前的头结点打印(放到数组末尾)。那么,最终得到的结果就是整个链表的从后往前打印的结果。

可以简单的以下面图示理解:
在这里插入图片描述 1.
刚开始,head指向头结点,它后面还有节点(具体有几个都无所谓)。将head指向下一个节点,然后递归调用函数。

在这里插入图片描述
2.现在是这样的,从第1步过来,该递归调用函数求现在的这个链表的从后往前打印的结果。我们把现在这剩下的具有三个节点的链表,看成一个黑匣子整体,我的函数要对这个黑匣子求解它的倒序,然后这个函数返回了该链表的倒序的结果{4,3,2}。(其实这里面有好几个递归的过程,但是我们不必将所有的递归过程想出来,只需要知道,现在我们可以将后面剩余的链表看成一个新链表,然后得到它的倒序就行)

3.得到后面的链表的倒序后,是存入数组的,最后我们再将头结点的值,放到数组末尾res.push_back(head->val);,就可以得到{4,3,2,1},这正是我们要的结果。

注意:这里的递归解法中,返回数组的定义,可以放到函数体内,也可以放到函数体外。与第一种递归的不同之处在于,这里我们可以将以下两句话,放在同一个调用栈内看待,即
res =
printListFromTailToHead(head->next);这句话得到后面的链表的倒序后,就执行res.push_back(head->val);它们的执行,可以看成是在一个函数调用栈内进行的。而第一种递归调用中如果将返回数组定义在函数体内,每一个调用都是在一个新的函数调用栈,导致每一个函数栈中,都是一个新的返回数组,最终只能得到最顶层的函数栈中的数组,也就是链表的头结点的值。

2、使用栈

递归解法虽然写法简单,但是递归调用需要重新开辟函数调用栈,开销比较大。正常的解法实际上是使用栈来解决(栈这种数据结构本身就与递归有很大的联系)。

可以遍历链表,每遍历一个节点就将节点的值压入到栈中,直到遍历完链表。最后再依次将栈中的元素弹出到返回的数组中。

java代码

import java.util.Stack;
import java.util.ArrayList;
public class Solution {public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {Stack<Integer> st = new Stack<>();ListNode pListNode = listNode;while(pListNode != null){st.push(pListNode.val);pListNode = pListNode.next;}ArrayList<Integer> resArray = new ArrayList<>();while(!st.isEmpty()){resArray.add(st.pop());}return resArray;}
}

C++代码

class Solution {
public:vector<int> printListFromTailToHead(ListNode* head) {vector<int> res;stack<int> st;ListNode* p = head;while(p != nullptr){st.push(p->val);p = p->next;}int len = st.size();for(int i = 0;i<len;++i){res.push_back(st.top());st.pop();}return res;}
};

总结

  • 理解递归的精髓,上述两种递归的不一样之处
  • 理解递归与栈的关系

学习探讨加:
qq:1126137994
微信:liu1126137994

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/423108.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络协议之http和tcp思维导图

http协议和tcp协议之前学的整理一部分&#xff0c;后续更新

【剑指offer - C++/Java】4、重建二叉树

牛客网题目链接&#xff1a;重建二叉树 文章目录0 题目描述&#xff1a;1、题目分析2、代码2.1、java代码2.2 C代码3、总结0 题目描述&#xff1a; 输入某二叉树的前序遍历和中序遍历的结果&#xff0c;请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字…

Windows下Android开发环境 搭建

1、 下载Android SDK &#xff08;1&#xff09;官网下载解压后即可用。 在http://androidappdocs.appspot.com/sdk/index.html 下选择 合适自己的平台下载&#xff0c;下载后解压 运行 【SDK Setup.exe】 出现在线安装包。 会出现“Failed to fetch URL https://dl-ssl.google…

【OS学习笔记】二 汇编语言和汇编软件

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 上一篇文章讲解了处理器&#xff0c;内存&#xff0c;和指令。学习了Intel 8086处理器的相关知识&#xff0c;如Intel通用寄存器的作用&#xff0c;程…

【软件开发底层知识修炼】一 深入浅出处理器之一 微处理器与微控制器

学习交流加&#xff08;可免费帮忙下载CSDN资源&#xff09;&#xff1a;个人微信&#xff1a; liu1126137994学习交流资源分享qq群1&#xff08;已满&#xff09;&#xff1a; 962535112学习交流资源分享qq群2&#xff1a; 780902027 深入浅出处理器这几篇文章可能你觉得对软件…

【软件开发底层知识修炼】二 深入浅出处理器之二 中断的概念与意义

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 上一篇文章我们学习了微处理器与微控制器的区别。点击链接查看上一篇文章的内容&#xff1a;微处理器与微控制器 本篇文章我们学习中断的概念与意义。…

推荐学习-数据结构与算法之美

推荐一个学习资源&#xff1a;数据结构与算法之美。主要包括以下几个学习内容&#xff1a; 20个经典数据结构与算法100个真实项目场景案例文科生都能看懂的算法手绘图解轻松搞定BAT的面试通关秘籍 作者&#xff1a;王争 前谷歌工程师 内容很强。扫描我的二维码买的话&#x…

【软件开发底层知识修炼】三 深入浅出处理器之三 内存管理与内存管理单元(MMU)

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 上一篇文章学习了中断的概念与意义&#xff0c;以及中断的应用-断点调试原理。点击链接复习上一篇文章&#xff1a;中断的概念与意义 本片文章继续学…

hbase思维导图

持续更新中 hbase思维导图 感谢分享1. hbase 概述2. hbase 数据模型3. hbase 架构4. hbase 的读写流程5. hbase 优化1. hbase 概述 2. hbase 数据模型 3. hbase 架构 4. hbase 的读写流程 5. hbase 优化

安卓从入门到进阶推荐学习方法与书籍整理(pdf)

前言&#xff1a;技术书阅读方法论 一.速读一遍&#xff08;最好在1~2天内完成&#xff09; 人的大脑记忆力有限&#xff0c;在一天内快速看完一本书会在大脑里留下深刻印象&#xff0c;对于之后复习以及总结都会有特别好的作用。 对于每一章的知识&#xff0c;先阅读标题&…

SharePoint中的权限体系

1.MOSS中的权限结构 MOSS中的权限结构主要有三部分&#xff1a;网站权限&#xff0c;列表权限&#xff0c;个人权限。 网站权限由18种如下图&#xff1a; 列表权限由12种&#xff0c;如下图&#xff1a; 个人权限由三种&#xff0c;如下图&#xff1a; 2.权限级别 上面提供…

MobaXterm_Personal_20.1最简单方式链接阿里云服务器

MobaXterm_Personal_20.1链接购买的阿里云服务器1.登录到阿里云首页2.阿里云首页3. 找到 服务器实例4. 重置密码5. 配置 MobaXterm_Personal_20.15.1 MobaXterm_Personal_20.1介绍6. 输入重置的密码1.登录到阿里云首页 2.阿里云首页 3. 找到 服务器实例 4. 重置密码 5. 配置 Mo…

机器学习和python学习之路精心整理技术书从入门到进阶

“机器学习&#xff0f;深度学习并不需要很多数学基础&#xff01;”也许你在不同的地方听过不少类似这样的说法。对于鼓励数学基础不好的同学入坑机器学习来说&#xff0c;这句话是挺不错的。不过&#xff0c;机器学习理论是与统计学、概率论、计算机科学、算法等方面交叉的领…

FusionChart完全入门手册8

动画样式类型 FusionCharts做得最好的事情之一就是图表动画序列&#xff0c;这是用户静静乐道的。默认情况下&#xff0c;FusionCharts仅在数据绘制&#xff08;柱状图、饼图、折线图等&#xff09;时候使用动画。但是FusionCharts V3不限制你的想象力。 使用动画样式,你可以…

算法与数据结构+一点点ACM从入门到进阶吐血整理推荐书单(珍藏版)

学习交流加&#xff08;可免费帮忙下载CSDN资源&#xff09;&#xff1a; 个人微信&#xff1a; liu1126137994 学习交流资源分享qq群1&#xff08;已满&#xff09;&#xff1a; 962535112 学习交流资源分享qq群2&#xff1a; 780902027 前言&#xff1a;技术书阅读方法论…

工作中使用的工具

工作中使用的工具分享一下后续持续更新

【软件开发底层知识修炼】四 深入浅出处理器之四 结合高速缓存以及TLB与虚拟内存

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 文章目录0、回顾1、高速缓存&#xff08;Cache&#xff09;的引入2、利用TLB加速地址翻译3、Cache与物理内存是如何映射的3.1、直接映射法&#xff1a…

研发中会使用到的工具

研发中会使用到的工具分享一下后续持续更新

【软件开发底层知识修炼】五 gcc-C语言编译器

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 文章目录1、GCC与gcc2、gcc的幕后工作3、实用的gcc选项3.1、预处理选项-解决宏错误3.2、-S参数-辅助编写汇编程序的好方法3.3、获取系统头文件路径3.4…