当前位置: 首页 > news >正文

剑指offer之重建二叉树、用两个栈实现队列、旋转数组的最小数字

重建二叉树

题目描述:

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

示例

输入

[1,2,3,4,5,6,7],[3,2,4,1,6,5,7]

返回值

{1,2,5,3,4,6,7}

方法:递归算法

二叉树的遍历知识:

二叉树的前序遍历:根->左->右

二叉树的中序遍历:左->根->右

二叉树的后序遍历:左->右->根

建立树的相关步骤:

struct TreeNode{
  int vall;
  TreeNode *left;
  TreeNode *right;
  TreeNode(int x):val(x), left(nullptr), right(nullptr){}
};
//建树伪代码
TreeNode* build(1...){
  if (2...) return nullptr;
  TreeNode *root = new TreeNode(3...);
  root->left = build(4...); //递归建立左子树
  rott->right = build(5...); //递归建立右子树
  return root;
}

通过记住建树伪代码中的1到5步,就可以建立一颗二叉树。那么1到5分别要填入什么呢?

假设:1. 数组vector 中的元素为树中的节点。

  1. 如果数组为空,则返回nullptr
  2. 新创建一个根结点的值
  3. 左子树的数组元素
  4. 右子树的数组元素

本段递归代码的理解就是,不断的创建二叉树中的根节点。因为二叉树中的左右子树节点也可以视为新树的根节点,所以不断的递归调用创建根节点,就可以创建一颗二叉树。具体化的步骤代码:

// 假设元素在数组v中,并且头结点的下标为 root_index, first < root_index < last,
// 这里只是会的容易讲解
TreeNode* build(int first, int last) {
    if (first > last) return nullptr;
    TreeNode *root = new TreeNode(v[root_index]);
    root->left = build(first, root_index - 1);
    root->right = build(root_index + 1, last);
    return root;
} 

那么这个root_index哪里来?根据先验知识了解到树的前序遍历数组的第一个元素便为二叉树的根节点,而树的中序遍历数组在根节点元素的左边的所有元素是左子树的序列,在右边的所有元素是右子树的序列。

以本题例子为例:
前序遍历序列: {1,2,4,7,3,5,6,8}
中序遍历序列: {4,7,2,1,5,3,8,6}
第一步:根结点为1
第二步:根结点在中序遍历序列中下标为3的位置,那么[0…2]就为左子树,[4…7]就为右子树
只不过现在build()参数中为2个数组,道理一样,维护2个数组的下标就行了。
那么现在这道题就可以解决了。

class Solution{
public:
		TreeNode* reBuild(vector<int>& pre, int pre_left, int pre_right, vector<int>& vin, int vin_left, int vin_right){//1. 需要建立树的数组
      if(pre_left < pre_right) return nullptr; //2. 数组判空
      TreeNode* root = new TreeNode(pre[pre_left]); //3. 创建根节点
      for(int i = 0; i <= vin_right; ++i){
        if(vin[i] == pre[pre_left]){   // 找到中序遍历中的root_index
          root->left = reBuild(pre, pre_left+1, pre_left+i-vin_left, vin, vin_left, i-1);//4. 左子树
          root->right = reBuild(pre, pre_left+i-vin_left+1, pre_right, vin, i+1, vin_right);//5. 右子树
        }
      }
      return root;
    }  
  	TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin){
      return reBuild(pre, 0, pre.size()-1, vin, 0, vin.size()-1);
    }
}

其中递归左子树中,前序遍历的结尾为:pre_left + root_index - vin_left 的解释:

root_index - vin_left为根结点左边有几个元素
pre_left + root_index - vin_left 为从pre_left开始往后推这么多元素便是前序遍历的左子树序列。

用两个栈实现队列

题目描述

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

先验知识

  1. 入栈的元素是先进后出;
  2. 入队的元素是先进先出。

因此使用两个栈就可以实现一个队列操作,先将元素压入stack1,再将元素出栈,将出栈元素依次入stack2,最后将元素出栈,便实现了队列效果。

方法思路

push操作就只作用在stack1上,pop操作要分一下类:当stack2为空时,就将stack1中的元素转移到stack2中,然后对stack2进行pop操作;当stack2不为空时,直接pop

class Solution{
public:
  	void push(int node){
      stack1.push(node);
    }
  	int pop(){
      if(stack2.empty()){
        while(!stack1.empty()){
          stack2.push(stack1.pop());
          stack1.pop;
        }
      }
      int res = stack2.pop();
      stack2.pop();
      return res;
    }
private:
  stack<int> stack1;
  stack<int> stack2;
}

旋转数组的最小数字

题目描述

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

示例

输入

[3,4,5,1,2]

返回值

1

对于这种有序或部分有序的数组中查找某个元素,一般考虑使用二分法;或者说如果能够明确二分后,能够清楚的判断答案位于二分的某一侧,就可以使用二分法。

这种二分查找难就难在,arr[mid]跟谁比。我们的目的是:当进行一次比较时,一定能够确定答案在mid的某一侧。一次比较为 arr[mid]跟谁比的问题。一般的比较原则有:

  • 如果有目标值target,那么直接让arr[mid]和target比较即可。
  • 如果没有目标值,一般可以考虑端点

这里我们把target 看作是右端点,来进行分析,那就要分析以下三种情况,看是否可以达到上述的目标。

  1. 情况1, arr[mid] > target: 4, 5, 6, 1, 2, 3

    • arr[mid] 为 6, target为右端点 3, arr[mid] > target,说明[first … mid] 都是 >= target 的,因为原始数组是非递减,所以可以确定答案为 [mid+1…last]区间,所以 first = mid + 1
  2. 情况2,arr[mid] < target: 5, 6, 1, 2, 3, 4

    • arr[mid]为1,target右端点为4,arr[mid] < target,说明答案肯定不在[mid+1…last],但是arr[mid] 有可能是答案,或者答案在arr[mid]之前,所以答案在[first, mid]区间,所以last = mid;
  3. 情况3,arr[mid] == target:

    • 如果是 1 0 1 1 1, arr[mid] = target = 1, 显然答案在左边
    • 如果是 1 1 1 0 1, arr[mid] = target = 1, 显然答案在右边
      所以这种情况,不能确定答案在左边还是右边,那么就让last = last - 1;慢慢缩少区间,同时也不会错过答案。

如图

img

代码:

class Solution{
public:
  int minNumberInRotateArray(vector<int> rotateArray){
    if(rotateArray.size() == 0) return 0;
    int first = 0, last = rotateArray.size()-1;
    while(first < last){ // 剩下最后一个元素,即为答案
      int mid = floor((first + last)/2)
      if (rotateArray[last] < rotateArray[mid]){ // 情况1
        	first = mid + 1;
      }
      else if(rotateArray[last] > rotateArray[mid]){//情况2
        	last = mid;
      }
      else{//情况3
        	--last;
      }
    }
    return rotateArray[first];
  }
}

复杂度分析

时间复杂度:二分,所以为O(longN), 但是如果是[1, 1, 1, 1],会退化到O(n)
空间复杂度:没有开辟额外空间,为O(1)

相关文章:

  • 剑指offer(c++)-01.数组中重复的数字
  • 剑指offer(c++)-02.二维数组中的查找
  • 操作系统-01.操作系统的运行机制和体系结构
  • 剑指offer(c++)-03.替换空格
  • 剑指offer(c++)-04.从尾到头打印链表
  • 操作系统-02.中断与异常及系统调用
  • 操作系统-03.进程的定义、组成、组织方式、特征
  • 操作系统-04.进程的状态与切换
  • 操作系统-05.进程控制
  • 操作系统-06.进程通信
  • 操作系统-06.线程概念、多线程模型
  • 操作系统-07.处理机调度概念、层次
  • 设计模式-01.面向对象七大设计原则
  • C++面向对象高级开发-01.C++ 类相关解析
  • C++面向对象高级开发-02.堆、栈与内存管理
  • 时间复杂度分析经典问题——最大子序列和
  • 「面试题」如何实现一个圣杯布局?
  • 【个人向】《HTTP图解》阅后小结
  • android高仿小视频、应用锁、3种存储库、QQ小红点动画、仿支付宝图表等源码...
  • cookie和session
  • es6--symbol
  • JSDuck 与 AngularJS 融合技巧
  • js操作时间(持续更新)
  • js递归,无限分级树形折叠菜单
  • JS题目及答案整理
  • Linux学习笔记6-使用fdisk进行磁盘管理
  • Phpstorm怎样批量删除空行?
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • SQL 难点解决:记录的引用
  • Sublime text 3 3103 注册码
  • 分类模型——Logistics Regression
  • 马上搞懂 GeoJSON
  • 小程序开发之路(一)
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • #免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (3)选择元素——(17)练习(Exercises)
  • (33)STM32——485实验笔记
  • (附源码)springboot金融新闻信息服务系统 毕业设计651450
  • (附源码)springboot社区居家养老互助服务管理平台 毕业设计 062027
  • (附源码)计算机毕业设计SSM教师教学质量评价系统
  • (一)【Jmeter】JDK及Jmeter的安装部署及简单配置
  • (转)jdk与jre的区别
  • (转载)PyTorch代码规范最佳实践和样式指南
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .Net core 6.0 升8.0
  • .NET/C# 使用反射调用含 ref 或 out 参数的方法
  • .netcore 如何获取系统中所有session_如何把百度推广中获取的线索(基木鱼,电话,百度商桥等)同步到企业微信或者企业CRM等企业营销系统中...
  • .NET导入Excel数据
  • .NET开源项目介绍及资源推荐:数据持久层 (微软MVP写作)
  • .NET框架设计—常被忽视的C#设计技巧
  • /run/containerd/containerd.sock connect: connection refused
  • @Mapper作用
  • [ NOI 2001 ] 食物链