二叉树 20 道经典题,一篇讲透所有套路(附完整代码示例)
二叉树是算法学习中最核心的数据结构之一,它几乎贯穿了结构理解、递归、动态规划、搜索和路径问题 的各个方面。如果你想在算法面试或者竞赛中拔高,二叉树的题型必须彻底掌握。本篇文章将通过 20 道经典题,帮你建立一套完整的二叉树解题思路体系。
一、建树问题
题目 1:前序 + 中序建树
核心思路:
-
前序第一个节点是根
-
在中序中找到根,划分左右子树
-
递归构建左右子树
示例代码:
TreeNode* buildTree(string preorder, string inorder, unordered_map<char,int>& mp, int preStart, int inStart, int inEnd) {
if (preStart >= preorder.size() || inStart > inEnd) return nullptr;
char rootVal = preorder[preStart];
TreeNode* root = new TreeNode(rootVal);
int pos = mp[rootVal];
root->left = buildTree(preorder, inorder, mp, preStart+1, inStart, pos-1);
root->right = buildTree(preorder, inorder, mp, preStart+pos-inStart+1, pos+1, inEnd);
return root;
}
题目 2:中序 + 后序建树
核心与前序类似,只是根节点是后序的最后一个。
二、遍历套路
题目 3:前序遍历
void preorder(TreeNode* root) {
if (!root) return;
cout << root->val;
preorder(root->left);
preorder(root->right);
}
题目 4:中序遍历
void inorder(TreeNode* root) {
if (!root) return;
inorder(root->left);
cout << root->val;
inorder(root->right);
}
题目 5:后序遍历
void postorder(TreeNode* root) {
if (!root) return;
postorder(root->left);
postorder(root->right);
cout << root->val;
}
题目 6:层序遍历(BFS)
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if (!root) return res;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
int sz = q.size();
vector<int> level;
for (int i = 0; i < sz; i++) {
TreeNode* node = q.front(); q.pop();
level.push_back(node->val);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
res.push_back(level);
}
return res;
}
三、树的变形问题
题目 7:翻转二叉树
TreeNode* invertTree(TreeNode* root) {
if (!root) return nullptr;
swap(root->left, root->right);
invertTree(root->left);
invertTree(root->right);
return root;
}
题目 8:对称二叉树
bool isSymmetric(TreeNode* root) {
if (!root) return true;
return isMirror(root->left, root->right);
}
bool isMirror(TreeNode* t1, TreeNode* t2) {
if (!t1 && !t2) return true;
if (!t1 || !t2) return false;
return (t1->val==t2->val) && isMirror(t1->left, t2->right) && isMirror(t1->right, t2->left);
}
题目 9:右视图
vector<int> rightSideView(TreeNode* root) {
vector<int> res;
if (!root) return res;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
int sz = q.size();
for (int i = 0; i < sz; i++) {
TreeNode* node = q.front(); q.pop();
if (i == sz-1) res.push_back(node->val);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
}
return res;
}
题目 10:最大路径和
int maxPathSum(TreeNode* root) {
int max_sum = INT_MIN;
function<int(TreeNode*)> dfs = [&](TreeNode* node){
if(!node) return 0;
int left = max(0, dfs(node->left));
int right = max(0, dfs(node->right));
max_sum = max(max_sum, left+right+node->val);
return node->val + max(left, right);
};
dfs(root);
return max_sum;
}
四、二叉搜索树特有题
题目 11:验证 BST
bool isValidBST(TreeNode* root) {
return helper(root, LONG_MIN, LONG_MAX);
}
bool helper(TreeNode* node, long min_val, long max_val){
if(!node) return true;
if(node->val<=min_val || node->val>=max_val) return false;
return helper(node->left,min_val,node->val) && helper(node->right,node->val,max_val);
}
题目 12:第 k 小元素
int kthSmallest(TreeNode* root, int k) {
int count=0, result=0;
function<void(TreeNode*)> inorder = [&](TreeNode* node){
if(!node) return;
inorder(node->left);
count++;
if(count==k) { result=node->val; return; }
inorder(node->right);
};
inorder(root);
return result;
}
题目 13:最小高度 BST(有序数组建树)
TreeNode* sortedArrayToBST(vector<int>& nums, int l, int r) {
if(l>r) return nullptr;
int mid = l + (r-l)/2;
TreeNode* root = new TreeNode(nums[mid]);
root->left = sortedArrayToBST(nums,l,mid-1);
root->right = sortedArrayToBST(nums,mid+1,r);
return root;
}
五、树的路径类问题
题目 14:路径总和
判断是否存在根到叶子节点路径和等于 target:
bool hasPathSum(TreeNode* root, int sum) {
if(!root) return false;
if(!root->left && !root->right) return root->val==sum;
return hasPathSum(root->left,sum-root->val) || hasPathSum(root->right,sum-root->val);
}
题目 15:所有路径
void dfs(TreeNode* root,int sum,vector<int>& path, vector<vector<int>>& res){
if(!root) return;
path.push_back(root->val);
if(!root->left && !root->right) res.push_back(path);
dfs(root->left,sum,path,res);
dfs(root->right,sum,path,res);
path.pop_back();
}
六、特殊树形操作
题目 16:展开二叉树为链表
void flatten(TreeNode* root) {
TreeNode* node=root;
while(node){
if(node->left){
TreeNode* pre=node->left;
while(pre->right) pre=pre->right;
pre->right=node->right;
node->right=node->left;
node->left=nullptr;
}
node=node->right;
}
}
题目 17:左右子树交换(镜像)
与翻转二叉树一致
题目 18:子树判断
bool isSubtree(TreeNode* s, TreeNode* t) {
if(!s) return false;
return isSameTree(s,t) || isSubtree(s->left,t) || isSubtree(s->right,t);
}
bool isSameTree(TreeNode* a, TreeNode* b){
if(!a && !b) return true;
if(!a || !b) return false;
if(a->val != b->val) return false;
return isSameTree(a->left,b->left) && isSameTree(a->right,b->right);
}
题目 19:统计二叉树节点数
int countNodes(TreeNode* root) {
if(!root) return 0;
return 1+countNodes(root->left)+countNodes(root->right);
}
题目 20:最大深度 / 最小深度
int maxDepth(TreeNode* root){
if(!root) return 0;
return 1+max(maxDepth(root->left),maxDepth(root->right));
}
int minDepth(TreeNode* root){
if(!root) return 0;
return 1+min(minDepth(root->left),minDepth(root->right));
}
七、总结
通过以上 20 道题,你可以看到:
-
建树是所有树题的基础
-
遍历是信息获取和递归的核心
-
BST 的特性能大幅简化搜索类问题
-
路径类和动态规划类题目本质是对子树结果进行组合
-
特殊操作题往往是遍历 + 小技巧