【C++】面试101,二叉搜索树的最近公共祖先,在二叉树中找到两个节点的最近公共祖先,序列化二叉树,重建二叉树,输出二叉树的右视图,组队竞赛,删除公共字符
目录
1.二叉搜索树的最近公共祖先
2.在二叉树中找到两个节点的最近公共祖先
3.序列化二叉树
4.重建二叉树
5.输出二叉树的右视图
6.组队竞赛
7.删除公共字符
1.二叉搜索树的最近公共祖先
这是一个简单的问题,因为是二叉搜索树(有序),首先看看p,q是不是都在左子树?右子树
如果不是,那么现在的根就是两个的最近公共祖先
int lowestCommonAncestor(TreeNode* root, int p, int q) {// write code here、if(!root) return -1;if(p<root->val &&q<root->val) return lowestCommonAncestor(root->left, p, q);else if(p>root->val && q>root->val) return lowestCommonAncestor(root->right, p, q);//在两个子树else return root->val;}
2.在二叉树中找到两个节点的最近公共祖先
最一般的树怎么找最近公共祖先?
最直观的方法肯定是和上面搜索树的想法一样,先看看这两个节点是不是在一侧的子树里
但是这不是搜索树,不可以比较节点的值来判断在左子树/右子树
没有条件创造条件也要上,谁让我想不出来更好的思路....
bool Isintree(TreeNode* root, int x) {if (!root) return false;return root->val == x||Isintree(root->left, x) ||Isintree(root->right, x);}int lowestCommonAncestor(TreeNode* root, int o1, int o2) {if (!root) return -1;if (root->val == o1 || root->val == o2) return root->val;bool Iso1inleft = Isintree(root->left, o1);bool Iso1inright = !Iso1inleft;bool Iso2inleft = Isintree(root->left, o2);bool Iso2inright = !Iso2inleft;if ((Iso1inleft && Iso2inright) || (Iso1inright && Iso2inleft))return root->val;if (Iso1inleft && Iso2inleft)return lowestCommonAncestor(root->left, o1, o2);elsereturn lowestCommonAncestor(root->right, o1, o2);}
欣喜若狂的提交,超时!!!!!!!!!!!!!!!!!!!!!!!!!!!!
为什么?
因为在子树里搜索很浪费时间
那么只能换方法
记得我们怎么做相交链表的题目吗?
想找到链表的交点,那么我们计算两个链表的长度,让更长的链表先走差距(两个链表长度差)步,然后挨个比较目前的val值,找到一样的就输出
那你看这个树枝是不是很像链表
所以思路就有了
bool FindPath(TreeNode* root, stack<int>& s, int o) //找到这个节点的所有祖先
{if (!root) return false; //遇到空节点肯定不是祖先s.push(root->val); //先把这个节点入栈if (root->val == o) //找到了目标节点,直接返回return true;if (FindPath(root->left, s, o)) return true; //在左子树里找到他,返回trueif (FindPath(root->right, s, o)) return true; //右子树中找到他,trues.pop(); //走到这说明左/右子树没找到这个节点 ,这个root就不是目标节点的祖先,popreturn false; //返回在这个路径没找到
}
int lowestCommonAncestor(TreeNode* root, int o1, int o2) {// 两个栈记录他们的祖先列表stack<int> s1;stack<int> s2;FindPath(root, s1, o1);FindPath(root, s2, o2);while (s1.size() < s2.size()) //长度不一样s2.pop(); //更长的popwhile (s1.size() > s2.size())s1.pop();while (s1.top() != s2.top()) //当前不是公共祖先 {//都把这个节点pops1.pop();s2.pop();}return s1.top(); //或者s2.top() 都一样的值
}
3.序列化二叉树
写两个函数,让字符串可以构建成二叉树,二叉树也可以写成字符串
void dfs_Serialize(TreeNode* root, string& s)
{if (!root)//如果是空节点,直接写一个#,空节点的子节点被省略,所以直接返回{s += '#';return;}s += to_string(root->val) + ','; //把非空节点的val变成字符,写入到字符串dfs_Serialize(root->left, s); //左子树dfs_Serialize(root->right, s);//右子树
}
char* Serialize(TreeNode* root) { //用二叉树写字符串数组string s; //首先写成字符串dfs_Serialize(root, s); //深度优先遍历(前序)char* ans = new char[s.size() + 1]; //最后应该输出数组形式,先开数组(加上1写'\0')memcpy(ans, s.c_str(), s.size()); //把字符串拷贝给数组ans[s.size()] = '\0';//终止符return ans;
}
TreeNode* dfs_Deserialize(char*& s) //把字符串变二叉树,注意这个字符串在递归的时候不是每次从头开始,所以&
{if (*s == '#')//是空节点的标志,直接返回nullptr{s++; //s向后走一个,因为这个字符对应的节点已经被表示完了return nullptr;}int val = 0; //字符串对应的节点valwhile (*s != ',') //看样例,字符串以,分割{val = val * 10 + (*s - '0'); //万一不是个位数,需要这样算好每一位s++;//s往后走}s++; //把逗号跳过TreeNode* root = new TreeNode(val); //用val创建节点root->left = dfs_Deserialize(s); //左节点的创建root->right = dfs_Deserialize(s);//右节点return root; //返回根
}
TreeNode* Deserialize(char* str) {return dfs_Deserialize(str);
}
还有第二个方法,借助队列,把字符串构建成二叉树(这个显然没有上面的方法快)
char* Serialize(TreeNode* root) { //用层序来写字符串if (!root) return nullptr; //如果是空节点,不需要记录他的孩子string str; queue<TreeNode*> que;que.push(root); //先把根入队while (!que.empty()){auto node = que.front(); //取队头元素que.pop();if (node) //取出的节点不是空{str += to_string(node->val) + ","; //字符串保存这个节点val对应的字符,并且在字符串里面写入分割符号que.push(node->left);//带入左孩子que.push(node->right);}else {str += "#";//是空,写成#}}auto res = new char[str.size() + 1]; //和上一方法这里的用处一样strcpy(res, str.c_str());res[str.size()] = '\0';return res;
}
int Getinteger(char* str, int& k) //把字符转化成数字
{int val = 0;while (str[k] != ',' && str[k] != '\0' && str[k] != '#'){val = val * 10 + str[k] - '0';++k;}if (str[k] == '\0') return -1;if (str[k] == '#') val = -1;k++;return val;
}
TreeNode* Deserialize(char* str) {if (!str) return nullptr;int k = 0; //记录字符串的下标queue<TreeNode* > que;auto head = new TreeNode(Getinteger(str, k)); //一定要把字符串下标也传,要不然找不到位置了que.push(head); //把头结点入队while (!que.empty()){auto node = que.front();//队头que.pop();//这里的反序列化是根据前序用字符串构建二叉树int leftval = Getinteger(str, k); //把字符串转换成左节点的valif (leftval != -1)//如果左孩子的val是-1,代表是空节点{//不是空auto nodeleft = new TreeNode(leftval) ;//创建节点node->left = nodeleft; //node和左孩子链接que.push(nodeleft);//链接好的节点入队}//右节点同样的方法int rightval = Getinteger(str, k);if (rightval != -1){auto noderight = new TreeNode(rightval);node->right = noderight;que.push(noderight);}}return head; //返回头结点
}
4.重建二叉树
之前就说过三种遍历方式:前中后,只有前+后不能构建出二叉树
同一个颜色是同一次递归
最后根据 这个规律可以得到二叉树
TreeNode* _reConstructBinaryTree(vector<int>& pre, vector<int>& vin, int& k, //k一定要给&,在每个递归里是看得见改变的int begin, int end) {if (begin > end) return nullptr; //区间不成立,说明为空int rooti = begin; //rooti记录中序节点里面根的下标while (rooti <= end) { //根的下标肯定在合法区间里if (vin[rooti] == pre[k]) //在中序数组里找到根,k记录每个根在前序里的下标break;++rooti;}//rooti代表根节点的下标//[begin,rooti-1] rooti [rooti+1,end]TreeNode* root = new TreeNode(pre[k++]); //创建节点root->left =_reConstructBinaryTree(pre, vin, k, begin, rooti - 1); //左孩子在左区间找(因为中序是左根右)root->right =_reConstructBinaryTree(pre, vin, k, rooti + 1, end);return root;
}
TreeNode* reConstructBinaryTree(vector<int> pre, vector<int> vin) {int k = 0;return _reConstructBinaryTree(pre, vin, k, 0, pre.size() - 1);
}
5.输出二叉树的右视图
根据给的前序和中序构建树,然后层序思想,每次到一层的最左侧,直接入ans
TreeNode* buildtree(vector<int>& xianxu, vector<int>& zhongxu, int& k, //建树(和前面重建二叉树是一样的)int begin, int end) {if (begin > end) return nullptr;int rooti = begin;while (rooti <= end) {if (xianxu[k] == zhongxu[rooti])break;++rooti;}TreeNode* root = new TreeNode(xianxu[k++]);root->left = buildtree(xianxu, zhongxu, k, begin, rooti - 1);root->right = buildtree(xianxu, zhongxu, k, rooti + 1, end);return root;
}
void bfs(TreeNode* root, vector<int>& ans) { //层序+找最右节点if (!root) return;queue<TreeNode*> q;q.push(root);while (!q.empty()) {int size = q.size(); //当前层数的sizewhile (size--) {TreeNode* node = q.front();q.pop();if (size == 0) ans.push_back(node->val); //直到size==0,找到最右节点if (node->left) q.push(node->left);if (node->right) q.push(node->right);}}
}
vector<int> solve(vector<int>& xianxu, vector<int>& zhongxu) {// write code herevector<int> ans;int k = 0;TreeNode* root = buildtree(xianxu, zhongxu, k, 0, xianxu.size() - 1);bfs(root, ans);return ans;
}
但是这样做总是觉得很麻烦,所以去评论区看来一下大佬的代码
void _solve(vector<int> xianxu, vector<int> zhongxu, vector<int>& ans,int level) {if (xianxu.empty()) return; //如果前序是空的,证明根是空if (ans.size() < level) ans.push_back(xianxu[0]); //ans里面的元素个数一定是等于层数,如果小于,直接把当前的根入elseans[level - 1] = xianxu[0]; //level应该也是ans的个数,最后一个元素下标就是level-1int headpos = 0; //还是找中序里的根while (zhongxu[headpos] != xianxu[0])headpos++;_solve(vector<int>(xianxu.begin() + 1, xianxu.begin() + headpos + 1), vector<int>(zhongxu.begin(), zhongxu.begin() + headpos), ans, level + 1);_solve(vector<int>(xianxu.begin() + headpos + 1, xianxu.end()),vector<int>(zhongxu.begin() + headpos + 1, zhongxu.end()), ans, level + 1);
}
vector<int> solve(vector<int>& xianxu, vector<int>& zhongxu) {vector<int> ans;_solve(xianxu, zhongxu, ans, 1);return ans;
}
前面的不解释了,主要看大佬的递归
之前我们构建二叉树的时候只想着找到根节点的下标,但是居然没有发现前序begin()+1~begin()+headpos整个闭区间是左子树的所有节点!!!!!!!!!!!!!!!
也就是说我们之前写的构造二叉树的所有步骤都写麻烦了
来看前序+中序怎么构建二叉树
TreeNode* _reConstructBinaryTree(vector<int> pre, vector<int>vin) {if (pre.empty()) return nullptr;int rooti = 0;while (vin[rooti] != pre[0]) {++rooti;}TreeNode* root = new TreeNode(pre[0]);root->left =_reConstructBinaryTree(vector<int>(pre.begin() + 1, pre.begin() + rooti + 1),vector<int>(vin.begin(), vin.begin() + rooti));root->right =_reConstructBinaryTree(vector<int>(pre.begin() + 1 + rooti, pre.end()),vector<int>(vin.begin() + rooti + 1, vin.end()));return root;
}
TreeNode* reConstructBinaryTree(vector<int> pre, vector<int> vin) {return _reConstructBinaryTree(pre, vin);
}
6.组队竞赛
这个题不在面试必刷101,作为补充
一道简单的数学题
其实没啥好说的,先排序,最小的节点就是前n个,n是队伍的个数,也就代表了如果想让所有队伍的能力值和最大,必须每个队伍有一个重排v之后的前n个数之一
比如
n=2
排序之后前两个数1 2,应该给每个队伍分配一个,不能让1 2同时出现在一个队伍里
所以sum求和的时候i从n开始,不应该直接挨着取n之后的元素,因为你把最大的一些数全部没取到,应该跳两个取一次
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{int n;while (cin >> n){vector<int> v;v.resize(3 * n);for (int i = 0; i < v.size(); i++){cin >> v[i];}sort(v.begin(), v.end());long long sum = 0;for (int i = n;i < v.size(); i += 2){sum += v[i];}cout << sum<<endl;}return 0;
}
7.删除公共字符
#include <iostream>
#include <string>using namespace std;int main(){string s1, s2;getline(cin, s1);getline(cin, s2);for (int i= 0; i < s1.size(); i++) {if (s2.find(s1[i]) == -1) //在s2中找不到这个字符则输出cout << s1[i];}return 0;}
这个不是我自己最初写的,一开始是把s2里面先遍历然后映射到数组里,但是显然慢
这个直接在s2里面找s1是不是在很方便
相关文章:

【C++】面试101,二叉搜索树的最近公共祖先,在二叉树中找到两个节点的最近公共祖先,序列化二叉树,重建二叉树,输出二叉树的右视图,组队竞赛,删除公共字符
目录 1.二叉搜索树的最近公共祖先 2.在二叉树中找到两个节点的最近公共祖先 3.序列化二叉树 4.重建二叉树 5.输出二叉树的右视图 6.组队竞赛 7.删除公共字符 1.二叉搜索树的最近公共祖先 这是一个简单的问题,因为是二叉搜索树(有序)&am…...

Java常见面试题及解答
Java常见面试题及解答1 面向对象的三个特征2 this,super关键字3 基础数据类型4 public、protected、default、private5 接口6 抽象类6.1 抽象类和接口的区别7 重载(overload)、重写(override)8 final、finalize、final…...

【Docker】镜像的原理定制化镜像
文章目录镜像是什么UnionFS(联合文件系统)Docker镜像加载原理制作本地镜像 docker commit -m"提交的描述信息" -a"作者" 容器ID 要创建的目标镜像名:[标签名]案例演示ubuntu安装vim本地镜像发布到阿里云本地镜像发布到阿里云流程将本…...

国内版的ChatGPT弯道超车的机会在哪里?
前言 从去年11月最后一天ChatGPT诞生,截至目前,ChatGPT的热度可谓是爆了。众所周知,ChatGPT是美国“开放人工智能研究中心”研发的聊天机器人程序,它是一个人工智能技术驱动的自然语言处理工具,它能够通过学习和理解人…...

【字符串】
string1.char str[]类型fgets(s,10000,stdin) cin.getline(cin,10000) strlen(str)sizeof 求静态数组长度2.string类型getline(cin,a) cin.getline(cin,10000) str.lenth()str.size()cin 遇到空格就停止3.gets 函数char str[20];gets(str);4.puts 函数puts(str) 相当于 cout<…...

加载驱动之后无法在/dev/下生成vedio0
前言 环境介绍: 1.编译环境 Ubuntu 18.04.5 LTS 2.SDK orangepi Linux 5.4 SDK 3.uboot v2020.04 4.gcc gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf 5.单板 orangepi pc plus 一、问题 继上一篇成功加载gc2035.ko文件之后,理论上…...

Java之类与对象(图文结合)
目录 一、面向对象的初步认知 1、什么是面向对象 2、面向对象与面向过程 二、类定义和使用 1、简单认识类 2、类的定义格式 3、练习 (1)定义一个狗类 (2)定义一个学生类 三、类的实例化 1、什么是实例化 2、类和对象的…...

基于 VCS-NLP 的动态低功耗仿真验证介绍
🔥点击查看精选 IC 技能树系列文章🔥 🔥点击进入【芯片设计验证】社区,查看更多精彩内容🔥 📢 声明: 🥭 作者主页:【MangoPapa的CSDN主页】。⚠️ 本文首发于CSDN&#…...

ESP32-S3 自带usb/jtag初步尝试体验
一、背景 最近在做一台小机器,设备初步规划使用几个实体按钮,这样方便用户戴手套操作。但因为设备有一些需要配置的参数,有需要配备屏幕。但是开发时间比较紧。考虑再三,决定先在初步配备一个简单的控制箱。控制箱上不带屏幕。后…...

前端性能优化总结
前端性能优化是指在设计和开发网站时,采取一些措施来提升网站的性能。这对用户来说是非常重要的,因为高性能的网站可以带来更好的用户体验,同时也有助于提升搜索引擎排名。一、常见前端性能优化措施常见的前端性能优化方法有:压缩…...

React(四) ——hooks的使用
🧁个人主页:个人主页 ✌支持我 :点赞👍收藏🌼关注🧡 文章目录⛳React Hooks💸useState(保存组件状态)🥈useEffect(处理副作用)🔋useCallback(记忆函数&#…...

iphone手机热点卡顿多次断连解决办法
文章目录解决方法检查一下几个地方:1.个人热点是否打开2.查看手机是否为4g3.查看手机的最大兼容性开关是否关闭!!很重要解决方法 检查一下几个地方: 1.个人热点是否打开 这个个人热点容易自动断开,先检查一下是不是…...

设置Typora图床(Github)
PicGo,Github,Typora Nodejs下载: Node.js PicGo下载: GitHub - Molunerfinn/PicGo: A simple & beautiful tool for pictures uploading built by vue-cli-electron-builder 选择downloads或release. 然后进行安装。 Gith…...

jira提交bug规范
一、目的 1)方便开发人员根据bug描述快速进行定位问题原因,减少沟通成本。 2)规范bug编写,可以提现测试团队的专业性、严谨性。 3)可以帮助产品、项目经理及其它人员快速了解bug。 二、说明 本文档主要描述了技术产…...

【数据结构】链表相关题目(中档题)
🚀write in front🚀 📜所属专栏:初阶数据结构 🛰️博客主页:睿睿的博客主页 🛰️代码仓库:🎉VS2022_C语言仓库 🎡您的点赞、关注、收藏、评论,是对…...

小菜鸟Python历险记:(第四集)
今天写的文章是记录我从零开始学习Python的全过程。在Python中函数是非常重要的,这里也可以称为方法。在前面分享的几篇文章中用到的方法有print(),str(),int().这些都是方法,而除了上面写的这几种内置方法以外,我们也可以自己在程序中自定义…...

字符函数和字符串函数【下篇】
文章目录🎖️1.函数介绍📬1.8. strstr📬1.9. strtok📬1.10. strerror📬1.11. memcpy📬1.12. memmove📬1.13. memcmp📬1.14. memset🎖️1.函数介绍 📬1.8. st…...

【CSS】盒子模型内边距 ② ( 内边距复合写法 | 代码示例 )
文章目录一、内边距复合写法1、语法2、代码示例 - 设置 1 个值3、代码示例 - 设置 2 个值4、代码示例 - 设置 3 个值5、代码示例 - 设置 4 个值一、内边距复合写法 1、语法 盒子模型内边距 可以通过 padding-left 左内边距padding-right 右内边距padding-top 上内边距padding-…...

uni-app ——使用uploadFile上传多张图片
前言:最近的工作中出现了一个功能点,具体写法我在前面的文章中已经阐述过,不过之前的情况是上传图片调用后端的一个接口,整个表单页面提交的时候调用的是另一个接口,我也从中学到了另外的一种方法,写到这里…...

Linux - 进程控制(进程等待)
进程等待必要性之前讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力&#…...

Python 可视化最频繁使用的10大工具
今天介绍Python当中十大可视化工具,每一个都独具特色,惊艳一方。 文章目录Matplotlib技术提升SeabornPlotlyBokehAltairggplotHoloviewsPlotnineWordcloudNetworkxMatplotlib Matplotlib 是 Python 的一个绘图库,可以绘制出高质量的折线图、…...

Windows与Linux端口占用、查看的方法总结
Windows与Linux端口占用、查看的方法总结 文章目录Windows与Linux端口占用、查看的方法总结一、Windows1.1Windows查看所有的端口1.2查询指定的端口占用1.3查询PID对应的进程1.4查杀死/结束/终止进程二、Linux2.1lsof命令2.2netstat命令一、Windows 1.1Windows查看所有的端口 …...

48天强训 Day1 JavaOj
48天强训 & Day1 & JavaOj 1. 编程题1 - 组队竞赛 组队竞赛_牛客笔试题_牛客网 (nowcoder.com) 1.1 读题 1.2 算法思想基础 我们应该尽量的让每一个队伍的中间值都最大化~我们应该尽量的让每一个队伍的最小值都足够小~前33%的不应该都作为每个队伍的最大值~ 接下来…...

崩溃的一瞬间
——我可以忍受黑暗,除非我从未见过光明 原来,人真的会崩溃,如果不是昨夜的眼泪,我到现在还不知道人为什么会在一瞬间崩溃。 刚和认识不久的女孩子聊完天准备入睡。忽然想到自己可能过几个月就要离开这座待了仅一年多的城市…...

13回归网络:HTTP/2是怎样的网络协议?
本篇文章我们先放下实践,回归网络,深入gRPC底层的HTTP/2协议,去探究一下框架底层网络协议的原理,提升对高性能网络协议的认知,相信读完这篇文章以后,我们就可以了解HTTP/2有哪些优势,为什么gRPC要使用HTTP/2作为底层的传输协议。 在众多研究HTTP/2的博客和资料中,最具…...

CSS学习笔记——基础选择器,字体属性,文本属性,三种样式表
文章目录基础选择器标签选择器类选择器多类名使用方式id选择器通配符选择器字体属性字体系列字体字号字体粗细文字样式复合属性文本属性文本颜色对齐文本装饰文本文本缩进行间距CSS的三种样式表行内样式表(行内式)内部样式表(嵌入式ÿ…...

第十四届蓝桥杯三月真题刷题训练——第 16 天
目录 第 1 题:英文字母 问题描述 输入格式 输出格式 样例输入 1 样例输出 1 样例输入 2 样例输出 2 评测用例规模与约定 运行限制 代码: 第 2 题:单词分析 题目描述 输入描述 输出描述 输入输出样例 运行限制 数组代码&…...

鸟哥的Linux私房菜 Shell脚本
第十二章、学习 Shell Scripts https://linux.vbird.org/linux_basic/centos7/0340bashshell-scripts.php 12.2 简单的 shell script 练习 #!/bin/bash# Program: # User inputs his first name and last name. Program shows his full name.read -p "Please in…...

FPGA基于RIFFA实现PCIE采集ov5640图像传输,提供工程源码和QT上位机
目录1、前言2、RIFFA理论基础3、设计思路和架构4、vivado工程详解5、上板调试验证并演示6、福利:工程代码的获取1、前言 PCIE是目前速率很高的外部板卡与CPU通信的方案之一,广泛应用于电脑主板与外部板卡的通讯,PCIE协议极其复杂,…...

week13周报
一.动态规划走楼梯2难点:不能连续走三次两级台阶如何表示思路:可以用二维数组f[i][j],i表示当前台阶数,j表示已经连续走了j次二级台阶了转移方程:f[i2][j1]f[i2][j1]f[i][j] 当j!2时,我们可以选择走二级台阶…...