剑指offer刷题记录Day2 07.数组中重复的数字 ---> 11.旋转数组的最小数字

名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪)
创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)

目录

        • 1、重建二叉树
          • ①代码实现(带注释)
          • ②补充说明(前序遍历和中序遍历重建二叉树的原理)
        • 2、二叉树的下一个结点
          • ①代码实现(带注释)
        • 3、用两个栈实现队列
          • ①代码实现(带注释)
          • ②补充说明(栈和队列)
        • 4、斐波那契数列
          • ①代码实现(带注释)
          • ②补充说明(斐波那契数列)
        • 5、旋转数组的最小数字
          • ①代码实现(带注释)
          • ②补充说明(二分搜索法)

1、重建二叉树

原题链接:07.重建二叉树

在这里插入图片描述

①代码实现(带注释)
#include <unordered_map>
#include <vector>
/*** struct TreeNode {*  int val;*  struct TreeNode *left;*  struct TreeNode *right;*  TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* };*/
/*解题关键:前序序列中的第一个元素总是树的根。
通过这个根,我们可以在中序序列中找到根的索引位置。中序序列又总是被根索引分成两部分:左子树和右子树。
同样地,我们就能够可以确定前序序列中左右子树的边界。最后通过递归这个过程来重建整棵树即可解决问题。*/class Solution {public:unordered_map<int, int>indexMap; // 用于快速访问中序遍历中值的索引的映射TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder,int preorderStart, int preorderEnd, int inorderStart, int inorderEnd) {//若前序遍历的起始位置大于结束位置               if (preorderStart > preorderEnd) {return nullptr; //若返回 nullptr ,表示该子树不存在任何节点。}// 根据前序遍历的特点,我们可以从前序遍历获取根节点值int rootVal = preorder[preorderStart]; // 创建原二叉树的根节点TreeNode* root = new TreeNode(rootVal); // 在中序遍历中找到根的索引下标int rootIndexInInorder = indexMap[rootVal]; // leftElements:左子树中的元素数量int leftElements = rootIndexInInorder - inorderStart;// 递归构建左子树root->left = buildTree(preorder, inorder, preorderStart + 1,preorderStart + leftElements, inorderStart, rootIndexInInorder - 1);// 递归构建右子树root->right = buildTree(preorder, inorder, preorderStart + leftElements + 1,preorderEnd, rootIndexInInorder + 1, inorderEnd);//返回每次所构建子树的根节点return root;}TreeNode* reConstructBinaryTree(vector<int>& preOrder, vector<int>& vinOrder) {int n = preOrder.size();// 填充indexMap以便快速访问中序遍历中的索引/*在重建二叉树的过程中,需要频繁地找到前序遍历中的根节点在中序遍历序列中的位置,这样才能确定左右子树的范围,因此此处使用了映射。如果不使用映射,每次查找都可能需要遍历整个中序遍历序列来找到根节点的位置*/for (int i = 0; i < n; i++) {indexMap[vinOrder[i]] = i;}return buildTree(preOrder, vinOrder, 0, n - 1, 0, n - 1);}
};

在这里插入图片描述

②补充说明(前序遍历和中序遍历重建二叉树的原理)

该题根据前序遍历和中序遍历重建二叉树的原理是什么?

构建二叉树的原理依赖于前序遍历和中序遍历的特性来确定树的结构。前序遍历的顺序是根 左 右。中序遍历的顺序是左 根 右。

根据前序遍历和中序遍历构建二叉树的步骤:

  1. 确定根节点:在前序遍历中,序列的第一个元素总是树的根节点。

  2. 划分左右子树:在中序遍历中,根节点将序列分为两部分,左边是树的左子树,右边是树的右子树。

  3. 递归构建:利用上一步得到的左右子树的中序遍历序列,和从前序遍历序列中提取相应的左右子树序列,递归地重复上述过程构建整棵树。

例如,假设有一棵树的前序遍历序列是 [ A , B , D , E , C , F ] [\text{A}, \text{B}, \text{D}, \text{E}, \text{C}, \text{F}] [A,B,D,E,C,F],中序遍历序列是 [ D , B , E , A , C , F ] [\text{D}, \text{B}, \text{E}, \text{A}, \text{C}, \text{F}] [D,B,E,A,C,F]

  1. 前序遍历的第一个元素是 A A A,所以 A A A 是根节点。

  2. 在中序遍历中, A A A 前面的 [ D , B , E ] [\text{D}, \text{B}, \text{E}] [D,B,E] 是左子树的中序遍历序列, A A A 后面的 [ C , F ] [\text{C}, \text{F}] [C,F] 是右子树的中序遍历序列。

  3. 递归构建左子树和右子树。

    • 对于左子树,前序遍历序列变为 [ B , D , E ] [\text{B}, \text{D}, \text{E}] [B,D,E],中序遍历序列是 [ D , B , E ] [\text{D}, \text{B}, \text{E}] [D,B,E]。重复上述步骤,可以构建出左子树。
    • 对于右子树,前序遍历序列是 [ C , F ] [\text{C}, \text{F}] [C,F],中序遍历序列是 [ C , F ] [\text{C}, \text{F}] [C,F]。重复上述步骤,可以构建出右子树。

通过这种方式,我们就可以准确地重建原始的二叉树结构了。

2、二叉树的下一个结点

原题链接:08.二叉树的下一个结点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

①代码实现(带注释)
/*
struct TreeLinkNode {int val;struct TreeLinkNode *left;struct TreeLinkNode *right;struct TreeLinkNode *next;TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {}
};
*/
class Solution {public:// 定义树节点指针向量nodes 用于存储中序遍历所有节点指针vector<TreeLinkNode*> nodes;// 获取给定节点的下一个节点TreeLinkNode* GetNext(TreeLinkNode* pNode) {TreeLinkNode* root = pNode;// 循环遍历找到根节点while (root->next) root = root->next;// 对树进行中序遍历,并将节点指针存储在nodes向量中InOrder(root); int n = nodes.size(); // 获取遍历后得到的节点总数// 遍历节点,查找给定节点的下一个节点for (int i = 0; i < n - 1; i++) {TreeLinkNode* cur = nodes[i];// 如果当前节点是给定节点if (pNode == cur) { // 则返回下一个节点return nodes[i +1]; }}// 若给定节点没有下一个节点,则返回NULLreturn NULL; }// 中序遍历二叉树void InOrder(TreeLinkNode* root) {if (root == NULL) return;InOrder(root->left); // 遍历左子树//push_back函数可以在Vector向量最后添加一个元素(括号中的参数为要插入的值)nodes.push_back(root); // 访问当前节点InOrder(root->right); // 遍历右子树}
};

在这里插入图片描述

3、用两个栈实现队列

原题链接:09.用两个栈实现队列

在这里插入图片描述

①代码实现(带注释)
class Solution
{
public://使用两个栈实现队列的push操作void push(int node) {stack1.push(node); //直接将元素压入stack1}//使用两个栈实现队列的pop操作int pop() {//如果stack2为空,则将stack1中的所有元素转移到stack2中if (stack2.empty()) {while (!stack1.empty()) {//将stack1的栈顶元素压入stack2stack2.push(stack1.top());//删除stack1的栈顶元素stack1.pop();}}//如果stack2不为空,则直接从stack2中弹出栈顶元素,即队头元素//获取stack2的栈顶元素int head = stack2.top(); //删除stack2的栈顶元素stack2.pop();//返回队头元素return head;}private:stack<int> stack1;//栈1用于插入操作stack<int> stack2;//栈2用于删除操作
};

在这里插入图片描述

②补充说明(栈和队列)

栈(Stack)和队列(Queue)是两种基本的数据结构,它们在处理数据的方式上有着本质的区别:

  • 是一种后进先出的数据结构。
    最后添加进栈的元素将是第一个被移除的。想象一下一叠盘子,你只能从顶部添加或移除盘子。常见的操作包括“push”(添加一个元素到栈顶)和“pop”(移除栈顶元素)。

  • 队列 是一种先进先出的数据结构。
    第一个添加进队列的元素将是第一个被移除的。可以想象成在银行排队,新来的顾客排在队伍的末尾,而服务员从队伍的前端开始服务顾客。常见的操作包括“enqueue”(将一个元素添加到队列末尾)和“dequeue”(移除队列前端的元素)。

4、斐波那契数列

原题链接:10. 斐波那契数列
在这里插入图片描述

①代码实现(带注释)
class Solution {
public:/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param n int整型 * @return int整型*/int Fibonacci(int n) {//处理边界情况if(n <= 1)return n;//初始化前两个斐波那契数	int a=0,b=1;//计算第n项for(int i = 2; i <= n; i++){//更新斐波那契数到第n项int next = a+b;a = b;b = next;}//第n项的斐波那契数列return b;}
};

在这里插入图片描述

②补充说明(斐波那契数列)

斐波那契数列是一种在数学和计算机科学中广泛应用的数列。它由以下递归关系定义:每个数是前两个数的和,其最初两个数通常定义为 F ( 0 ) = 0 , F ( 1 ) = 1 F(0) = 0, F(1) = 1 F(0)=0,F(1)=1。因此,斐波那契数列的开始部分是:

0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , … 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, \ldots 0,1,1,2,3,5,8,13,21,34,

形式上,斐波那契数列可以用数学公式表示为:

F ( n ) = F ( n − 1 ) + F ( n − 2 ) F(n) = F(n-1) + F(n-2) F(n)=F(n1)+F(n2)

其中, n ≥ 2 n \geq 2 n2 F ( 0 ) = 0 F(0) = 0 F(0)=0, F ( 1 ) = 1 F(1) = 1 F(1)=1

特点:除了最开始的两个数字外,每个数字都是其前两个数字的和。

5、旋转数组的最小数字

原题链接:11. 旋转数组的最小数字

在这里插入图片描述

①代码实现(带注释)
class Solution {
public:/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param nums int整型vector * @return int整型*//*由于数组原本是非降序排列的,旋转后,数组被分成两个非降序的子数组。最小值是这两个子数组的分界点。 因此我们可以使用二分搜索法/折半查找法来优化搜索过程,这样时间复杂度就是O(logn)。*/ int minNumberInRotateArray(vector<int>& nums) {//如果数组为空,返回-1if(nums.empty()) return -1; //定义左侧left位置为0int left = 0;//右侧right位置为n-1int right = nums.size() - 1;//如果旋转数组没有旋转(例如[1,2,3,4,5]变成[1,2,3,4,5]),直接返回第一个元素if(nums[left] < nums[right]){return nums[left];}//二分查找while (left < right) {int mid = left + (right - left) / 2;if(nums[mid] > nums[right]){//[mid+1,right]区间left = mid + 1;}else if (nums[mid] < nums[right]) {//[left,mid]区间right = mid;}else {//当中间值和右边值相等时,缩小范围right--;}}//循环结束,此时left == right,指向的就是数组中的最小值return nums[left];}
};

在这里插入图片描述

②补充说明(二分搜索法)

二分搜索法(Binary Search)是一种在有序数组中查找特定元素的高效算法。

原理:将待搜索的数组分成两半,然后根据中间元素与目标值的比较结果来确定下一步搜索的区域,从而逐步缩小搜索范围,直到找到目标值或确定目标值不存在。

二分搜索的基本步骤如下:

  1. 确定搜索区间:初始搜索区间为整个数组。
  2. 找到中间元素:计算搜索区间的中点位置,取出中间元素进行比较。
  3. 比较中间元素与目标值
    • 如果中间元素正好等于目标值,则搜索成功。
    • 如果中间元素小于目标值,则将搜索区间调整为中间元素右侧的子区间,继续搜索。
    • 如果中间元素大于目标值,则将搜索区间调整为中间元素左侧的子区间,继续搜索。
  4. 重复步骤2和3,直到找到目标值或搜索区间为空(表示目标值不存在于数组中)。

二分搜索的效率较高,其时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn),其中 n n n 是数组的长度。因此本题采用二分搜索法,可以帮助我们在查找数据时节省大量的时间。

很感谢你能看到这里,如有相关疑问,还请下方评论留言。
Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)
如果大家喜欢的话,请动动手点个赞和关注吧,非常感谢你们的支持!

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

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

相关文章

【重温设计模式】职责链模式及其Java示例

职责链模式的介绍 在开发过程中&#xff0c;我们经常会遇到这样的问题&#xff1a;一个请求需要经过多个对象的处理&#xff0c;但是我们并不知道具体由哪个对象来处理&#xff0c;或者说&#xff0c;我们希望由接收到请求的对象自己去决定如何处理或者是将请求传递给下一个对…

【深度学习笔记】计算机视觉——锚框

锚框 目标检测算法通常会在输入图像中采样大量的区域&#xff0c;然后判断这些区域中是否包含我们感兴趣的目标&#xff0c;并调整区域边界从而更准确地预测目标的真实边界框&#xff08;ground-truth bounding box&#xff09;。 不同的模型使用的区域采样方法可能不同。 这里…

吴恩达deeplearning.ai:正则化对于偏方差的影响制定用于性能评估的基准

以下内容有任何不理解可以翻看我之前的博客哦&#xff1a;吴恩达deeplearning.ai专栏 这节我们看看正则化系数 文章目录 以线性回归为例交叉验证误差对于确定 λ \lambda λ的作用 指定用于性能评估的基准语音识别的例子 以线性回归为例 让我们举一个例子&#xff1a; 模型&am…

Outlook邮箱IMAP密码怎么填写?账户设置?

Outlook邮箱IMAP密码是什么&#xff1f;Outlook如何设置IMAP&#xff1f; 许多用户会选择通过IMAP协议将邮箱与各种邮件客户端进行连接。而在设置过程中&#xff0c;填写IMAP密码是必不可少的一步。那么&#xff0c;Outlook邮箱的IMAP密码应该如何填写呢&#xff1f;接下来&am…

【Linux】深入理解ls命令

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Linux ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 基本用法 常用选项 示例 高级用法 结语 我的其他博客 前言 在 Linux 系统中&#xff0c;ls 命令是一个强大而又基础的工具&am…

高刷显示器 - HKC VG253KM

&#x1f525;&#x1f525; 今天来给大家揭秘一款电竞神器 - HKC VG253KM 高刷电竞显示器&#xff01;这款显示器可是有着雄鹰展翅般的设计灵感&#xff0c;背后的大鹏展翅鹰翼图腾让人过目难忘。那么&#xff0c;这款显示器到底有哪些过人之处呢&#xff1f;一起来看看吧&…

【MySQL】基于Docker搭建MySQL一主二从集群

本文记录了搭建mysql一主二从集群&#xff0c;这样的一个集群master为可读写&#xff0c;slave为只读。过程中使用了docker&#xff0c;便于快速搭建单体mysql。 1&#xff0c;准备docker docker的安装可以参考之前基于yum安装docker的文章[1]。 容器相关命令[2]。 查看正在…

Pod和容器设计模式

为什么需要 Pod&#xff1b; Pod 的实现机制&#xff1b; 详解容器设计模式。 一、为什么需要 Pod 容器的基本概念 现在来看第一个问题&#xff1a;为什么需要 Pod&#xff1f;我们知道 Pod 是 Kubernetes 项目里面一个非常重要的概念&#xff0c;也是非常重要的一个原子调…

144. 二叉树的前序遍历

给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,2,3]示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[]示例 3&#xff1a; 输入&#xff1a;root [1] 输出&am…

SpringCloud(18)之Sleuth +Zipkin链路追踪

一、Zipkin介绍 Zipkin是一个开放源代码分布式的跟踪系统&#xff0c;它可以帮助收集服务的时间数据&#xff0c;以解决微服务架构中的延迟问 题&#xff0c;包括数据的收集、存储、查找和展现。每个服务向zipkin报告计时数据&#xff0c;zipkin会根据调用关系通 过Zipkin UI…

1.2 在卷积神经网络中,如何计算各层感受野的大小

1.2 在卷积神经网络中&#xff0c;如何计算各层感受野的大小 分析与解答&#xff1a; 在卷积神经网络中&#xff0c;由于卷积的局部连接性&#xff0c;输出特征图上的每个节点的取值&#xff0c;是由卷积核在输入特征图对应位置的局部区域内进行卷积而得到的&#xff0c;因此这…

【笔试强训错题选择题】Day5.习题(错题)解析

文章目录 前言 错题题目 错题解析 总结 前言 错题题目 1. ​ ​ 2. 3. ​ 4. ​ 5. ​ 错题解析 1. 移位运算符的使用 2. 3. 4. 5. 总结

如何用TCC实现分布式事务?

TCC事务介绍 TCC&#xff08;Try-Confirm-Cancel&#xff09;是除可靠消息队列以外的另一种常见的分布式事务机制&#xff0c;它是由数据库专家帕特 赫兰德&#xff08;Pat Helland&#xff09;在2007年撰写的论文《Life beyond Distributed Transactions: An Apostate’s Op…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的体育赛事目标检测系统(Python+PySide6界面+训练代码)

摘要&#xff1a;开发和研究体育赛事目标检测系统对于增强体育分析和观赏体验至关重要。本篇博客详细讲述了如何运用深度学习技术构建一个体育赛事目标检测系统&#xff0c;并提供了完整的实现代码。系统基于先进的YOLOv8算法&#xff0c;对比了YOLOv7、YOLOv6、YOLOv5的性能&a…

爬虫入门到精通_实战篇7(Requests+正则表达式爬取猫眼电影)_ 抓取单页内容,正则表达式分析,保存至文件,开启循环及多线程

1 目标 猫眼榜单TOP100&#xff1a;https://www.maoyan.com/board 2 流程框架 抓取单页内容&#xff1a;利用requests请求目标站点&#xff0c;得到单个网页HTML代码&#xff0c;返回结果。正则表达式分析&#xff1a;根据HTML代码分析得到电影名称,主演,上映时间,评分,图片…

C语言中的分支和循环语句:从入门到精通

分支和循环语句 1. 前言2. 预备知识2.1 getchar函数2.2 putchar函数2.3 计算数组的元素个数2.4 清屏2.5 程序的暂停2.6 字符串的比较 3. 结构化3.1 顺序结构3.2 分支结构3.3 循环结构 4. 真假性5. 分支语句&#xff08;选择结构&#xff09;5.1 if语句5.1.1 语法形式5.1.2 else…

Java网络通信UDP

目录 网络通信基础 UDP通信 服务器 1.想要使用UDP通信 要先打开DatagramSocket文件 端口号可以手动指定或系统随机分配 2.阻塞等待接收客户端数据&#xff1b;创建DatagramPacket接收客户端传来的数据 3.处理客户端传来的数据&#xff0c;并进行业务处理&#xff08;这里…

MySQL 教程 2.4

MySQL UNION 操作符 本教程为大家介绍 MySQL UNION 操作符的语法和实例。 描述 MySQL UNION 操作符用于连接两个以上的 SELECT 语句的结果组合到一个结果集合&#xff0c;并去除重复的行。 UNION 操作符必须由两个或多个 SELECT 语句组成&#xff0c;每个 SELECT 语句的列数…

Python降维数据库之umap使用详解

概要 在数据科学和机器学习领域,数据通常是高维度的,而高维度数据不仅难以可视化,还会增加建模的复杂性。降维是一种处理高维数据的关键技术,而Python UMAP(Uniform Manifold Approximation and Projection)是一种强大的降维工具,它在保留数据结构的同时,将高维数据映…

【IDEA+通义灵码插件】实现属于你的大模型编程助手

目录 1.前言 2.下载安装 3.解释代码 4.生成单元测试 5.生成注释 6.智能补全 1.前言 大模型到底该以一种什么方式落地&#xff0c;从而嵌入我们的工作当中&#xff0c;助力我们工作效率的提升&#xff0c;其实最好的方式也许就是虚拟助手的方式&#xff0c;就像钢铁侠的&…