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

news/2024/7/20 22:49:23 标签: 链表, 算法, 深度优先, 数据结构, leetcode, , 队列

算法题】链表系列

  • 一、从尾到头打印链表
    • 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.niftyadmin.cn/n/467138.html

相关文章

JUC并发工具类--AQS

JUC并发工具类--AQS 管程 — Java同步的设计思想MESA模型 AQS&#xff08;AbstractQueuedSynchronizer&#xff1a;抽象队列同步器&#xff09;AQS简介AQS核心结构AQS内部维护属性state。state三种访问方式 两种资源访问方式AQS实现时主要实现的方法isHeldExclusively()tryAcqu…

AI换脸背后的产业链详解,往后神仙姐姐背后有可能是......

近期在各大平台都可以看到换脸新闻&#xff0c;和大家分享一下最近让我心痛的一张图片 那除了大家用来恶搞之外&#xff0c;AI诈骗的新闻层出不穷。我们国内目前今天最大的是下面这起事件&#xff1a; 而国外&#xff0c;因为技术更加成熟一点&#xff0c;所以被诈骗的金额高达…

制作自定义pfx证书(数字签名)

目录 生成server.key 生成server.crt 生成server.pfx 结果 exe文件签名 生成server.key openssl genrsa -des3 -out server.key 2048 Generating RSA private key, 2048 bit long modulus (2 p

说说你对 SSG 的理解

说说你对 SSG 的理解 SSG&#xff08;Static Site Generation&#xff0c;静态网站生成&#xff09;&#xff09;是一种构建静态网站的技术和方法&#xff0c;在构建时预先生成静态页面&#xff0c;并将这些页面部署到 CDN 或者其他存储服务中&#xff0c;以提升 Web 应用的性…

员工身份管理(EIAM)如何帮助企业降本增效?

随着市场竞争的加剧和经济环境的变化&#xff0c;降本增效成为了现代企业的共同目标。要实现这一目标&#xff0c;企业需要彻底改变传统的生产管理方式&#xff0c;借助数字化技术来实现数据在线、人员在线和行为在线。 数据在线意味着企业的数据可以在多个平台上进行共享、协…

代码随想录算法训练营第四十六天| 139.单词拆分、多重背包、背包问题总结

单词拆分 题目链接&#xff1a;力扣 确定dp数组以及下标的含义 dp[i] : i为字符串长度&#xff0c;dp[i]为true&#xff0c;表示可以拆分为一个或多个在字典中出现的单词。确定递推公式 如果确定dp[j] 是true&#xff0c;且 [j, i] 这个区间的子串出现在字典里&#xff0c;那…

nginx的优化

目录 一 隐藏版本号在网页上面有nginx的版本号会让别人攻击你的服务器 二 nginx的优化之日志分割 三 nginx的优化之页面压缩 四 连接超时 五 nginx的并发设置 七总结:nginx的优化 一 隐藏版本号在网页上面有nginx的版本号会让别人攻击你的服务器 如图所示 第一种方法是关…

6.25~~~~

wmic命令 WMIC&#xff08;Windows Management Instrumentation Command-Line&#xff09;是Windows操作系统的一种命令行工具&#xff0c;用于管理本地或远程的Windows计算机系统。 它可以执行许多任务&#xff0c;包括&#xff1a; 检查系统信息&#xff08;如系统的名称、操…