剑指offer --- 从尾到头打印链表

目录

前言

一、读懂题目

二、思路分析

三、代码呈现

总结


前言

        当我们需要访问单向链表中特定位置值时,算法复杂度往往是O(n),在得到靠后节点的值时不可避免地从前向后遍历访问链表,那么当应题目要求从尾到头打印链表时,至少对每个链表节点需要访问两次。那么有哪些方法可以实现题目反向输出地要求呢?


一、读懂题目

题目: 输入一个链表的头结点,从尾到头反过来打印出每个结点的值

为了便于分析描述,我们不妨设定链表节点的定义:

typedef struct ListNode
{int val;ListNode* next;
}ListNode;

该题目无论是否使用额外外部空间都可以实现:

1)当我们不使用额外空间时,就必须对链表结构下手,由于单链表的遍历都是由前向后,所以可以在首次遍历链表时将每个节点间的指针指向关系调转,从而接着按照新链表从头到尾打印即可实现题目的倒序打印;

2)当然,一般遍历打印的功能或题目都是更偏向于不希望我们改变原始结构,这时就需要额外空间出面解决问题了。我们想到先遍历到的元素后打印,和栈的特征完全吻合,那么利用栈实现就具有得天独厚的条件。另外,同样可以使用数组,使用新创建的临时链表都可实现类似的存储功能便于后续依次取出作打印,进而解决题目要求。

现在理清可行实现思路后,我们进行思路总结和代码呈现:

二、思路分析

结合实际需求和功能限制,我们全文只讲述不改变原始结构即利用额外空间的方法:

1)利用栈实现

2)利用存值的数组实现(比存节点的数组省空间)

3)利用创建新链表实现

        我们需要认识到,这三点实现方法中,利用栈和新链表的优势在于无需预先知道链表长度(节点数目),而数组若设定为静态数组局限性很大,很容易造成访问越界或空间浪费,所以数组需要设定为动态数组,且是可以随时根据内部元素size占容量capacity的比值来动态拓展数组空间。换而言之,当我们同存储n个节点信息用于打印时,数组类型可以仅存每个节点需要打印的数据类型,无需存储整个结构体变量,当然其他两方法也可以通过存储结构体类型的指针来实现节省空间,但是对每个节点元素打印操作前多了一次解引用操作还是会对效率少有影响,甚至栈也可设定为打印类型并非结构体指针类型,这样可以吸纳数组存储的优点,同时避开创建临时新链表的多余解指针操作。

        所以接下来我们只讨论只存储打印类型的栈来实现逆序输出的功能,剩余两者都是比较好实现的,可以通过栈来对照模拟。

三、代码呈现

首先给出简单单链表这里需要用到的基础函数: 

// 链表基础功能
ListNode* init_List()
{ListNode* head = new ListNode();if (head == nullptr) { return nullptr; }head->val = 0;head->next = nullptr;
}ListNode* appendNode(ListNode* head, int val)
{assert(head);ListNode* node = new ListNode();node->val = val;node->next = nullptr;ListNode* cur = head;while (cur->next != nullptr){cur = cur->next;}cur->next = node;return node;
}void printList(const ListNode* head)     // 注意这里直接设定为const ListNode*
{assert(head);const ListNode* cur = head->next;while (cur != nullptr){printf("%-8d", cur->val);cur = cur->next;}printf("\n");
}

接着我们构建一个简单链表:

ListNode* test_list()
{ListNode* head = init_List();   // 带头节点的单链表appendNode(head, 10);appendNode(head, 9);appendNode(head, 8);appendNode(head, 7);appendNode(head, 6);return head;
}

利用栈实现逆序打印:

void test1()
{// 打印正序对比ListNode* head = test_list();printf("正序打印:");printList(head);// 遍历并将节点用于打印的值填入栈内const ListNode* cur = head->next;stack<int> st;while (cur != nullptr){st.push(cur->val);cur = cur->next;}// 对栈遍历打印printf("逆序打印:");while (!st.empty()){printf("%-8d", st.top());st.pop();}printf("\n");
}

运行结果:

可以看到结果很理想,同时我们没有在首次遍历链表时使用计数器统计节点个数。

我们想到,栈和递归实际上是一回事,那可不可以写出对应的利用递归实现的功能代码呢?

利用递归实现逆序打印:

// 利用递归实现
void traversal_back_printList(const ListNode* node)
{if (node == nullptr) { return; }static const ListNode* const index = node;    // 很重要  通过index标记链表头节点的地址,避免打印其初始化val值0traversal_back_printList(node->next);if (node != index){printf("%-8d", node->val);}else{printf("\n");}
}

需要注意的是,语句 “static const ListNode* const index = node;” 中,static用于保证递归过程中每次进入函数都不会改变index存储的是头结点所在地址的值,避免后续执行递归函数最外层时打印头结点初始化的值;第一个const用于等价接收cosnt ListNode* node节点,第二个const在这里可省略。

运行结果:


总结

在单向链表中,要实现逆序打印链表的要求,可以采用以下几种方法:

  1. 利用栈:遍历链表,将节点的值依次压入栈中,然后再依次弹出栈顶元素,即可实现逆序打印链表的效果。这种方法不需要改变链表的结构,适用于不知道链表长度的情况。

  2. 利用递归:通过递归的方式,先递归访问链表的后续节点,再打印当前节点的值,可以实现逆序打印链表。需要在递归函数中设置一个标记来避免打印头节点的初始化值。

  3. 利用新链表(New List)或数组(Array):遍历链表,将节点的值存储到新链表或数组中,然后按照逆序从新链表或数组中取出并打印节点的值。这种方法需要额外的空间来存储节点的值,适用于不想改变链表结构的情况。

需要注意的是,以上三种方法都可以实现逆序打印链表的功能,选择使用哪种方法取决于具体的需求和限制。根据题目要求或实际情况选择最合适的方法来实现。

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

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

相关文章

网络编程——以太网协议

一&#xff0c;以太网格式 1.1目的地址 这个地址不是IP地址&#xff0c;而是网络中的另一套地址体系&#xff0c;mac 地址&#xff08;物理地址&#xff09;&#xff0c;这个地址的作用与IP地址有一定的重合。因为网络层协议与应用层协议是各自独立被开发的&#xff0c;所以地…

sed 原地替换文件时遇到的趣事

哈喽大家好&#xff0c;我是咸鱼 在文章《三剑客之 sed》中咸鱼向大家介绍了文本三剑客中的 sed sed 全名叫 stream editor&#xff0c;流编辑器&#xff0c;用程序的方式来编辑文本 那么今天咸鱼打算讲一下我在用 sed 原地替换文件时遇到的趣事 sed 让文件属性变了&#xff…

计算机视觉基础——基于yolov5-face算法的车牌检测

文章目录 车牌检测算法检测实现1.环境布置2.数据处理2.1 CCPD数据集介绍2.1.1 ccpd2019及20202.1.2 文件名字解析 2.2数据集处理2.2.1 CCPD数据处理2.2.2 CPRD数据集处理 2.3 检测算法2.3.1 数据配置car_plate.yaml2.3.2 模型配置2.3.3 train.py2.3.4 训练结果 2.4 部署2.4.1 p…

约数之和 (普通快速幂求逆元做法)

假设现在有两个自然数 A 和 B&#xff0c;S 是 AB 的所有约数之和。 请你求出 Smod9901 的值是多少。 输入格式 在一行中输入用空格隔开的两个整数 A 和 B 。 输出格式 输出一个整数&#xff0c;代表 Smod9901 的值。 数据范围 0≤A,B≤5107 输入样例&#xff1a; …

双链表详解(初始化、插入、删除、遍历)(数据结构与算法)

1. 单链表与双链表的区别 单链表&#xff08;Singly Linked List&#xff09;和双链表&#xff08;Doubly Linked List&#xff09;是两种常见的链表数据结构&#xff0c;它们在节点之间的连接方式上有所区别。 单链表&#xff1a; 单链表的每个节点包含两个部分&#xff1a;数…

小程序使用echarts(超详细教程)

小程序使用echarts第一步就是先引用到小程序里面&#xff0c;可以直接从这里下载 文件很多&#xff0c;我们值下载 ec-canvas 就好&#xff0c;下载完成后&#xff0c;直接放在pages同级目录下 index.js 在我们需要的页面的 js 文件顶部引入 // pages/index/index.js impor…

【小白专用】微信小程序个人中心、我的界面(示例一)23.11.04

微信小程序使用button按钮实现个人中心、我的界面&#xff08;示例一&#xff09; 微信小程序个人中心、我的界面&#xff0c;使用button按钮实现界面布局&#xff0c;更好的将分享好友、获取头像等功能展现出来&#xff0c;更多示例界面&#xff0c;请前往我的主页哦。 1、js…

ChatGPT 实际上是如何工作的?

添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; ChatGPT 操作的两个主要阶段 我们再用谷歌来打个比方。当你要求谷歌查找某些内容时&#xff0c;你可能知道它不会——在你提出要求的那一刻——出去搜索整个网络来寻找答案。相反&#xff0c;谷歌会在其数…

COCOS2DX3.17.2 Android升级targetSDK30问题解决方案

一、luajit不兼容问题 不兼容版本&#xff1a;【2.1.0-bate2、2.1.0-bate3都存在异常】 出问题系统&#xff1a;Android11&#xff1b;Android10的系统部分机型有问题&#xff0c;部分机型正常 异常点1&#xff1a;c调用lua接口&#xff0c;pushObjiect的时候crash 异常点2…

下载JMeter

最近准备对接口进行测试&#xff0c;下了JMeter来玩玩 一、下载地址 百度云下载&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1RbMemwzGR6dnDi6BSlPGrw 提取码&#xff1a;uscy 二、开启方式 1.解压后打开目录 2. 进入bin目录 3.双击 jmeter.bat&#xff0c;就可以…

1015. 摘花生

题目&#xff1a; 1015. 摘花生 - AcWing题库 思路&#xff1a;dp 代码&#xff1a; #include<iostream> #include<cstdio> #include<cmath> using namespace std; const int N 110; typedef long long ll; int T, r, c; int num[N][N]; ll dp[N][N];//dp…

Linux学习第27天:Platform设备驱动开发(一): 专注与分散

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 专注与分散是我在题目中着重说明的一个内容。这是今天我们要学习分离与分层概念的延伸。专注是说我们要专注某层驱动的开发&#xff0c;而对于其他层则是芯片厂商…

基于LDA主题+协同过滤+矩阵分解算法的智能电影推荐系统——机器学习算法应用(含python、JavaScript工程源码)+MovieLens数据集(三)

目录 前言总体设计系统整体结构图系统流程图 运行环境模块实现1. 数据爬取及处理2. 模型训练及保存1&#xff09;协同过滤2&#xff09;矩阵分解3&#xff09;LDA主题模型 3. 接口实现1&#xff09;流行电影推荐2&#xff09;相邻用户推荐3&#xff09;相似内容推荐 相关其它博…

ubuntu 20.04 + cuda-11.8 + cudnn-8.6+TensorRT-8.6

1、装显卡驱动 ubuntu20.04 cuda10.0 cudnn7.6.4_我是谁&#xff1f;&#xff1f;的博客-CSDN博客 查看支持的驱动版本&#xff1a; 查看本机显卡能够配置的驱动信息 luhost:/usr/local$ ubuntu-drivers devices/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0 moda…

LeetCode | 203. 移除链表元素

LeetCode | 203. 移除链表元素 OJ链接 这里有两个思路我接下来看 当cur不等于6就一直找&#xff0c;找到了6就删除&#xff0c;但是能不能直接删除&#xff1f;不能&#xff0c;直接free了就找不到下一个了 这个时候我们就要定义next指针&#xff0c;和prev指针&#xff0c…

必须收藏:IPv6核心知识梳理!!(原理+基础配置)

一、概述 由于NAT技术的应用&#xff0c;缓解了IPv4地址不足产生的问题&#xff0c;但是部署IPv6是解决IPv4地址不足的最终方案。当前世界上不同地区对部署IPv6的需求强烈程度不一&#xff0c;且当前IPv4网络仍然占主流地位&#xff0c;因此短时间内IPv6和IPv4将会共存。 IPv4网…

【Midjourney入门教程3】写好prompt常用的参数

文章目录 1、图片描述词&#xff08;图片链接&#xff09;文字描述词后缀参数2、权重划分3、后缀参数版本选择&#xff1a;--v版本风格&#xff1a;--style长宽比&#xff1a;--ar多样性: --c二次元化&#xff1a;--niji排除内容&#xff1a;--no--stylize--seed--tile、--q 4、…

Spring Data Redis + RabbitMQ - 基于 string + hash 实现缓存,计数(高内聚)

目录 一、Spring Data Redis 1.1、缓存功能(分析) 1.2、案例实现 一、Spring Data Redis 1.1、缓存功能(分析) hash 类型存储缓存相比于 string 类型就有更多的更合适的使用场景. 例如,我有以下这样一个 UserInfo 信息 假设这样一个场景就是:万一只想获取其中某一个…

机器视觉行业最大的污点是什么?99%机器视觉公司存在测量项目数据造假,很遗憾,本人不没有恪守技术的本分

机器视觉行业最大的污点是什么&#xff1f;99%机器视觉公司存在测量项目数据造假&#xff0c;很遗憾&#xff0c;本人没有恪守技术的本分。 1%是没做过机器视觉测量项目&#xff0c;我们应该具体分析和具体判断&#xff0c;更应该提高自己的认知能力和技术能力。 那我们​在现场…

根据Word模板,使用POI生成文档

突然想起来有个小作业&#xff1a;需要根据提供的Word模板填充数据。这里使用POI写了一个小demo验证下。 测试用模板&#xff1a; 执行结果 1.引入依赖坐标 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId&…