一、二叉树的最大深度
题目:给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点
题解:此题在二叉树博客中有写过,这里我留不一一详解,如果还不了解的同学可以去看看(二叉树的高度)https://blog.csdn.net/qq_44443986/article/details/111995739
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int maxDepth(TreeNode* root) {
if (root == nullptr)
return 0;
int left = maxDepth(root->left);
int right = maxDepth(root->right);
return left > right ? left + 1 : right + 1;
}
};
二、单值二叉树
题目:如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。只有给定的树是单值二叉树时,才返回 true;否则返回 false。
题解:我们可以定义一个新的函数用来递归二叉树,我们先序遍历该二叉树,只要他们的值都和根节点的值相同,那么它们就是单值二叉树。只要有一个节点的值与根节点的值不同,那么该树就不是单值二叉树。
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool helper(TreeNode* root, int val)
{
if (root == nullptr)
return true;
if (root->val != val)
return false;
return helper(root->left, val) && helper(root->right, val);
}
bool isUnivalTree(TreeNode* root)
{
if (root == nullptr)
return true;
return helper(root, root->val);
}
};
三、翻转二叉树
题目:翻转一棵二叉树。
题解:这里我们用到递归,交换当前节点的左右孩子,交换完后再交换左孩子的左右子树,依次递归。当两个孩子都为空时,则是该子树翻转完毕。再交换右孩子的左右子树。同理,当两个孩子都为空时,则是该子树翻转完毕。
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* mirrorTree(TreeNode* root)
{
if (root == nullptr)
return nullptr;
TreeNode* tmp = root->left;
root->left = root->right;
root->right = tmp;
mirrorTree(root->left);
mirrorTree(root->right);
return root;
}
};
四、相同的树
题目:给定两个二叉树,编写一个函数来检验它们是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
题解:我们可以同时遍历两棵树,依次比较他们结点的值是否相同,这里我们用先序遍历两棵树,当他们的值相同时,就遍历左子树是否相同,如果相同,再遍历右子树。只有左右子树都相同时,这两棵树才是相同的树。
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q)
{
if (p == nullptr && q == nullptr)
return true;
if (p== nullptr || q == nullptr)
return false;
return p->val == q->val
&& isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}
};
五、另一个数的子树
题目:给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。
题解:这道题与上一道题非常相似,可以说上一道题是这道题的一个子问题。递归得判断s树的每个结点,让每个结点都为一个根节点去与t树判断是否为相同树,只要是相同树就返回true,也就是s 树中是否包含和 t 树具有相同结构和节点值的子树。当当前节点与t树不同时,再访问s树的下一个结点,这里我们采用的是先序遍历,每个结点都要访问。
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q)
{
if (p == nullptr && q == nullptr)
return true;
if (p== nullptr || q == nullptr)
return false;
return p->val == q->val
&& isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}
bool isSubtree(TreeNode* root, TreeNode* subRoot)
{
if (root == nullptr)
return false;
if (subRoot == nullptr)
return true;
if (isSameTree(root, subRoot))
return true;
return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}
};
六、对称二叉树
题目:给定一个二叉树,检查它是否是镜像对称的。
题解:根据题目的描述,镜像对称,就是左右两边相等,也就是左子树和右子树是相等的。注意这句话,左子树和右子相等,也就是说要递归的比较左子树和右子树。我们将根节点的左子树记做 left,右子树记做 right。比较 left 是否等于 right,不等的话直接返回就可以了。如果相等,比较 left 的左节点和 right 的右节点,再比较 left 的右节点和 right 的左节点
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool helper(TreeNode* left, TreeNode* right)
{
if (left == nullptr && right == nullptr)
return true;
if (left == nullptr || right == nullptr)
return false;
return left->val == right->val
&& helper(left->left, right->right) && helper(left->right, right->left);
}
bool isSymmetric(TreeNode* root)
{
if (root == nullptr)
return true;
return helper(root->left, root->right);
}
};
七、平衡二叉树
题目:给定一个二叉树,判断它是否是高度平衡的二叉树。本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1
。
题解:在第一题中,我们已经知道如何获取当前节点的深度,我们分别计算左右子树的深度相差是否小于2,如果小于2,就继续遍历下一个节点的左右子树。这里采用的是先序遍历。只要存在左右孩子的深度差大于等于2,说明该树不是平衡二叉树,返回false;
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int BTreeHigh(TreeNode* root)
{
if (root == nullptr)
return 0;
int left = BTreeHigh(root->left);
int right = BTreeHigh(root->right);
return left > right ? left + 1 : right + 1;
}
bool isBalanced(TreeNode* root)
{
if (root == nullptr)
return true;
return abs(BTreeHigh(root->left) - BTreeHigh(root->right)) < 2
&& isBalanced(root->left) && isBalanced(root->right);
}
};
八、二叉树的构建及遍历
题目:编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。
题解:此题在二叉树博客中有写过,这里我留不一一详解,如果还不了解的同学可以去看看(二叉树的创建,二叉树的遍历)https://blog.csdn.net/qq_44443986/article/details/111995739
代码:
#include <iostream>
#include <string>
using namespace std;
struct TreeNode
{
char val;
TreeNode* left, *right;
TreeNode(int val)
:val(val)
,left(nullptr)
,right(nullptr)
{}
};
TreeNode* createTree(const string& str, int& idx)
{
if (str[idx] == '#')
{
++idx;
return nullptr;
}
TreeNode* root = new TreeNode(str[idx]);
++idx;
root->left = createTree(str, idx);
root->right = createTree(str, idx);
return root;
}
void inorder(TreeNode* root)
{
if (root == nullptr)
return;
inorder(root->left);
cout << root->val << " ";
inorder(root->right);
}
int main()
{
string str;
while (getline(cin, str))
{
int idx = 0;
TreeNode* root = createTree(str, idx);
inorder(root);
cout << endl;
}
return 0;
}
九、二叉树的前序遍历
题目:
这里我们采用非递归的解法去解决这道题。
思路:
- 访问每一个结点开始的最左路径,访问到的每一个结点入栈,并保存在数组中
- 最左路径访问完之后,获取栈顶元素并出栈,继续访问以栈顶元素的右子树为根的子结构,继续执行第一步
- 结束:栈为空 && 根为空
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root)
{
vector<int> res;
stack<TreeNode*> st;
while (root || !st.empty())
{
while (root)
{
res.push_back(root->val);
st.push(root);
root = root->left;
}
root = st.top();
st.pop();
root = root->right;
}
return res;
}
};
十、二叉树的中序遍历
思路:
- 以根结点开始,走最左路径,此路径上遇到的每一个结点先入栈,但不访问
- 获取栈顶元素并访问栈顶元素
- 获取栈顶元素的右子树,继续执行第一步
- 结束:栈为空 && 根为空
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root)
{
stack<TreeNode*> st;
vector<int> vec;
while (root || !st.empty())
{
while (root)
{
st.push(root);
root = root->left;
}
root = st.top();
st.pop();
vec.push_back(root->val);
root = root->right;
}
return vec;
}
};
十一、二叉树的后序遍历
题目:
思路:
后序遍历:
1、以结点开始,遍历最左路径,遇到的每一个结点都入栈。
2、获取栈顶元素:
判断栈顶元素是否可以访问:
a、没有右子树:可以访问–>访问完执行第一步
b、有右子树且右子树已经被访问:可以访问–>访问完执行第一步
c、有右子树但是右子树没被访问:不能访问当前元素–>以右子树为根节点执行第一步
3、结束:结点为空 && 栈为空
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root)
{
vector<int> vec;
stack<TreeNode*> st;
TreeNode* prev = nullptr;
while (root || !st.empty())
{
while (root)
{
st.push(root);
root = root->left;
}
TreeNode* top = st.top();
if (top->right == nullptr || top->right == prev)
{
prev = top;
vec.push_back(top->val);
st.pop();
}
else
{
root = top->right;
}
}
return vec;
}
};
十二、根据二叉树创建字符串
思路:
遇到结点就将结点的值保存在字符串中
判断左子树是否为空
1.为空:再判断右子树是否为空,
1.1右子树为空,则什么都不做,并退出
1.2右子树不为空,添加空括号()
2.不为空:添加前括号(,再继续递归遍历左孩子,遍历结束加后括号)
判断右子树是否为空
1.为空:什么都不做
2.不为空:添加前括号(,再继续递归遍历右孩子,遍历结束加后括号)
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
void _tree2str(TreeNode* root, string& res)
{
if (root)
{
stringstream ss;
ss << root->val;
res += ss.str();
if (root->left)
{
res += '(';
_tree2str(root->left, res);
res += ')';
}
else
{
if (root->right != nullptr)
res += "()";
else
return;
}
if (root->right)
{
res += '(';
_tree2str(root->right, res);
res += ')';
}
}
}
string tree2str(TreeNode* root)
{
string res;
_tree2str(root, res);
return res;
}
};
十三、二叉树的层序遍历①
思路:这里要实现层序遍历,我们不能再用递归的方式去实现。我们可以借助一个队列,先将根节点入队。然后再将根节点出队的同时,将根节点的左孩子和右孩子依次入队,注意,这里只能左孩子先入队,右孩子才能入队。当然,这里是在孩子存在的情况下,如果孩子为空,就不入队。然后出队头元素,在出的同时将队头元素的左右孩子依次入队,依次循环,直到队列为空为止
代码:
void BinaryTreeLevelOrder(TreeNode* root)
{
//队列
Queue<TreeNode*> queue;
//将根节点先入队
if (root)
queue.push(root);
//只要队列不为空就继续循环
while (!queue.empty())
{
//获取队头元素
Node* front = queue.front();
//出队
queue.pop();
cout << front->val << " ";
//左孩子非空,入队
if (front->_left)
queue.push(front->_left);
//右孩子非空,入队
if (front->_right)
queue.push(front->_right);
}
}
十四、二叉树的层序遍历②
思路:
和上面一题差不多,只需要增加一个二维数组来保存结果,当我们进入循环时,定义一个一维数组tmp,用来保存这一层的所有结点。获取队列的长度row,也就是这一层的结点的个数。再用一个while循环,保存这一层的结点,也就是循环row次,每循环一次就获取当前队列头结点的值,保存在一维数组tmp中。然后删除队列头结点,让他去遍历下一个。这while循环里最后要保存队列头结点的左右孩子(非空情况),用于下一层的遍历。最后在外层循环将一维数组中的值保存在二维数组res中。
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root)
{
vector<vector<int>> res;
queue<TreeNode*> queue;
if (root)
queue.push(root);
//为空则表示下一层没有结点了
while (!queue.empty())
{
int row = queue.size();
vector<int> tmp;
while (row--)
{
TreeNode* front = queue.front();
queue.pop();
tmp.push_back(front->val);
if (front->left)
queue.push(front->left);
if (front->right)
queue.push(front->right);
}
res.push_back(tmp);
}
return res;
}
};
十五、二叉树的最近公共祖先
题目:
思路:主要思路就是用一个栈保存各自的路径,也就是从跟结点往下走到指定结点所遇到的所有结点,长路径的结点要删掉比短路径结点多的多余结点,只要路径上从下往上,遇到相同的结点,就是它们公共祖先的最近结点。下面解释如何获得路径并保存在栈上
- 只要当前节点不为空,就保存在栈中,为空则返回false,表示不在这条路径上
- 判断当前节点是否为要找的结点,是就返回true,表示已找到
- 如果当前节点不是要找的结点,那么查看当前的结点的左右子树中是否存在,只要存在,则该结点就是走到指定结点的路径,保存在栈中。如果左右子树都不存在,表示以此结点为根的树中不存在指定结点,不是 指定结点的路径,将该结点出栈
例如我们查找7结点和8结点的最近公共祖先
同理,查找8结点也是一样,此时他们栈中的数据如下
但是他们长度不同,公共祖先是同一个结点,同一个结点肯定就在同一层,我们只要让栈空间大减到和小的栈的空间一样大,然后让他们一起往下找同一个结点,当找到第一个相同节点,就是找到了他们最近的公共祖先
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool getPath(TreeNode* root, TreeNode* cur, stack<TreeNode*>& st)
{
//根为空,表示没找到
if (root == nullptr)
return false;
st.push(root);
if (root == cur)//找到直接返回
return true;
//没找到就查看左右子树是否存在
if (getPath(root->left, cur, st))
return true;
if (getPath(root->right, cur, st))
return true;
//根节点和根节点的左右子树都不存在,表示该结点不在路径上
//将该节点出栈,并返回false表示没找到
st.pop();
return false;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
stack<TreeNode*> pst;
stack<TreeNode*> qst;
getPath(root, p, pst);
getPath(root, q, qst);
while (pst.size() != qst.size())
{
if (pst.size() > qst.size())
pst.pop();
else
qst.pop();
}
while (pst.top() != qst.top())
{
pst.pop();
qst.pop();
}
return pst.top();
}
};
十六、二叉搜索树与双向链表
题目:
思路:要想让二叉搜索树有序输出,就必须是中序遍历,所以我们可以中序遍历,完整树的连接
- 先遍历左子树,并用一个prev结点表示是上一次访问的结点
- 访问节点,让该结点的左孩子为上一次访问的结点,如果prev结点不为空,则表示上一次有访问过结点,就将上次访问的结点prev的右孩子为当前访问的结点。上次访问的一定是比当前访问的结点小。访问完后将当前节点置为已访问的结点prev。再访问右子树
- 最后要求返回链表头结点,也就是搜索树的最左结点
返回链表的头结点,访问置cur的左孩子为空时,此时的cur就是最小结点
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
void _convert(TreeNode* root, TreeNode*& prev)
{
if (root)
{
//先访问左子树
_convert(root->left, prev);
//访问跟结点进行连接
root->left = prev;
if (prev)
prev->right = root;
prev = root;
//最后访问右子树
_convert(root->right, prev);
}
}
TreeNode* Convert(TreeNode* pRootOfTree)
{
TreeNode* prev = nullptr;
_convert(pRootOfTree, prev);
TreeNode* cur = pRootOfTree;
while (cur && cur->left)
{
cur = cur->left;
}
return cur;
}
};
十七、从前序与中序遍历序列构造二叉树
题目:
思路:
自上而下创建节点,自底向上连接节点
(配合代码看)
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder, int& prev, int startIdx, int endIdx)
{
if (startIdx > endIdx)
return nullptr;
//创建当前节点
TreeNode* cur = new TreeNode(preorder[prev]);
int curIdx = startIdx;
for (; curIdx <= endIdx; ++curIdx)
{
if (preorder[prev] == inorder[curIdx])
break;
}
//左子树区间[startIdx, curIdx - 1]
if (startIdx < curIdx)
cur->left = _buildTree(preorder, inorder, ++prev, startIdx, curIdx - 1);
else
cur->left = nullptr;
//右子树区间[curIdx + 1, endIdx]
if (curIdx < endIdx)
cur->right = _buildTree(preorder, inorder, ++prev, curIdx + 1, endIdx);
else
cur->right = nullptr;
return cur;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
{
int prev = 0;
return _buildTree(preorder, inorder, prev, 0, preorder.size() - 1);
}
};
十八、从中序与后序遍历序列构造二叉树
题目:
思路:
和上一题思路类似,只是要从后序序列开始创建节点,并先创建好右子树再创建左子树
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* _buildTree(vector<int>& inorder, vector<int>& postorder, int& post, int startIdx, int endIdx)
{
if (startIdx > endIdx)
return nullptr;
TreeNode* cur = new TreeNode(postorder[post]);
int curIdx = startIdx;
for (; curIdx <= endIdx; ++curIdx)
{
if (inorder[curIdx] == postorder[post])
break;
}
//右子树区间 [curIdx + 1, endIdx]
if (curIdx < endIdx)
cur->right = _buildTree(inorder, postorder, --post, curIdx + 1, endIdx);
else
cur->right = nullptr;
//左子树 [startIdx, curIdx - 1]
if (startIdx < curIdx)
cur->left = _buildTree(inorder, postorder, --post, startIdx, curIdx - 1);
else
cur->left = nullptr;
return cur;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder)
{
int post = postorder.size() - 1;
return _buildTree(inorder, postorder, post, 0, postorder.size() - 1);
}
};