1. 二叉树基础概念与C++实现
二叉树是每个节点最多有两个子节点的树结构,在C++中通常通过指针链接实现。我们先看一个最简单的二叉树节点定义:
cpp复制struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
这个结构体包含三个成员:存储节点值的val,指向左子节点的left指针,以及指向右子节点的right指针。构造函数初始化节点值并将子节点指针设为nullptr,这是C++11引入的空指针常量,比传统的NULL更安全。
注意:现代C++推荐使用
nullptr而非NULL或0表示空指针,因为nullptr有明确的指针类型,能避免一些隐式类型转换的问题。
二叉树有多种特殊形态:
- 满二叉树:每个节点都有0或2个子节点
- 完全二叉树:除最后一层外完全填充,且最后一层节点靠左排列
- 二叉搜索树(BST):左子树所有节点值小于根节点,右子树所有节点值大于根节点
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 二叉树的遍历方法与实现
二叉树遍历是算法题中的常客,主要分为四种方式:
2.1 递归遍历实现
cpp复制// 前序遍历:根->左->右
void preorder(TreeNode* root) {
if(!root) return;
cout << root->val << " ";
preorder(root->left);
preorder(root->right);
}
// 中序遍历:左->根->右
void inorder(TreeNode* root) {
if(!root) return;
inorder(root->left);
cout << root->val << " ";
inorder(root->right);
}
// 后序遍历:左->右->根
void postorder(TreeNode* root) {
if(!root) return;
postorder(root->left);
postorder(root->right);
cout << root->val << " ";
}
递归实现简洁但存在栈溢出风险,对于深度很大的树不适用。
2.2 迭代遍历实现
以中序遍历为例的栈实现:
cpp复制vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> st;
TreeNode* curr = root;
while(curr || !st.empty()) {
while(curr) {
st.push(curr);
curr = curr->left;
}
curr = st.top(); st.pop();
res.push_back(curr->val);
curr = curr->right;
}
return res;
}
技巧:迭代遍历中,前序和中序相对简单,后序遍历最复杂,可以尝试"根->右->左"再反转结果的方式实现。
3. 二叉搜索树操作实战
二叉搜索树(BST)因其高效的查找性能而广泛应用,时间复杂度为O(h),h为树高。
3.1 BST查找实现
cpp复制TreeNode* searchBST(TreeNode* root, int val) {
while(root && root->val != val) {
root = val < root->val ? root->left : root->right;
}
return root;
}
3.2 BST插入操作
cpp复制TreeNode* insertIntoBST(TreeNode* root, int val) {
if(!root) return new TreeNode(val);
TreeNode* curr = root;
while(true) {
if(val < curr->val) {
if(!curr->left) {
curr->left = new TreeNode(val);
break;
}
curr = curr->left;
} else {
if(!curr->right) {
curr->right = new TreeNode(val);
break;
}
curr = curr->right;
}
}
return root;
}
3.3 BST删除操作
删除节点有三种情况:
- 无子节点:直接删除
- 有一个子节点:用子节点替代
- 有两个子节点:用右子树最小节点替代
cpp复制TreeNode* deleteNode(TreeNode* root, int key) {
if(!root) return nullptr;
if(key < root->val) {
root->left = deleteNode(root->left, key);
} else if(key > root->val) {
root->right = deleteNode(root->right, key);
} else {
if(!root->left) return root->right;
if(!root->right) return root->left;
TreeNode* minNode = findMin(root->right);
root->val = minNode->val;
root->right = deleteNode(root->right, minNode->val);
}
return root;
}
TreeNode* findMin(TreeNode* node) {
while(node->left) node = node->left;
return node;
}
4. 二叉树高级应用与优化
4.1 平衡二叉树实现
普通BST可能退化成链表,平衡二叉树(AVL)通过旋转保持平衡:
cpp复制class AVLTree {
struct Node {
int val, height;
Node *left, *right;
Node(int v) : val(v), height(1), left(nullptr), right(nullptr) {}
};
int height(Node* n) { return n ? n->height : 0; }
Node* rightRotate(Node* y) {
Node* x = y->left;
Node* T2 = x->right;
x->right = y;
y->left = T2;
y->height = max(height(y->left), height(y->right)) + 1;
x->height = max(height(x->left), height(x->right)) + 1;
return x;
}
// 类似实现leftRotate
// ...
};
4.2 二叉树序列化与反序列化
cpp复制string serialize(TreeNode* root) {
if(!root) return "null";
return to_string(root->val) + "," + serialize(root->left) + "," + serialize(root->right);
}
TreeNode* deserialize(string data) {
queue<string> q;
string s;
for(char c : data) {
if(c == ',') {
q.push(s);
s = "";
} else {
s += c;
}
}
if(!s.empty()) q.push(s);
return helper(q);
}
TreeNode* helper(queue<string>& q) {
string s = q.front(); q.pop();
if(s == "null") return nullptr;
TreeNode* root = new TreeNode(stoi(s));
root->left = helper(q);
root->right = helper(q);
return root;
}
4.3 二叉树直径计算
二叉树直径是任意两节点间的最长路径,可能不经过根节点:
cpp复制int diameterOfBinaryTree(TreeNode* root) {
int diameter = 0;
height(root, diameter);
return diameter;
}
int height(TreeNode* node, int& diameter) {
if(!node) return 0;
int lh = height(node->left, diameter);
int rh = height(node->right, diameter);
diameter = max(diameter, lh + rh);
return 1 + max(lh, rh);
}
5. 二叉树常见问题解析
5.1 最近公共祖先(LCA)
cpp复制TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(!root || root == p || root == q) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
return !left ? right : (!right ? left : root);
}
5.2 验证二叉搜索树
常见误区是只检查当前节点与子节点关系:
cpp复制bool isValidBST(TreeNode* root) {
return helper(root, LONG_MIN, LONG_MAX);
}
bool helper(TreeNode* root, long min, long max) {
if(!root) return true;
if(root->val <= min || root->val >= max) return false;
return helper(root->left, min, root->val) &&
helper(root->right, root->val, max);
}
5.3 二叉树层次遍历
cpp复制vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if(!root) return res;
queue<TreeNode*> q;
q.push(root);
while(!q.empty()) {
int size = q.size();
vector<int> level;
for(int i = 0; i < size; ++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;
}
6. 性能优化与内存管理
6.1 避免内存泄漏
cpp复制void deleteTree(TreeNode* root) {
if(!root) return;
deleteTree(root->left);
deleteTree(root->right);
delete root;
}
6.2 使用智能指针
现代C++推荐使用智能指针管理树节点:
cpp复制struct TreeNode {
int val;
unique_ptr<TreeNode> left;
unique_ptr<TreeNode> right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
6.3 缓存优化
对于频繁访问的树,可以考虑将节点存储在连续内存中:
cpp复制class CompactTree {
vector<int> nodes; // 按层次存储
// 实现各种遍历操作...
};
7. 实际工程中的应用案例
7.1 文件系统目录树
cpp复制class FileNode {
public:
string name;
bool isFile;
vector<unique_ptr<FileNode>> children;
// ...
};
7.2 表达式树
cpp复制class ExprNode {
public:
virtual double evaluate() const = 0;
};
class NumberNode : public ExprNode {
double value;
public:
explicit NumberNode(double val) : value(val) {}
double evaluate() const override { return value; }
};
class AddNode : public ExprNode {
unique_ptr<ExprNode> left, right;
public:
AddNode(unique_ptr<ExprNode> l, unique_ptr<ExprNode> r)
: left(move(l)), right(move(r)) {}
double evaluate() const override {
return left->evaluate() + right->evaluate();
}
};
7.3 游戏中的决策树
cpp复制class BehaviorNode {
public:
enum Status { Running, Success, Failure };
virtual Status update() = 0;
};
class SequenceNode : public BehaviorNode {
vector<unique_ptr<BehaviorNode>> children;
size_t current = 0;
public:
Status update() override {
while(current < children.size()) {
Status status = children[current]->update();
if(status != Success) return status;
++current;
}
current = 0;
return Success;
}
};
8. 调试技巧与测试方法
8.1 可视化打印二叉树
cpp复制void printTree(TreeNode* root, int space = 0, int gap = 5) {
if(!root) return;
space += gap;
printTree(root->right, space);
cout << endl;
for(int i = gap; i < space; ++i) cout << " ";
cout << root->val << "\n";
printTree(root->left, space);
}
8.2 单元测试框架集成
cpp复制TEST(BinaryTreeTest, InsertTest) {
TreeNode* root = nullptr;
root = insertBST(root, 5);
root = insertBST(root, 3);
root = insertBST(root, 7);
EXPECT_EQ(root->val, 5);
EXPECT_EQ(root->left->val, 3);
EXPECT_EQ(root->right->val, 7);
}
8.3 内存泄漏检测
使用Valgrind或AddressSanitizer检测:
bash复制g++ -fsanitize=address -g tree.cpp && ./a.out
