【算法题】链表系列之从尾到头打印链表、重建二叉树、用两个栈实现队列

news2024/11/25 10:57:31

【算法题】链表系列

  • 一、从尾到头打印链表
    • 1.1、题目描述
    • 1.2、递归法
    • 1.3、栈(stack)
  • 二、重建二叉树
    • 2.1、题目描述
    • 2.2、前置知识:
    • 2.3、分治算法
    • 2.4、小结
  • 三、用两个栈实现队列
    • 3.1、题目描述
    • 3.2、双栈法
    • 3.3、小结
  • 总结

一、从尾到头打印链表

1.1、题目描述

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

示例 1:

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

1.2、递归法

利用递归,先走至链表末端,回溯时依次将节点值加入列表 ,这样就可以实现链表值的倒序输出。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void dfs(ListNode* head,vector<int> &nums)
    {
        if(head->next==NULL)
        {
            nums.emplace_back(head->val);
            return;
        }
        
        dfs(head->next,nums);
        nums.emplace_back(head->val);
    }
    vector<int> reversePrint(ListNode* head) {
        vector<int> nums;
        if(head==NULL)
            return nums;
        dfs(head,nums);
        return nums;

    }
};

时间复杂度O(N): 遍历链表,递归 N次。
空间复杂度O(N): 系统递归需要使用 O(N)的栈空间。

1.3、栈(stack)

栈的特点是后进先出,即最后压入栈的元素最先弹出。利用栈的这一特点,使用栈将链表元素顺序倒置。从链表的头节点开始,依次将每个节点压入栈内,然后依次弹出栈内的元素并存储到数组中。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        vector<int> nums;
        if(head==NULL)
            return nums;
        stack<int> st;
        ListNode* cur=head;
        while(cur)
        {
            st.push(cur->val);
            cur=cur->next;
        }

        while(!st.empty())
        {
            nums.emplace_back(st.top());
            st.pop();
        }
        return nums;
    }
};

时间复杂度O(N)。
空间复杂度O(N)。

二、重建二叉树

2.1、题目描述

输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

示例 1:

Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]

在这里插入图片描述

示例 2:

Input: preorder = [-1], inorder = [-1]
Output: [-1]

来源:力扣(LeetCode)

2.2、前置知识:

二叉树前序遍历的顺序为:

  1. 先遍历根节点;
  2. 随后递归地遍历左子树;
  3. 最后递归地遍历右子树。

二叉树中序遍历的顺序为:

  1. 先递归地遍历左子树;
  2. 随后遍历根节点;
  3. 最后递归地遍历右子树。

对于任意一颗树而言,前序遍历的形式总是:

[ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]
即根节点总是前序遍历中的第一个节点。

中序遍历的形式总是:

[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]

2.3、分治算法

分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。即一种分目标完成程序算法,简单问题可用二分法完成。

(1)算法思路:

  1. 通过【前序遍历列表】确定【根节点 (root)】;
  2. 将【中序遍历列表】的节点分割成【左分支节点】和【右分支节点】;
  3. 递归寻找【左分支节点】中的【根节点 (left child)】和 【右分支节点】中的【根节点 (right child)】。

(2)实现逻辑:

  1. 先保存前序遍历列表到全局变量中,方便使用。
  2. 创建一个字典(比如C++的unordered_map容器),将中序遍历列表的元素和下标保存下来;方便计算左右子树在前序遍历列表中的开始位置和长度。
  3. 递归函数实现,参数是根节点在前序遍历列表的下标、左子树开始节点在前序遍历列表的下标、右子树结束节点在前序遍历列表的下标;递归的终止条件是左子树的开始下标大于右子树的结束下标(即 left>right)。
  4. 递归函数要创建根节点,前序遍历列表的开始位置即是根节点的值,然后依次递归左子树和右子树。
  5. 字典保存了中序遍历列表的下标,可以利用它来计算左右子树的长度。

在这里插入图片描述

代码实现:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
private:
    unordered_map<int,int> map;// 存储中序遍历数组的元素和下标,用于确定左右子树的元素数量/长度。
    vector<int> preorder;     // 保存中序遍历数组,方便递归时依据索引查看先序遍历的值
    TreeNode* myBuildTree(int root,int left,int right)
    {
        if(left>right)
            return nullptr;
        // 创建根节点
        TreeNode* rtn=new TreeNode(preorder[root]);
        // 划分根节点、左子树、右子树
        int idx=map[preorder[root]];
        // 递归左子树
        rtn->left=myBuildTree(root+1,left,idx-1);
        // 递归右子树
        rtn->right=myBuildTree(root+idx-left+1,idx+1,right);
        // 回溯返回根节点
        return rtn;
    }
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n=preorder.size();
        if(n==0)
            return nullptr;
        int i=0;
        this->preorder=preorder;
        for(i=0;i<n;i++)
            map[inorder[i]]=i;
            
        return myBuildTree(0,0,n-1);
    }
};

执行用时:8 ms, 在所有 C++ 提交中击败了95.43%的用户
内存消耗:24.8 MB, 在所有 C++ 提交中击败了63.43%的用户

时间复杂度 O(N) 。

空间复杂度 O(N) 。

2.4、小结

利用前序遍历和中序遍历的特性,重建二叉树;根据分治法实现递归运算。

三、用两个栈实现队列

3.1、题目描述

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )。

示例 1:

输入:
[“CQueue”,“appendTail”,“deleteHead”,“deleteHead”,“deleteHead”]
[[],[3],[],[],[]]
输出:[null,null,3,-1,-1]

示例 2:

输入:
[“CQueue”,“deleteHead”,“appendTail”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]

来源:力扣(LeetCode)

3.2、双栈法

将一个栈当作输入栈,用于压入 appendTail 传入的数据;另一个栈当作输出栈,用于 deleteHead 操作。

每次deleteHead 时,若输出栈为空则将输入栈的全部数据依次弹出并压入输出栈,这样输出栈从栈顶往栈底的顺序就是队列从队首往队尾的顺序。

代码实现:

class CQueue {
private:
    stack<int> st;// 输入栈
    stack<int> st2;//输出栈
public:
    CQueue() {
        
    }
    
    void appendTail(int value) {
        st.push(value);
    }
    
    int deleteHead() {
        
        if(st2.empty())
        {
            if(st.empty())
                return -1;
            while(!st.empty())
            {
                st2.push(st.top());
                st.pop();
            }
        }

        int ret=st2.top();
        st2.pop();

        return ret;
    }
};

时间复杂度:O(1)。

空间复杂度:O(n)。

3.3、小结

双栈法。

  1. 一个栈作为入栈、一个栈作为出栈;
  2. 当出栈为空时,再将入栈所有数据弹出,压到输出栈;
  3. 如果两个栈都为空,则返回-1。

总结

一定要做好总结,特别是当没有解出题来,没有思路的时候,一定要通过结束阶段的总结来反思犯了什么错误。解出来了也一定要总结题目的特点,题目中哪些要素是解出该题的关键。不做总结的话,花掉的时间所得到的收获通常只有 50% 左右。

在题目完成后,要特别注意总结此题最后是归纳到哪种类型中,它在这种类型中的独特之处是什么。

在这里插入图片描述

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

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

相关文章

从零开始 Spring Boot 48:JPA Hibernate

从零开始 Spring Boot 48&#xff1a;JPA & Hibernate 图源&#xff1a;简书 (jianshu.com) 对象关系映射&#xff08;ORM&#xff09;是将Java对象转换为数据库表的过程。换句话说&#xff0c;这允许我们在没有任何SQL的情况下与关系数据库进行交互。Java Persistence AP…

Redis入门(4)-list

redis中list数据会按照插入顺序进行排序&#xff0c;其底层是一个无头结点的双向链表&#xff0c;因此表头和表尾的操作性能较高&#xff0c;但中间元素操作性能较差。 1.lpush key element [element ] 从表头插入元素 lpush nosql redis hbase lpush nosql mongdb2.lrange…

信息熵、条件熵、信息增益

一、信息熵 其中&#xff1a; &#xff1a;样本属于第i个类别的概率 &#xff1a;总样本数 &#xff1a;集合 中属于第 个类别的样本个数 二、条件熵 其中&#xff1a; &#xff1a;属性 的取值个数 &#xff1a;选出属性 取值等于 的样本集合 三、信息增益 信息增…

安全防御 --- IPSec理论(03)

DPD 死亡对等体检测&#xff08;dead peer detection&#xff09;&#xff0c;检查对端IKE SA&#xff08;iskmp sa&#xff09;是否存在。当隧道出现异常&#xff0c;检测出异常重新发起协商&#xff0c;维持隧道。 作用&#xff1a;DPD解决隧道黑洞问题&#xff0c;用于检查…

Docker搭建Hadoop集群

目录 1.拉取centos镜像 2.基础镜像配置(基于centos构建hadoopbase镜像) 3.集群环境配置 1.创建3个容器 2.配置网络 3.配置主机和ip的映射关系 4.配置3个节点的免密登录 4.搭建hadoop集群 1.安装hadoop 2.修改配置文件 3.分发Hadoop及配置文件my_env.sh 5.启动集群 …

使用CLion创建Cmake项目,使用GoogleTest和GoogleMock对代码进行测试

文章目录 1、环境准备2、CLion创建项目3、编写测试用例4、复杂测试用例 1、环境准备 注意版本匹配&#xff0c;我本地是g 8.1.0 的&#xff0c;最开始装了GoogleTest最新版1.10.0结果发现不能用&#xff0c;又回去下载旧的版本。g 8.1.0 应该可以使用 Google Test 1.8.1 版本。…

HLW8032交流电参数模块采样原理讲解

目录 一、文章概述 二、采样说明 1.采样方式 1.1隔离采样 电流型电压互感器&#xff1a; 1.2非隔离采样 2.采样范围 3.采样精度 三、数据说明 四、HLW8032模块说明 一、文章概述 本博文主要讲解HLW8032交流电参数模块的采样原理以及设计说明&#xff0c;才疏学浅&am…

微服务系列文章之 SpringCloud面试

1、什么是 Spring Cloud&#xff1f; Spring cloud 流应用程序启动器是基于 Spring Boot 的 Spring 集成应用程序&#xff0c;提供与外部系统的集成。Spring cloud Task&#xff0c;一个生命周期短暂的微服务框架&#xff0c;用于快速构建执行有限数据处理的应用程序。 2、使…

团体程序设计天梯赛-练习集L2篇⑦

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;Hello大家好呀&#xff0c;我是陈童学&#xff0c;一个与你一样正在慢慢前行的普通人。 &#x1f3c0;个人主页&#xff1a;陈童学哦CSDN &#x1f4a1;所属专栏&#xff1a;PTA &#x1f381;希望各…

奇异值分解MVDR算法功率谱估计MATLAB完整程序分享

clear ;close all;clc; 产生输入信号 N 1024; %样本点数 snr[20 25 30]; %信噪比 n0:N-1; %数据轴 g100; %蒙特卡诺仿真次数 M14; %阶数 Pmvdr_szeros(3,1024); %存放MVDR谱 signal1 exp(1i*0.1…

Oracle-DG备库应用查询不到数据问题处理

背景: 一套准备上线的Oracle 11G RAC主备集群&#xff0c;应用报告说部分模块测试发现在备库查不到新插入的数据&#xff0c;而且问题发生的频率很高&#xff0c;需确认主备之间同步是否存在问题&#xff0c;此套主备之间同步采用SYNCAFFIRM模式 问题分析: 接到问题之后&#…

2022(二等奖)C2464植物保护管理系统

作品介绍 一、需求分析 1. 应用背景 森林是陆地生态系统的主体&#xff0c;是人类生存与发展的物质基础。以森林为主要经营对象的林业&#xff0c;不仅承担着生态建设的主要任务&#xff0c;而且承担着提供多种林产品的重大使命。进入21世纪&#xff0c;人类正在继农业文明和…

libevent(12)bufferevent的基础知识

一、bufferevent的基本概念 bufferevent 是 libevent 中的一个事件缓冲 IO&#xff0c;内部实现了基本 socket recv/send 操作 &#xff0c;用户只需要调用 bufferevent 的 API 即可实现数据的读写。 &#xff08;1&#xff09;缓冲区&#xff1a;每个 bufferevent 都有一个读…

Effective第三版 中英 | 避免使用终结方法和清理方法

文章目录 Effective第三版前言避免使用终结方法和清理方法&#xff08;Avoid finalizers and cleaners&#xff09;总结 Effective第三版 前言 大家好&#xff0c;这里是 Rocky 编程日记 &#xff0c;喜欢后端架构及中间件源码&#xff0c;目前正在阅读 effective-java 书籍。…

基于SpringBoot+vue的家乡特色推荐系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

【C语言督学训练营 第十五天】常用的查找算法----顺序查找、二分查找、二叉排序树

文章目录 前言一、顺序查找1,思想2.代码实战 二、二分查找1.思想2.代码实战 三、二叉排序树1.建树思想2.删除节点思想3.代码实战 考研真题实战 前言 本篇博客会介绍到数据结构中常用到的查找算法&#xff0c;合理的使用查找算法可以让我们很轻松找到自己想要的答案。本小节必须…

Java异常面试题

什么是Java异常机制 Java异常机制是java语言为我们提供一种异常处理机制&#xff0c;在java语言中&#xff0c;异常本身是一个类&#xff0c;产生异常就是创建异常对象并抛出这个异常对象&#xff0c;程序发生异常情况之后程序会抛出封装了错误信息的异常对象&#xff0c;程序…

python-线性规划

线性规划&#xff1a;定义&#xff1a;1 线性规划&#xff08;Linear programming,简称LP&#xff09;&#xff0c;是运筹学中研究较早、发展较快、应用广泛、方法较成熟的一个重要分支&#xff0c;是辅助人们进行科学管理的一种数学方法&#xff0c;是研究线性约束条件下线性…

WPF 零基础入门笔记(2):控件模板+数据模版

文章目录 文章合集地址WPF控价模版解决重复嵌套标签书写的问题实战 WPF数据绑定解决界面和业务数据沟通的问题 WPF数据模版数据模板解决数据的样式设置&#xff08;以CellTemplate为例&#xff09;数据模板和控件模板的区别ItemTemplate 元素模板ItemTemplate是用于绝大部分控件…

CAT1模块 EC800M HTTP使用总结记录

分享记录一下 CAT1 模块EC800 HTTP 协议使用流程 ...... by 矜辰所致目录 前言一、基础说明1.1 CAT1 与 4G1.2 EC800M 模块1.3 HTTP 二、开始使用2.1 硬件设计部分2.2 模块上电流程2.3 PDP 上下文2.3.1 什么是 SGSN 和 GGSN &#xff1f; 三、 HTTP 流程3.1 客户端3.1.1 PDP 上…