剑指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]; 
            }
        }
        // 若给定节点没有下一个节点,则返回NULL
        return 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的栈顶元素压入stack2
                stack2.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) {
        //如果数组为空,返回-1
        if(nums.empty()) return -1; 
        //定义左侧left位置为0
        int left = 0;
        //右侧right位置为n-1
        int 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😊)
如果大家喜欢的话,请动动手点个赞和关注吧,非常感谢你们的支持!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MQL5学习之简单移动平均线MA的编写

昨天还是有点高估自己了&#xff0c;MACD相对较难一点&#xff0c;改学MA的编写&#xff0c;首先明确MA的计算&#xff0c;假如有4个值&#xff0c;p[1&#xff0c;2&#xff0c; 3&#xff0c; 4], period3, 则v[0]p[0], v[1]p[1],v[2](p[0]p[1]p[2])/32, v[3](v[2]*3p[3]-p…

rust多个mod文件引用和文件夹mod使用注意事项

如果mod文件都在同一级目录&#xff0c;则直接使用就可以&#xff0c;因为rust文件都是一个隐藏的mod&#xff0c;但是如果mod文件在另外一个目录下面&#xff0c;就需要在目录下面声明一个mod.rs文件&#xff0c;这样才能将那个目录识别为一个mod&#xff0c;可以在mod.rs里面…

分布式事务详解-高频面试题

分布式事务都有哪些 其实说到分布式事务 我们不得不提事务的分类 事务可以分为本地事务&#xff0c;和分布式事务&#xff0c; 本地事务就是单体系统下基于数据库的ACID来实现的事务&#xff0c;而分布式事务是指在分布式环境下保证多个系统事务一致性的问题 而分布式事务 其…

初阶数据结构之---栈和队列(C语言)

引言 在顺序表和链表那篇博客中提到过&#xff0c;栈和队列也属于线性表 线性表&#xff1a; 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构。线性表在逻辑上是线性结构&#xff0c;也就是说是连…

Java项目:33 基于Java Web的网上拍卖系统(含源码数据库+文档免费送)

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 主要功能包括&#xff1a; 1.前台模块 &#xff08;1&#xff09;普通用户登录/注册。 &#xff08;2&#xff09;分类查看商品(普通商品与促销商品…

集成2.5G/5G/10G高速率网络变压器的RJ45网口连接器产品特点介绍

Hqst华轩盛(石门盈盛)电子导读&#xff1a;集成2.5G/5G/10G高速率网络变压器的RJ45网口连接器产品特点介绍&#xff1a; 第一、 高速率&#xff1a;支持高达2.5Gbps、5Gbps和10Gbps的传输速率&#xff0c;能够满足高带宽的网络应用需求。 第二、 集成2.5G/5G/10G高速率网…

【C++】set、multiset与map、multimap的使用

目录 一、关联式容器二、键值对三、树形结构的关联式容器3.1 set3.1.1 模板参数列表3.1.2 构造3.1.3 迭代器3.1.4 容量3.1.5 修改操作 3.2 multiset3.3 map3.3.1 模板参数列表3.3.2 构造3.3.3 迭代器3.3.4 容量3.3.5 修改操作3.3.6 operator[] 3.4 multimap 一、关联式容器 谈…

Vue开发实例(五)修改项目入口页面布局

修改项目入口 一、创建新入口二、分析代码&#xff0c;修改入口三、搭建项目主页面布局1、Container 布局容器介绍2、创建布局3、布局器铺满屏幕4、创建Header页面5、加入Aside、Main和Footer模块 一、创建新入口 创建新的入口&#xff0c;取消原来的HelloWorld入口 参考代码…

测试需求平台9-Table 组件应用产品列表优化

✍此系列为整理分享已完结入门搭建《TPM提测平台》系列的迭代版&#xff0c;拥抱Vue3.0将前端框架替换成字节最新开源的arco.design&#xff0c;其中约60%重构和20%新增内容&#xff0c;定位为从 0-1手把手实现简单的测试平台开发教程&#xff0c;内容将囊括基础、扩展和实战&a…

文件操作和IO(2):Java中操作文件

目录 一、File的属性 二、File的构造方法 三、File的方法 四、代码示例 1、getName&#xff0c;getParent&#xff0c;getPath方法 2、getAbsolutePath&#xff0c;getCanonicalPath方法 3、exists&#xff0c;isDirectory&#xff0c;createNewFile方法 4、createNewF…

【04】C语言括号匹配问题

欢迎来到土土的博客~&#x1f973;&#x1f973;&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#xff1a;大耳朵土土垚的博客 &#x1f4a5; 所属专栏&#xff1a;C语言系列函数实现 题目描述&#xff1a; 给定一个只包括 ‘(’&#xff0c;‘)’&#xf…

金融业被网络攻击了怎么办,如何治理和风险控制?

近年来&#xff0c;网络罪犯的人数和复杂程度都在增加&#xff0c;网络罪犯的目标锁定变得更具策略性&#xff0c;更加专注于最大效率和获利。随着有关全球网络犯罪的数据持续涌入&#xff0c;可以看出金融服务企业已然成为头号锁定目标。虽然金融服务企业在网络安全人员、工具…

python 基础知识点(蓝桥杯python科目个人复习计划57)

今日复习计划&#xff1a;做题 例题1&#xff1a;笨笨的机器人 问题描述&#xff1a; 肖恩有一个机器人&#xff0c;他能根据输入的指令移动相应的距离。但是这个机器人很笨&#xff0c;他永远分不清往左边还是往右边移动。肖恩也知道这一点&#xff0c;所以他设定这个机器人…

实现数组方法 forEach map filter every

手写forEach Array.prototype.myforEach function (fn, thisValue) {let index 0;let arr thisValue || this;if (typeof fn ! function) {throw new TypeError(fn is not a function)}while (index < arr.length) {if (index in arr) {fn.call (thisValue, arr[index],…

AI入门笔记(三)

神经网络是如何工作的 神经网络又是如何工作的呢&#xff1f;我们用一个例子来解释。我们看下面这张图片&#xff0c;我们要识别出这些图片都是0并不难&#xff0c;要怎么交给计算机&#xff0c;让计算机和我们得出同样的结果&#xff1f;难点就在于模式识别的答案不标准&…

Mybatis_plus-逻辑删除、通用枚举、自动填充、插件等

一、逻辑删除 曾经我们写的删除代码都是物理删除。 逻辑删除&#xff1a;删除转变为更新 ​ update user set deleted1 where id 1 and deleted0 查找: 追加 where 条件过滤掉已删除数据,如果使用 wrapper.entity 生成的 where 条件也会自动追加该字段 ​ 查找: select id,nam…

【NR 定位】3GPP NR Positioning 5G定位标准解读(五)

前言 3GPP NR Positioning 5G定位标准&#xff1a;3GPP TS 38.305 V18 3GPP 标准网址&#xff1a;Directory Listing /ftp/ 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;一&#xff09;-CSDN博客 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;…

扑克牌翻牌记忆小游戏源码

源码由HTMLCSSJS组成&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面 效果预览 下载地址 https://www.qqmu.com/2296.html

Spring学习笔记(七)SpringMVC入门

一.什么是Spring MVC 1.什么是MVC&#xff08;一种思想模式&#xff09; M:模型 V:视图 C:控制器 2、Java EE三层架构 在Java EE开发中&#xff0c;系统经典的三层架构包括表现层、业务层和持久层。 表现层&#xff08;Web层&#xff09;负责接收客户端请求&#xff0c;并…

Linux基本指令(下)

目录 1. less指令 2. head与tail指令 3. find指令 示例 4. grep指令 示例 ​编辑 5. zip/unzip 打包与压缩 示例 ​编辑 6. tar指令 7. find指令&#xff1a; -name 8. echo指令 9. 时间相关的指令 1.在显示方面&#xff0c;使用者可以设定欲显示的格式&#xff…