这里内容是引自微信公众号: 菜鸟名企梦 的同名文章,这里我稍作整理,原文是 JAVA 版,将其用 C++ 语言重写了一下,程序均本地跑过例程。 这里体现的方法并不唯一,且并不是最优的,只是提供一种解题思路,如果读者对其中问题有更好的解读,欢迎留言区交流学习。
文章目录
- 二叉树结构:
- 1. 求二叉树的最大深度
- 2. 求二叉树的最小深度
- 3. 求二叉树中节点的个数
- 4. 求二叉树中叶子节点的个数
- 5. 求二叉树中第 k 层节点的个数
- 6. 判断二叉树是否是平衡二叉树
- 7. 判断二叉树是否是完全二叉树
- 8. 两个二叉树是否完全相同
- 9. 两个二叉树是否互为镜像
- 10. 翻转二叉树 or 镜像二叉树
- 11. 求两个二叉树的最低公共祖先节点
- 12. 二叉树的前序遍历(迭代 or 递归)
- 13. 二叉树的中序遍历(迭代 or 递归)
- 14. 二叉树的后序遍历(递归)
- 15. 前序、中序构造二叉树
- 16. 在二叉树中插入节点
- 17.输入二叉树和整数,打印出二叉树节点值等于输入整数的所有路径
- 18.二叉树的搜索区间
- 19.二叉树的层次遍历
- 20.二叉树内两个节点的最长距离
- 21. 不同的二叉树
- 22. 判断二叉树是否是合法的二叉查找树(BST)
- 23.判断是否是子树关系
二叉树相关问题及答案:
二叉树结构:
class TreeNode{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x):val(x),left(null),right(null){}
};
1. 求二叉树的最大深度
// 递归
int find_max_depth_V1(TreeNode* root)
{
int depth = 0;
if (!root)
{
return 0;
}
depth = 1 + max(find_max_depth_V1(root->left), find_max_depth_V1(root->right));
return depth;
}
// 层遍历
int find_max_depth_V2(TreeNode* root)
{
if (!root)
{
return 0;
}
int depth = 0;
queue<TreeNode*> Q;
Q.push(root);
while (!Q.empty())
{
depth++;
for (int i = 0; i < Q.size(); i++)
{
TreeNode* t = Q.front();
Q.pop();
if (t->left)
{
Q.push(t->left);
}
if (t->right)
{
Q.push(t->right);
}
}
}
return depth;
}
2. 求二叉树的最小深度
这里与最大深度不同的地方是,需要考虑二叉树仅有一边子树的情况,若直接套用最大子树的思路,取左右子树中的最小值,那么最小值永远是0。
// 递归
int find_min_depth_V1(TreeNode* root)
{
int depth = 0;
if (!root)
{
return 0;
}
if (!root->left)
{
return 1 + find_min_depth_V1(root->right);
}
if (!root->right)
{
return 1 + find_min_depth_V1(root->left);
}
depth = 1 + min(find_max_depth_V1(root->left), find_max_depth_V1(root->right));
return depth;
}
// 层遍历
int find_min_depth_V2(TreeNode* root)
{
if (!root)
{
return 0;
}
int depth = 0;
queue<TreeNode*> Q;
Q.push(root);
while (!Q.empty())
{
depth++;
for (int i = 0; i < Q.size(); i++)
{
TreeNode* t = Q.front();
Q.pop();
if (!t->left && !t->right)
{
return depth;
}
if (t->left)
{
Q.push(t->left);
}
if (t->right)
{
Q.push(t->right);
}
}
}
return depth;
}
3. 求二叉树中节点的个数
int count_node(TreeNode* root)
{
if (!root)
{
return 0;
}
int left = count_node(root->left);
int right = count_node(root->right);
return left + right + 1;
}
4. 求二叉树中叶子节点的个数
int count_leaf_node(TreeNode* root)
{
if (!root)
{
return 0;
}
if (!root->left && !root->right)
{
return 1;
}
return count_leaf_node(root->left) + count_leaf_node(root->right);
}
5. 求二叉树中第 k 层节点的个数
int num_of_k_node(TreeNode* root, int k)
{
if (!root || k<1)
{
return 0;
}if (k == 1)
{
return 1;
}
int num_left = num_of_k_node(root->left, k - 1);
int num_right = num_of_k_node(root->right, k - 1);
return num_left + num_right;
}
6. 判断二叉树是否是平衡二叉树
int max_depth(TreeNode* root)
{
if (!root)
{
return 0;
}
int left = max_depth(root->left);
int right = max_depth(root->right);
if (left==-1 || right == -1 || fabs(left-right)>1)
{
return -1;
}
return max(left,right) + 1;
}
bool isBanlanced(TreeNode* root)
{
return max_depth(root) != -1;
}
7. 判断二叉树是否是完全二叉树
bool isCompleteTree(TreeNode* root)
{
if (!root)
{
return false;
}
queue<TreeNode*> Q;
Q.push(root);
bool result = true;
bool hasNoChild = false;
while (!Q.empty())
{
TreeNode* temp = Q.front();
Q.pop();
if (hasNoChild)
{
if (temp->left || temp->right)
{
result = false;
break;
}
}else {
if (temp->left && temp->right)
{
Q.push(temp->left);
Q.push(temp->right);
}
else if(temp->left && !temp->right)
{
Q.push(temp->left);
hasNoChild = true;
}
else if (!temp->left && temp->right)
{
result = false;
break;
}
else
{
hasNoChild = true;
}
}
}
return result;
}
8. 两个二叉树是否完全相同
bool isSameTree(TreeNode* root1, TreeNode*root2)
{
if (!root1 && !root2)
{
return true;
}
else if (!root1 || !root2)
{
return false;
}
else if (root1->val != root2->val)
{
return false;
}
bool left = isSameTree(root1->left, root2->left);
bool right = isSameTree(root1->right, root2->right);
return left && right;
}
9. 两个二叉树是否互为镜像
bool isMirror(TreeNode* root1, TreeNode* root2)
{
if (!root1 && !root2)
{
return true;
}
if (!root1 || !root2)
{
return false;
}
if (root1->val != root2->val)
{
return false;
}
return isMirror(root1->left, root2->right) && isMirror(root1->right, root2->left);
}
10. 翻转二叉树 or 镜像二叉树
TreeNode* mirrorTreeNode(TreeNode* root)
{
if (!root)
{
return NULL;
}
TreeNode* left = mirrorTreeNode(root->left);
TreeNode* right = mirrorTreeNode(root->right);
root->left = right;
root->right = left;
return root;
}
11. 求两个二叉树的最低公共祖先节点
bool findNode(TreeNode* root, TreeNode* node)
{
if (!root || !node)
{
return false;
}
if (root->val==node->val && root->left == node->left && \
root->right == node->right)
{
return true;
}
bool found = findNode(root->left, node);
if (!found)
{
found = findNode(root->right, node);
}
return found;
}
TreeNode* getLastCommonParent(TreeNode* root, TreeNode* node1, TreeNode* node2)
{
if (findNode(root->left,node1))
{
if (findNode(root->right, node2)) {
return root;
}
else {
return getLastCommonParent(root->left, node1, node2);
}
}
else
{
if (findNode(root->left,node2))
{
return root;
}
else
{
return getLastCommonParent(root->right, node1, node2);
}
}
}
12. 二叉树的前序遍历(迭代 or 递归)
// 迭代
vector<int> preOrder(TreeNode* root) {
stack<TreeNode*> s;
vector<int> v;
if (!root)
{
return v;
}
s.push(root);
while (!s.empty())
{
TreeNode* temp = s.top();
s.pop();
v.push_back(temp->val);
if (temp->right)
{
s.push(temp->right);
}
if (temp->left)
{
s.push(temp->left);
}
}
return v;
}
// 递归
void preOrder2(TreeNode* root, vector<int> &result)
{
if (!root)
{
return;
}
result.push_back(root->val);
preOrder2(root->left, result);
preOrder2(root->right, result);
}
vector<int> preOrderReverse(TreeNode* root)
{
vector<int> result;
preOrder2(root, result);
return result;
}
13. 二叉树的中序遍历(迭代 or 递归)
// 递归
void midOrder2(TreeNode* root, vector<int> &result)
{
if (!root)
{
return;
}
midOrder2(root->left, result);
result.push_back(root->val);
midOrder2(root->right, result);
}
vector<int> midOrderReverse(TreeNode* root)
{
vector<int> result;
midOrder2(root, result);
return result;
}
// 迭代
vector<int> minOrder(TreeNode* root)
{
vector<int> v;
stack<TreeNode*> s;
TreeNode* temp = root;
while (temp || !s.empty())
{
while (temp) {
s.push(temp);
temp = temp->left;
}
temp = s.top();
s.pop();
v.push_back(temp->val);
temp = temp->right;
}
return v;
}
14. 二叉树的后序遍历(递归)
// 递归
void posOrder2(TreeNode* root, vector<int> &result)
{
if (!root)
{
return;
}
posOrder2(root->left, result);
posOrder2(root->right, result);
result.push_back(root->val);
}
vector<int> midOrderReverse(TreeNode* root)
{
vector<int> result;
posOrder2(root, result);
return result;
}
// 非递归
查找资料中基本方法都是添加一个多的标志位或者辅助栈,这里为了方便,更改了一下树节点的构成:
struct TreeNode_tag
{
int val;
int tag;
TreeNode_tag* left;
TreeNode_tag* right;
TreeNode_tag(int x) :val(x),tag(0),left(NULL), right(NULL) {}
};
基于上面的结构,非递归版本如下:
vector<int> posOrder(TreeNode_tag* root)
{
vector<int> v;
stack<TreeNode_tag*> s;
TreeNode_tag* tmp = root;
while (tmp || !s.empty())
{
while (tmp) {
s.push(tmp);
tmp=tmp->left;
}
if (!s.empty())
{
tmp = s.top();
if (tmp->tag)
{
v.push_back(tmp->val);
s.pop();
tmp = NULL;
}
else
{
tmp->tag = 1;
tmp = tmp->right;
}
}
}
return v;
}
使用辅助栈
vector<int> posOrder(TreeNode *root) {
vector<int> result;
stack<TreeNode*> s;
if(!root)return result;
TreeNode *current = root, *lastVisited = NULL;
while (current != NULL || !s.empty()) {
while (current != NULL) {
s.push(current);
current = current->left;
}
current = s.top();
if (current->right == NULL || current->right == lastVisited) {
s.pop();
result.push_back(current->val);
lastVisited = current;
current = NULL;
} else {
current = current->right;
}
}
return result;
}
15. 前序、中序构造二叉树
此题《编程之美》和《剑指offer》 中均有。
// 解法1
int findPosition(vector<int> arr, int start, int end, int key)
{
int i;
for ( i = start; i<=end; i++)
{
if (arr[i] == key) {
return i;
}
}
return -1;
}
TreeNode* myBuildTree(vector<int> mid_order, int mid_start,int mid_end, vector<int> pre_order \
, int pre_start, int pre_end)
{
if (mid_start > mid_end)
{
return NULL;
}
TreeNode root = TreeNode(pre_order[pre_start]);
int position = findPosition(mid_order, mid_start, mid_end, pre_order[pre_start]);
root.left = myBuildTree(mid_order, mid_start, position - 1, pre_order, pre_start + 1, pre_start + position - mid_start);
root.right = myBuildTree(mid_order, position + 1, mid_end, pre_order, position - mid_end + pre_end + 1, pre_end);
return &root;
}
TreeNode* buildTree(vector<int> pre_order, vector<int> mid_order)
{
if (pre_order.size() != mid_order.size())
{
return NULL;
}
return myBuildTree(mid_order, 0, mid_order.size()-1, pre_order, 0, pre_order.size()-1);
}
解法二(LeetCode 105 提交答案)
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
unordered_map<int, int> in_entry_idx_map;
for (size_t i = 0; i < inorder.size(); i++)
{
in_entry_idx_map.emplace(inorder[i], i);
}
return helper(preorder, 0, preorder.size(), inorder, 0, inorder.size(), in_entry_idx_map);
}
TreeNode* helper(const vector<int>& preorder, int pre_s, int pre_e,
const vector<int>& inorder, int in_s, int in_e,
unordered_map<int, int>& in_entry_idx_map)
{
if (pre_s == pre_e || in_s == in_e)
{
return NULL;
}
auto idx = in_entry_idx_map.at(preorder[pre_s]);
auto left_size = idx - in_s;
auto node = new TreeNode(preorder[pre_s]);
node->left = helper(preorder, pre_s + 1, pre_s + 1 + left_size,
inorder, in_s, idx, in_entry_idx_map);
node->right = helper(preorder, pre_s + 1 + left_size, pre_e,
inorder, idx + 1, in_e, in_entry_idx_map);
return node;
}
};
16. 在二叉树中插入节点
这里的二叉树应为二叉搜索树,树中的结构有对应的大小关系:
TreeNode* insertNode(TreeNode* root, TreeNode* node)
{
if (root->val==node->val && root->left == node->left && root->right==node->right)
{
return node;
}
TreeNode * tmp = root;
TreeNode* last = NULL;
while (tmp)
{
last = tmp;
if (tmp->val > node->val)
{
tmp = tmp->left;
}
else
{
tmp = tmp->right;
}
}
if (last)
{
if (last->val > node->val) {
last->left = node;
}
else
{
last->right = node;
}
}
return root;
}
17.输入二叉树和整数,打印出二叉树节点值等于输入整数的所有路径
void helper(TreeNode* root, int target, vector<int> s, int currentSum)
{
currentSum += root->val;
s.push_back(root->val);
if (!root->left && !root->right)
{
if (currentSum == target) {
for (auto i:s)
{
cout << i <<" ";
}
cout << endl;
}
}
if (root->left)
{
helper(root->left, target, s, currentSum);
}
if (root->right)
{
helper(root->right, target, s, currentSum);
}
s.pop_back();
}
void findPath(TreeNode* root, int target)
{
if (!root)
{
return;
}
vector<int> s;
int currentSum = 0;
helper(root, target, s, currentSum);
}
18.二叉树的搜索区间
给定两个值 k1 和 k2(k1<k2) 和一个二叉查找树的根节点,找到树中所有值在 k1 和 k2 范围内的节点,即打印所有 x 其中 x 是二叉查找树的中的节点值。返回所有升序的节点值
void searchHelper(TreeNode* root, int k1, int k2, vector<int> &v)
{
if (!root)
{
return;
}
if (root->val>k1)
{
searchHelper(root->left, k1, k2, v);
}
if (root->val>=k1 && root->val<=k2)
{
v.push_back(root->val);
}
if (root->val < k2)
{
searchHelper(root->right, k1, k2, v);
}
}
vector<int>searchRange(TreeNode* root, int k1, int k2)
{
vector<int> result;
searchHelper(root, k1, k2, result);
return result;
}
19.二叉树的层次遍历
vector<vector<int>> levelOrder(TreeNode* root)
{
vector<vector<int>> result;
if (!root)
{
return result;
}
queue<TreeNode*> Q;
Q.push(root);
while (!Q.empty())
{
vector<int> tmp_v;
int s = Q.size();
for (int i = 0; i < s; i++)
{
TreeNode* tmp_node = Q.front();
Q.pop();
tmp_v.push_back(tmp_node->val);
if (tmp_node->left)
{
Q.push(tmp_node->left);
}
if (tmp_node->right)
{
Q.push(tmp_node->right);
}
}
result.push_back(tmp_v);
}
return result;
}
20.二叉树内两个节点的最长距离
分三种情况:
- 左子树的最大深度 + 右子树的最大深度
- 左子树的最大深度
- 右子树的最大深度
// 递归
int findMaxLen2(TreeNode* root)
{
if (!root)
{
return 0;
}
int leftMax = 0;
int rightMax = 0;
if (root->left)
{
leftMax = find_max_depth_V1(root->left);
}
if (root->right)
{
rightMax = find_max_depth_V1(root->right);
}
return leftMax + rightMax;
}
// 这里参考的 《编程之美》 P241 的解法,重新构造一个树节点结构,加入各个节点的左分支最大长度和右分支最大长度
struct TreeNode2{
int val;
TreeNode2* left;
TreeNode2* right;
int nMaxLeft;
int nMaxright;
TreeNode2(int x) : val(x), left(NULL), right(NULL), nMaxLeft(0), nMaxright(0){}
};
int findMaxLen(TreeNode2* root)
{
if (!root)
{
return 0;
}
if (!root->left) // 判断左子树最长距离
{
root->nMaxLeft = 0;
}
if (!root->right) // 判断右子树最长距离
{
root->nMaxright = 0;
}
if (root->left) // 遍历左子树
{
findMaxLen(root->left);
}
if (root->right) // 遍历右子树
{
findMaxLen(root->right);
}
if (root->left) // 求左子树的最大长度
{
int tmpMax = 0;
if (root->left->nMaxLeft > root->left->nMaxright)
{
tmpMax = root->left->nMaxLeft;
}
else {
tmpMax = root->left->nMaxright;
}
root->nMaxLeft = tmpMax + 1;
}
if (root->right) // 求右子树的最大长度
{
int tmpMax = 0;
if (root->right->nMaxLeft > root->right->nMaxright)
{
tmpMax = root->right->nMaxLeft;
}
else {
tmpMax = root->right->nMaxright;
}
root->nMaxright = tmpMax + 1;
}
return root->nMaxLeft + root->nMaxright;
}
21. 不同的二叉树
给出n,由 1…n 为节点组成的二叉查找树有多少种
int numTrees(int n)
{
vector<int> counts(n + 1, 0);
counts[0] = 1;
counts[1] = 1;
for (int i = 2; i <=n; i++)
{
for (int j = 0; j < i; j++) {
counts[i] += counts[j] * counts[i - j - 1];
}
}
return counts[n];
}
22. 判断二叉树是否是合法的二叉查找树(BST)
BST 树定义为:
1.节点的左子树值要小于该点的值
2.节点右子树值要大于该点的值
3.左右子树必须是二叉查找树
4.一个节点的树也是二叉查找树
bool firstNode = true;
int lastVal = INT_MAX;
bool isValidBST(TreeNode* root)
{
if (!root)
{
return true;
}
if (!isValidBST(root->left))
{
return false;
}
if (!firstNode && lastVal>=root->val)
{
return false;
}
firstNode = false;
lastVal = root->val;
if (!isValidBST(root->right))
{
return false;
}
return true;
}
这里还可以用二叉查找树中序遍历是一个递增序列的特性,使用二叉树的中序遍历进行求解,最后如果不是一个递增序列则不是,或者在中序非递归遍历过程中记录前一个节点的数值,如果是逆序的,则不是。
23.判断是否是子树关系
给定两个树 A 和 B ,看 B 是不是 A 的子树 (规定 空树 不属于任何树的子树)
class Solution {
public:
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
bool result=false;
if (!pRoot2 || !pRoot1)
return result;
if (pRoot1->val==pRoot2->val)
result= isSubTree(pRoot1,pRoot2);
if (!result)
result = HasSubtree(pRoot1->left,pRoot2);
if (!result)
result = HasSubtree(pRoot1->right,pRoot2);
return result;
}
bool isSubTree(TreeNode* pRoot1, TreeNode* pRoot2)
{
if(!pRoot2)
return true;
if(!pRoot1 || pRoot1->val != pRoot2->val)
return false;
return isSubTree(pRoot1->left,pRoot2->left) && isSubTree(pRoot1->right,pRoot2->right);
}
};