一、表达式树
1. 求数值
无题目链接
// BTNode的val为char型变量
int calcul(BTNode* root)
{
if (!root) return 0;
if (!root->lchild && !root->rchild) return root->data - '0';
else
{
int A = calcul(root->lchild);
int B = calcul(root->rchild);
return op(A, B, root->data);
}
}
2. 求字符串
// 方法1
void inorder(BTNode* root, int depth)
{
if (!root) return;
if (!root->lchild && !root->rchild) cout << root->data; // 叶子结点
else
{
if (depth > 1) cout << "(";
inorder(root->lchild, depth + 1);
cout << root->data;
inorder(root->rchild, depth + 1);
if (depth > 1) cout << ")";
}
}
// 方法2
string res;
string expressionTree(BTNode* root)
{
if (!root) return res;
if (!root->lchild && !root->rchild) res += root->data;
else {
res += "(";
expressionTree(root->lchild);
res += root->data;
expressionTree(root->rchild);
res += ")";
}
return res.substr(1, res.size() - 2);
}
二、二叉树的深度与宽度(easy)
1. 二叉树的深度
题目链接
a. 递归方法
int maxDepth(BTNode* root)
{
if (!root) return 0;
int ldepth = maxDepth(root->lchild);
int rdepth = maxDepth(root->rchild);
return max(ldepth, rdepth) + 1;
}
b. 非递归方法(王道P149)
// 方法1
int maxDepth(BTNode* root)
{
if (!root) return 0;
int depth = 0;
queue<BTNode*> q;
q.push(root);
while (q.size())
{
depth ++;
int len = q.size();
for (int i = 0; i < len; i ++)
{
auto t = q.front();
q.pop();
if (t->lchild) q.push(t->lchild);
if (t->rchild) q.push(t->rchild);
}
}
return depth;
}
// 方法2
int maxDepth(BTNode* root)
{
if (!root) return 0;
int depth = 0;
queue<BTNode*> q;
q.push(root);
while (q.size())
{
queue<BTNode*> tmp;
depth ++;
while (q.size())
{
auto t = q.front();
q.pop();
if (t->lchild) tmp.push(t->lchild);
if (t->rchild) tmp.push(t->rchild);
}
q = tmp;
}
return depth;
}
2. 二叉树的宽度(同一层最左边到最右边非空结点的最大个数)
无题目链接
int widthOfBinaryTree(BTNode* root) {
int max_width = 0;
if (!root) return max_width;
queue<BTNode*> q;
q.push(root);
while (q.size())
{
int len = q.size();
if (len > max_width) max_width = len;
for (int i = 0; i < len; i ++)
{
auto t = q.front();
q.pop();
if (t->lchild) q.push(t->lchild);
if (t->rchild) q.push(t->rchild);
}
}
return max_width;
}
};
3. 某一结点值为x的深度
无题目链接
// 某一结点值为x的深度
int depth = 1;
void findXDepth(BTNode *root, char x)
{
if (!root) return;
if (root->data == x)
cout << depth << endl;
depth ++;
findXDepth(root->lchild, x);
findXDepth(root->rchild, x);
depth --;
}
// 某一结点为p的深度
int getNodeDepth(BTNode *root, BTNode *p)
{
if (!root) return 0;
if (root == p) return 1; // 遍历到p点就设其深度为1,跟第1句道理类似
int ldepth = getNodeLevel(root->lchild, p);
int rdepth = getNodeLevel(root->rchild, p);
// 如果左子树或者右子树中有p点,则返回max(ldepth, rdepth) + 1
// 如果左子树和右子树中没有p点,直接返回0
if (ldepth || rdepth) return 1 + max(ldepth, rdepth);
else return 0;
}
4. 给出二叉树自下而上、从右到左的BFS
无题目链接
void reverseOrder(BTNode *root) {
if (!root) return;
queue<BTNode*> q;
stack<BTNode*> s;
q.push(root);
while (q.size())
{
auto t = q.front();
q.pop();
// 队头压栈,栈中得到逆层次遍历序列
s.push(t);
if (t->lchild) q.push(t->lchild);
if (t->rchild) q.push(t->rchild);
}
while (s.size())
{ // 打印栈中内容
auto t = s.top();
s.pop();
cout<<t->data<<" ";
}
}
三、找到第k个结点的值(easy)
1. 先序序列
无题目链接
int cnt = 0;
void pretrave(BTNode* root, int k)
{
if (!root) return;
if (++ cnt == k)
{
cout << root->data << endl;
return;
}
trave(root->lchild, k);
trave(root->rchild, k);
}
2. 中序序列
无题目链接
int cnt = 0;
void intrave(BTNode* root, int k)
{
if (!root) return;
trave(root->lchild, k);
if (++ cnt == k)
{
cout << root->data << endl;
return;
}
trave(root->rchild, k);
}
3. 后序序列
无题目链接
int cnt = 0;
void posttrave(BTNode* root, int k)
{
if (!root) return;
trave(root->lchild, k);
trave(root->rchild, k);
if (++ cnt == k)
{
cout << root->data << endl;
return;
}
}
四、重建二叉树
1. 先序和中序确定二叉树
vector<int> pre, in;
unordered_map<int, int> pos;
BTNode* buildtree(int il, int ir, int pl, int pr)
{
int k = pos[pre[pl]];
auto root = new BTNode(pre[pl]);
if (il < k)
root->lchild = buildtree(il, k - 1, pl + 1, pl + 1 + k - 1 - il);
if (k < ir)
root->rchild = buildtree(k + 1, ir, pl + 1 + k - 1 - il + 1, pr);
return root;
}
BTNode* reConstructBiTree(vector<int>& preOrder, vector<int>& inOrder) {
pre = preOrder, in = inOrder;
if (pre.empty() || in.empty()) return NULL;
for (int i = 0; i < in.size(); i ++) pos[in[i]] = i;
auto root = buildtree(0, in.size() - 1, 0, pre.size() - 1);
return root;
}
2. 中序和后序确定二叉树
vector<int> in, post;
unordered_map<int, int> pos;
BTNode* build(int il, int ir, int pl, int pr)
{
int k = pos[post[pr]];
auto root = new BTNode(post[pr]);
if (il < k)
root->lchild = build(il, k - 1, pl, pl + k - 1 - il);
if (k < ir)
root->rchild = build(k + 1, ir, pl + k - 1 - il + 1, pr - 1);
return root;
}
BTNode* buildTree(vector<int>& inorder, vector<int>& postorder)
{
in = inorder, post = postorder;
if (in.empty() || post.empty()) return NULL;
for (int i = 0; i < in.size(); i ++) pos[in[i]] = i;
auto root = build(0, in.size() - 1, 0, post.size() - 1);
return root;
}
3. 满二叉树和先序确定二叉树的后序遍历序列(天勤P172)
无题目链接
参考链接,这个博主写得好
void preToPost(int pre[], int pl, int pr)
{
int root = pre[pl]; //暂存根,子树遍历完后最后输出
if (pl < pr) {
pl = pl + 1;//左端 + 1(把根节点独立出来)
//此时左子树从pl + 1位开始,到(pl + pr) / 2)结束
preToPost(pre, pl, (pl + pr) / 2); //左子树
//此时右子树从(pl + pr) / 2 + 1位开始,到pr结束
preToPost(pre, (pl + pr) / 2 + 1, pr); //右子树
}
printf("%d", root); //输出
}
五、二叉树的各种结点数
1. 确定二叉树的所有结点数
// 方法1
int cnt = 0;
void dfs(BTNode* root)
{
if (!root) return;
cnt ++;
dfs(root->lchild);
dfs(root->rchild);
}
int node_num(BTNode* root)
{
dfs(root);
return cnt;
}
// 方法2
int node_num(BTNode* root)
{
if (!root) return 0;
int lcnt = node_num(root->left);
int rcnt = node_num(root->right);
return lcnt + rcnt + 1;
}
2. 确定二叉树的所有叶子结点数
无题目链接
// 方法1
int cnt = 0;
void dfs(BTNode* root)
{
if (!root) return;
if (!root->lchild && !root->right) cnt ++;
dfs(root->lchild);
dfs(root->rchild);
}
int leaf_num(BTNode* root)
{
dfs(root);
return cnt;
}
// 方法2
int leaf_num(BTNode* root)
{
if (!root) return 0;
else if (!root->lchild && !root->rchild) return 1;
else return leaf_num(root->lchild) + leaf_num(root->rchild);
}
3. 确定二叉树的双分支结点数
无题目链接
int cnt = 0;
void dfs(BTNode* root)
{
if (!root) return;
if (root->lchild && root->rchild) cnt ++;
dfs(root->lchild);
dfs(root->rchild);
}
int double_num(BTNode* root)
{
dfs(root);
return cnt;
}
六、判断问题
1. 判断给定二叉树是否是完全二叉树
bool isCompleteTree(BTNode* root)
{
if (!root) return true;
queue<BTNode*> q;
q.push(root);
bool null_flag = false;
while (q.size())
{
auto t = q.front();
q.pop();
if (!t) null_flag = true;
else
{
if (null_flag) return false;
q.push(t->lchild);
q.push(t->rchild);
}
}
return true;
}
2. 判断给定二叉树是否是二叉搜索树(BST)
bool isValidBST(BTNode* root)
{
if (!root) return true;
stack<BTNode*> s;
int minval = -0x7fffffff;
auto p = root;
while (p || s.size())
{
if (p)
{
s.push(p);
p = p->lchild;
}
else
{
// 注意!!这里不是auto p!!
p = s.top();
s.pop();
if (p->data <= minval) return false;
else minval = p->data;
p = p->rchild;
}
}
return true;
}
// 另一种差不多更习惯的写法
bool isValidBST(BTNode* root)
{
if (!root) return true;
stack<BTNode*> s;
int minval = -0x7fffffff;
auto p = root;
while (p || s.size())
{
if (p)
{
s.push(p);
p = p->lchild;
}
else
{
auto t = s.top();
s.pop();
if (t->data <= minval) return false;
else minval = t->data;
p = t->rchild;
}
}
return true;
}
3. 判断给定二叉树是否是平衡二叉树(AVL)
类似题目:题目链接(只需要判断其平衡性)
int depth(BTNode* root)
{
if (!root) return 0;
int ldepth = depth(root->lchild);
int rdepth = depth(root->rchild);
return max(ldepth, rdepth) + 1;
}
bool is_balance(BTNode* root)
{
if (!root) return true;
int ldepth = depth(root->lchild);
int rdepth = depth(root->rchild);
if (abs(ldepth - rdepth) > 1) return false;
return is_balance(root->lchild) && is_balance(root->rchild);
}
// 私认为针对题目来说,答案如下,即把判断BST和balance的集成一下
bool isValidBST(BTNode* root)
{
if (!root) return true;
stack<BTNode*> s;
int minval = -0x7fffffff;
auto p = root;
while (p || s.size())
{
if (p)
{
s.push(p);
p = p->lchild;
}
else
{
p = s.top();
s.pop();
if (p->data <= minval) return false;
else minval = p->data;
p = p->rchild;
}
}
return true;
}
int depth(BTNode* root)
{
if (!root) return 0;
int ldepth = depth(root->lchild);
int rdepth =depth(root->rchild);
return max(ldepth, rdepth) + 1;
}
bool is_balance(BTNode* root)
{
if (!root) return true;
int ldepth = depth(root->lchild);
int rdepth = depth(root->rchild);
if (abs(ldepth - rdepth) > 1) return false;
return is_balance(root->lchild) && is_balance(root->rchild);
}
bool is_balanced_BST(BTNode* root)
{
if (!root) return true;
return is_balance(root) && isValidBST(root);
}
4. 判断两棵二叉树是否相似
类似题目:题目链接(判断两棵二叉树是否相同)
bool is_same(BTNode* root1, BTNode* root2)
{
if (!root1 && !root2) return true;
else if (!root1) return false;
else if (!root2) return false;
else
{
if (root1->data != root2->data) return false;
else return is_same(root1->lchild, root2->lchild) && is_same(root1->rchild, root2->rchild);
}
}
// 私认为针对题目来说,答案如下
bool is_similar(BTNode* root1, BTNode* root2)
{
if (!root1 && !root2) return true;
else if (!root1) return false;
else if (!root2) return false;
else return is_similar(root1->lchild, root2->lchild) && is_similar(root1->rchild, root2->rchild);
}
七、路径问题
1. 找到所有结点到根结点的路径(天勤P171)
vector<vector<int>> paths;
vector<int> path;
vector<string> res;
void find_path(BTNode* root)
{
if (!root) return;
// 下面第一句总是忘记写在哪。。导致报错
path.push_back(root->data);
if (!root->lchild && !root->rchild)
paths.push_back(path);
find_path(root->lchild);
find_path(root->rchild);
path.pop_back();
}
vector<string> binaryTreePaths(BTNode* root) {
if (!root) return res;
find_path(root);
// 力扣上不加下面两句话,会报错
// if (paths.size() == 0 || paths[0].size() == 0)
// return res;
for (auto p : paths)
{
string re = to_string(p[0]);
for (int i = 1; i < p.size(); i ++)
{
re += "->";
re += to_string(p[i]);
}
res.push_back(re);
}
return res;
}
2. 计算二叉树的WPL(带权路径长度)
// 方法1
int WPL = 0;
void dfs(BTNode* root, int depth)
{
if (!root) return;
if (!root->lchild && !root->rchild) WPL += root->data * depth;
dfs(root->lchild, depth + 1);
dfs(root->rchild, depth + 1);
}
int pathSum(BTNode* root)
{
if (!root) return 0;
int depth = 0;
dfs(root, depth);
return WPL;
}
// 方法2
vector<vector<BTNode*>> paths;
vector<BTNode*> path;
int WPL = 0;
void find_path(BTNode* root)
{
if (!root) return;
path.push_back(root);
// 遍历到叶子结点就把path弹入paths,如果遍历到null就弹入的话,
// 一个叶子结点因为有两个null,所以,同一个path会被弹入paths两次
if (!root->lchild && !root->rchild) paths.push_back(path);
find_path(root->lchild);
find_path(root->rchild);
path.pop_back();
}
int pathSum(BTNode* root)
{
if (!root) return 0;
find_path(root);
for (auto p : paths)
{
int len = p.size();
WPL += (len - 1) * p[len - 1]->data;
}
return WPL;
}
八、祖先问题
1. 找到二叉树中任意两个结点的最近公共祖先(顺序+链式)
// 1. 顺序存储的二叉树的最近的公共祖先问题
// BTNode的val为char型变量,不然不好区分啥表示的空结点
int find_LCA(BTNode T[], int i, int j)
{
if (T[i].data != '#' && T[j].data != '#') // 如果为空结点,直接return 0
{
while (i != j)
{
if (i > j) i /= 2;
else j /= 2;
}
return i;
}
else return 0;
}
// 2. 链式存储的二叉树的最近公共祖先问题
// 方法1(我自己AC的版本,听过讲解感觉还是较难理解。。)
BTNode* find_LCA(BTNode* root, BTNode* p, BTNode* q)
{
if (!root) return NULL;
if (root == p || root == q) return root;
BTNode* lanc = find_LCA(root->lchild, p, q);
BTNode* ranc = find_LCA(root->rchild, p, q);
if (lanc && ranc) return root;
if (lanc) return lanc;
return ranc;
}
// 方法2(后续非递归序列,也AC了,很好理解,写着冗杂)
stack<BTNode*> find_ancestors(BTNode* root, BTNode* t)
{
stack<BTNode*> s;
BTNode *p = root, *r = NULL;
while (p || s.size())
{
if (p)
{
s.push(p);
p = p->lchild;
}
else
{
p = s.top();
if (p == t) break;
if (p->rchild && p->rchild != r) p = p->rchild;
else
{
s.pop();
r = p;
p = NULL;
}
}
}
return s;
}
BTNode* find_LCA(BTNode* root, BTNode* p, BTNode* q) {
if (!root) return NULL;
stack<BTNode*> s1, s2;
s1 = find_ancestors(root, p); // 放p和他的祖先们
s2 = find_ancestors(root, q); // 放q和他的祖先们
unordered_set<BTNode*> h;
while (s1.size())
{
auto t = s1.top();
s1.pop();
h.insert(t);
}
while (s2.size())
{
auto t = s2.top();
s2.pop();
if (h.count(t)) return t;
}
return NULL;
}
2. 打印值为x的结点的所有祖先
参考链接,这个博主文字解说部分写得好
无题目链接
// 方法1
// 适合于寻找二叉树中只有一个结点值为x的结点的祖先
void find_ancestors(BTNode* root, int x)
{
if (!root) return;
stack<BTNode*> s;
BTNode *p = root, *r = NULL;
while (p || s.size())
{
if (p)
{
s.push(p);
p = p->lchild;
}
else
{
p = s.top();
if (p->data == x)
{
s.pop();
while (s.size())
{
cout << s.top() << ' ';
s.pop();
}
return;
}
if (p->rchild && p->rchild != r)
p = p->rchild;
else
{
s.pop();
r = p;
p = NULL;
}
}
}
}
// 方法2(自己想的,核心部分已经写了,但有些尾巴没写完)
// 适合于寻找多个结点值为x的结点的全部的祖先/“残缺”路径
vector<int> path;
vector<vector<int>> paths;
void dfs(BTNode* root, int x)
{
if (!root) return;
path.push_back(root->data);
if (root->data == x)
{
// for (auto p : path) cout << p << ' ';
// puts("");
paths.push_back(path);
}
if (root->lchild) dfs(root->lchild, x);
if (root->rchild) dfs(root->rchild, x);
path.pop_back();
}
最后——杂七杂八
1. 将二叉树的叶结点按从左到右的顺序连成一个单链表,叶结点右指针域存放单链表指针(左—>右)(天勤P171)
无题目链接
// 注意!此处dummy和cur都是链表的结点,
// 但为了跟叶子结点的二叉链表结构(有lchild和rchild域)对应,其原来的next域用rchild表示
// 尾插法创建链表
auto dummy = new LNode(-1); // 虚拟头结点
LNode *cur = dummy;
LNode* inorder(BTNode* root)
{
if (!root) return NULL;
inorder(root->lchild); // 中序遍历左子树
if (!root->lchild && !root->rchild) // 叶结点
{
cur->rchild = root;
cur = root;
}
inorder(root->rchild); // 中序遍历右子树
cur->rchild = NULL; // 设置链表尾
return dummy;
}
2. 将二叉树的所有结点的左右子树进行交换
BTNode* Mirror(BTNode* root)
{
if (!root) return NULL;
swap(root->lchild, root->rchild);
Mirror(root->lchild);
Mirror(root->rchild);
// 交换语句在这也可以:
// swap(root->lchild, root->rchild);
return root;
}
3. 对于树中每个元素值为x的结点,删除以它为根的子树,释放相应空间(王道P149)
无题目链接
void delete_tree(BTNode* root)
{
if (!root) return;
delete_tree(root->lchild);
delete_tree(root->rchild);
free(root);
}
void levelorder(BTNode* root, int x)
{
if (!root) return;
if (root->data == x)
{
delete_tree(root);
return;
}
queue<BTNode*> q;
q.push(root);
while (q.size())
{
auto t = q.front();
q.pop();
if (t->lchild)
{
if (t->lchild->data == x)
{
delete_tree(t->lchild);
t->lchild = NULL;
}
else q.push(t->lchild);
}
if (t->rchild)
{
if (t->rchild->data == x)
{
delete_tree(t->rchild);
t->rchild = NULL;
}
else q.push(t->rchild);
}
}
}
4. 从大到小输出BST中所有值不小于k的关键字
无题目链接
void print(BTNode* root, int k)
{
if(!root) return;
print(root->rchild, k);
if(root->data >= k) cout << root->data << ' ';
print(root->lchild, k);
}