二叉树题目汇总
我个人把涉及到二叉树的这15到题分为4种大类,以供参考
-
打印类
- 从上到下打印
- 不分行
- 分行 - 之字型
- 从上到下打印
-
性质类
- 对称类
- 求树的镜像
- 判断是否为对称二叉树
- 平衡类
- 判断是否为平衡二叉树
- 存在类/判断类
- 是否包含(存在)某一子结构
- 是否存在和为某一值的路径
- 某一序列是否为二叉树的后序遍历
- 对称类
-
参数类
- 二叉树的深度
- 二叉树的下一个结点
- 搜索二叉树的第K个节点
-
重建(转换)类
- 根据中序和先序重建二叉树 【字符串】
- 转化为双向链表 【链表】
- 序列化和反序列化 【字符串】
具备的基本能力
- 二叉树的前/中/后/层次遍历
- 递归
- 非递归
- 树的相关概念的区别
- 二叉树 平衡二叉树 二叉搜索树 满二叉树 完整二叉树
- 高度 深度
1.打印类
22.从上往下打印二叉树
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector<int> PrintFromTopToBottom(TreeNode* root) {
vector<int> res;
if(!root) return res;
queue<TreeNode*> queueNode;
queueNode.push(root);
while(queueNode.size()){
TreeNode* pNode=queueNode.front();
queueNode.pop();
res.push_back(pNode->val);
if(pNode->left) queueNode.push(pNode->left);
if(pNode->right) queueNode.push(pNode->right);
}
return res;
}
};
60.把二叉树打印成多行
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
vector<vector<int> > Print(TreeNode* root) {
vector<vector<int>> res;
if(!root) return res;
queue<TreeNode*> q;
q.push(root);
while(q.size()){
int size=q.size();
vector<int> path;
for(int i=0;i<size;i++){
auto front=q.front();
path.push_back(front->val);
q.pop();
if(front->left) q.push(front->left);
if(front->right) q.push(front->right);
}
res.push_back(path);
}
return res;
}
};
59.按照之字形顺序打印二叉树
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
vector<vector<int> > Print(TreeNode* p) {
vector<vector<int>> res;
vector<int> path;
if(!p) return res;
queue<TreeNode*> q;
q.push(p);
bool flag=true;
// 加标志位,奇数行 左->右 偶数行 右-> 左
while(q.size()){
//vector<int> path;
int size= q.size();
for(int i=0;i<size;i++){
//注意 这里 i<q.size() 不能这么写 因为队列是动态变化的
auto front=q.front();
path.push_back(front->val);
q.pop();
if(front->left) q.push(front->left);
if(front->right) q.push(front->right);
}
if(!flag) reverse(path.begin(),path.end());
flag=!flag;
res.push_back(path);
path.clear();
}
return res;
}
};
2.性质类
- 对称类
18.二叉树的镜像
操作给定的二叉树,将其变换为源二叉树的镜像
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
void Mirror(TreeNode *pRoot) {
if (pRoot==NULL || (pRoot->left== NULL && pRoot->right==NULL)){
return;
}
TreeNode *temp=pRoot->left;
pRoot->left=pRoot->right;
pRoot->right=temp;
if(pRoot->left){
Mirror(pRoot->left);
}
if(pRoot->right){
Mirror(pRoot->right);
}
return;
}
};
58.对称的二叉树
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
bool isSymmetrical(TreeNode* p)
{
if(!p) return true;
return dfs(p->left,p->right);
}
bool dfs(TreeNode * p,TreeNode * q ){
if(!p || !q) return !p && !q ;
// 有一个为空,返回false,两个都空的话返回 true
if( p->val != q->val) return false;
return dfs(p->left,q->right) && dfs(p->right,q->left);
}
};
- 平衡类
39.平衡二叉树
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
class Solution {
public:
bool ans = true;
bool IsBalanced_Solution(TreeNode* p) {
dfs(p);
return ans;
}
int dfs (TreeNode* p){
if (!p) return 0;
int left = dfs(p->left),right=dfs(p->right);
if (abs(left-right)>1) ans=false;
return max(left,right)+1;
};
};
- 存在类
17.树的子结构
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
if(!pRoot1||!pRoot2) return false;
if(isPart(pRoot1,pRoot2)) return true;
return HasSubtree(pRoot1->left,pRoot2)|| HasSubtree(pRoot1->right,pRoot2);
//递归枚举 先枚举左边 若没有,再枚举右边
}
//判断 第二棵数的点是不是在第一课树上都有对应的点
bool isPart(TreeNode *p1,TreeNode *p2) {
if(!p2) return true; // 第二课树空了,说明之前的都匹配上了
if(!p1||p1->val != p2->val) return false;
return isPart(p1->left,p2->left) && isPart(p1->right,p2->right);
}
};
23.二叉搜索树的后续遍历序列
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
class Solution {
public:
vector<int> seq;
bool VerifySquenceOfBST(vector<int> sequence) {
if(sequence.empty()) return false;
seq=sequence;
return dfs(0,seq.size()-1);
}
bool dfs(int l, int r){
if(l>=r) return true;
int root = seq[r];
int k=l;
while(k<r && seq[k]< root) k++;
for (int i= k;i<r;i++){
if (seq[i]<root) return false;
}
return dfs(l,k-1)&& dfs(k,r-1);
}
};
24.二叉树中和为某一值的路径
输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
if(!root || expectNumber <= 0) return res;
dfs(root,expectNumber);
return res;
}
void dfs(TreeNode* p ,int sum)
{
if(!p) return ;
path.push_back(p->val);
sum=sum-p->val;
if(!p->left&&!p->right&&!sum) res.push_back(path);
dfs(p->left,sum);
dfs(p->right,sum);
path.pop_back();
}
};
3.参数类
38.二叉树的深度
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
int TreeDepth(TreeNode* p)
{
int res=0;
if(!p) return res;
queue<TreeNode*> q;
q.push(p);
while(q.size()){
res++;
int num = q.size();
for(int i =0; i < num; i++){ // i<q.size() 不能这样写 因为是动态变化的
TreeNode* node=q.front();
q.pop();
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
}
}
return res;
}
};
57.二叉树的下一个结点
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
/*
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:
TreeLinkNode* GetNext(TreeLinkNode* p)
{
if (p->right){ // 找右子树左边的那个
p=p->right;
while(p->left){
p=p->left;
}
return p;
}
while(p->next && p == p->next->right) p=p->next;
//进不去的条件是找到了上面的第一个做左孩子的子节点
return p->next;
}
};
62.二叉搜索树的第K个结点
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4
class Solution {
public:
TreeNode* KthNode(TreeNode* pRoot, int k)
{
if(pRoot == NULL) return NULL;
return dfs(pRoot,k) ;
}
TreeNode* dfs(TreeNode* pRoot, int& k)
{
TreeNode* target = NULL;
if(pRoot->left !=NULL)
target = dfs(pRoot->left,k);
k--;
if(k==0){
target = pRoot;
return target;
}
if(target == NULL && pRoot->right !=NULL && k>0)
target = dfs(pRoot->right,k);
return target;
}
};
4.重建(转换)类
4.重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
/**
* Definition for binary tree
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
if ( pre.empty()|| vin.empty()) return NULL;
int root_val=pre[0];
TreeNode *root=new TreeNode(root_val);
int leftLength=0;
int rightLength=0;
for (int i=0;i<vin.size();i++)
{
if (vin[i]==root_val)
{
leftLength=i;
rightLength=vin.size()-i-1;
break;
}
}
vector<int> in_left,in_right,pre_left,pre_right;
for(int j=0;j<leftLength;j++)
{
in_left.push_back(vin[j]);
pre_left.push_back(pre[j+1]);
}
for(int j=0;j<rightLength;j++)
{
in_right.push_back(vin[leftLength+1+j]);
pre_right.push_back(pre[1+leftLength+j]);
}
root->left=reConstructBinaryTree(pre_left,in_left);
root->right=reConstructBinaryTree(pre_right,in_right);
return root;
}
};
26.二叉搜索树与双向链表
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
TreeNode* Convert(TreeNode* root)
{
if(!root) return NULL;
auto sides=dfs(root);
return sides.first;
}
pair<TreeNode*,TreeNode*> dfs(TreeNode* root){
if(!root->left&& !root->right) return {root,root};
if(root->left&&root->right){
auto lsides=dfs(root->left),rsides=dfs(root->right);
lsides.second->right=root;root->left=lsides.second;
rsides.first->left=root;root->right=rsides.first;
return{lsides.first,rsides.second};
}
if(root->left){
auto lsides=dfs(root->left);
lsides.second->right=root,root->left=lsides.second;
return{lsides.first,root};
}
if(root->right){
auto rsides=dfs(root->right);
root->right=rsides.first,rsides.first->left=root;
return{root,rsides.second};
}
}
};
61.序列化二叉树
请实现两个函数,分别用来序列化和反序列化二叉树
二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。
二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
char* Serialize(TreeNode *root) {
if(! root) return NULL;
string str;
dfs_s(root ,str); //注意函数的返回值
char *res =new char[str.size()+1];
int i;
for(i=0;i<str.size();i++){
res[i]=str[i];
}
res[i]='\0';
return res;
}
void dfs_s(TreeNode *root,string &str){
if(root==NULL){
str+='#';
return ;
}
str+=to_string(root->val);
str+='!';
dfs_s(root->left,str);
dfs_s(root->right,str);
}
TreeNode* Deserialize(char *str) {
if(str == NULL) return NULL;
TreeNode* res=dfs_d(&str);
return res;
}
TreeNode* dfs_d(char **str){
if(**str == '#'){
++(*str);
return NULL;
}
//把字符转换成节点
int num=0;
while(**str!='\0' && **str != '!'){
num=num*10+((**str)-'0');
++(*str);
}
TreeNode *root=new TreeNode(num);
// 转化完毕,查看是否还有未转换的节点。
// 到达字符串末尾即转换完毕
if(**str=='\0') return root;
else (*str)++;
root->left=dfs_d(str);
root->right=dfs_d(str);
return root;
}
};