二叉树的主要实现与操作函数使用(C++)
二叉树的主要实现与操作函数使用
1.二叉树
二叉树是每个节点最多有两个子节点的树形结构,主要由左子树,右子树,以及节点值构成一个结构,适用于动态数据集合的快速检索。同时也可实现优先队列和堆排序。
及如下结构:
1 struct TreeNode {
2 int val;// 节点值
3 TreeNode* left;//左子树
4 TreeNode* right; //右子树
5 TreeNode() : val(0), left(nullptr), right(nullptr){}
6 TreeNode(int n) : val(n), left(nullptr), right(nullptr){}
7 TreeNode(int n, TreeNode* left, TreeNode* right) : val(n), left(left), right(right){}//函数构造
8 };
9
10 TreeNode* build(vector<int>& preorder, int& index, int count) {
11 if (index >= count) return nullptr;// count为数组大小
12 int val = preorder[index++];
13 if (val == 0) return nullptr;
14 TreeNode* root = new TreeNode(val);
15 root->left = build(preorder, index, count);
16 root->right = build(preorder, index, count);
17 return root;
18 }// 数组建树
二叉树是数据结构中最常用、最重要的树结构。
2.操作函数
树的遍历
树的遍历是指按照某种顺序访问树中所有节点一次且仅一次的过程。根据访问节点的顺序不同,可分为深度优先遍历(DFS)和广度优先遍历(BFS)两大类。
2.1 先序遍历(根 >> 左 >> 右 DFS)
1 void preorder(TreeNode* root, vector<int>& result) {
2 if (!root) return;// 递归终止条件:如果当前节点为空指针,则直接返回,不进行任何操作
3 result.push_back(root->val); // 1. 访问根节点:将当前节点的值添加到结果数组中
4 preorder(root->left, result);// 2. 递归遍历左子树:以当前节点的左孩子为根,继续执行前序遍历
5 preorder(root->right, result);// 3. 递归遍历右子树:以当前节点的右孩子为根,继续执行前序遍历
6 }
2.2 中序遍历(左 >> 根 >> 右 DFS)
1 void inorder(TreeNode* root, vector<int>& result) {
2 if (!root) return;// 递归终止条件:如果当前节点为空指针,则直接返回,不进行任何操作
3 inorder(root->left, result);// 1. 递归遍历左子树:以当前节点的左孩子为根,继续执行中序遍历
4 result.push_back(root->val); // 2. 访问根节点:将当前节点的值添加到结果数组中
5 inorder(root->right, result);// 3. 递归遍历右子树:以当前节点的右孩子为根,继续执行中序遍历
6 }
2.3 后序遍历(左 >> 右 >> 根 DFS)
1 void postorder(TreeNode* root, vector<int>& result) {
2 if (!root) return;// 递归终止条件:如果当前节点为空指针,则直接返回,不进行任何操作
3 postorder(root->left, result);// 1. 递归遍历左子树:以当前节点的左孩子为根,继续执行后序遍历
4 postorder(root->right, result);// 2. 递归遍历右子树:以当前节点的右孩子为根,继续执行后序遍历
5 result.push_back(root->val); // 3. 访问根节点:将当前节点的值添加到结果数组中
6 }
2.4 层序遍历(BFS)
1 void levelorder(TreeNode* root, vector<int>& result) {
2 if (!root) return;// 递归终止条件:如果当前节点为空指针,则直接返回,不进行任何操作
3 queue<TreeNode*> q; // 创建队列用于存储待访问的节点
4 q.push(root); // 将根节点加入队列,作为遍历的起点
5 while (!q.empty()) {// 当队列为空时,表示所有节点都已访问完毕
6 TreeNode* current = q.front();// 1. 取出队首节点:获取当前要访问的节点
7 q.pop();// 2. 将队首节点从队列中移除
8 result.push_back(current->val);// 3. 访问当前节点:将节点值添加到结果数组中
9 if (current->left) q.push(current->left);// 4. 将当前节点的左孩子加入队列(如果存在)
10 if (current->right) q.push(current->right); // 5. 将当前节点的右孩子加入队列(如果存在)
11 }
12 }
叶子节点数
叶子节点是指没有子节点的节点(即左孩子和右孩子都为空)。
1 int countLeaves(TreeNode* root) {
2 if (!root) return 0;// 如果当前节点为空指针,说明到达了树的底部(虚拟节点)// 叶子节点的定义:没有左孩子 且 没有右孩子 // 返回1表示发现了一个叶子节点
3 if (!root->left && !root->right) {// 叶子节点的定义:没有左孩子 且 没有右孩子 // 返回1表示发现了一个叶子节点
4 return 1; // 返回1表示发现了一个叶子节点
5 }
6 return countLeaves(root->left) + countLeaves(root->right);// 当前节点不是叶子节点,需要继续向下查找
7 }
树的高度
树的高度(Height)是指从根节点到最远叶子节点的路径上的边数(或节点数,取决于定义)。
1 int getHeight(TreeNode* root) {
2 if (!root) return 0;// 如果当前节点为空指针,说明到达了树的底部
3 int leftHeight = getHeight(root->left); // 函数会一直向下递归,直到左子树为空时返回0
4 int rightHeight = getHeight(root->right);// 同理,计算右子树的高度
5 return max(leftHeight, rightHeight) + 1; // 计算公式:max(左子树高度, 右子树高度) + 1
6 }
节点查找(BST)
节点查找是在二叉树中根据给定的值,找到对应的节点。
1 TreeNode* searchBST(TreeNode* root, int key) {
2 if (!root || root->val == key) {
3 return root;
4 }// 条件1:!root 表示当前节点为空,说明搜索到叶子节点的下方仍未找到
// 条件2:root->val == key 表示当前节点的值等于目标值,查找成功
// 无论是哪种情况,都不需要继续递归,直接返回 root
5 if (key < root->val) {
6 return searchBST(root->left, key);
7 }// 利用BST性质:左子树中所有节点的值都小于根节点
// 因此目标值只可能在左子树中,无需搜索右子树
// 递归在左子树中继续查找
8 else {
9 return searchBST(root->right, key);
10 }// 同理,利用BST性质:右子树中所有节点的值都大于根节点
// 因此目标值只可能在右子树中
// 递归在右子树中继续查找
11 }
3.实际问题
哈夫曼树
哈夫曼树(Huffman Tree)是一种带权路径长度最短的二叉树,也称为最优二叉树。它在数据压缩、编码优化等领域有着极其重要的应用。
#include <bits/stdc++.h>
using namespace std;
struct HuffmanNode {
int data; // 数据(整数)
int freq; // 频率/权重
HuffmanNode* left;
HuffmanNode* right;
HuffmanNode(int d, int f) : data(d), freq(f), left(nullptr), right(nullptr) {}
HuffmanNode(int f) : data(0), freq(f), left(nullptr), right(nullptr) {}
};
// 比较函数(用于优先队列,最小堆)
struct Compare {
bool operator()(HuffmanNode* a, HuffmanNode* b) {
return a->freq > b->freq;
}
};
// 构建哈夫曼树
HuffmanNode* buildHuffmanTree(vector<pair<int, int>>& nums) {
priority_queue<HuffmanNode*, vector<HuffmanNode*>, Compare> pq;
// 创建叶子节点并加入优先队列
for (auto& p : nums) {
pq.push(new HuffmanNode(p.first, p.second));
}
// 构建哈夫曼树
while (pq.size() > 1) {
HuffmanNode* left = pq.top(); pq.pop();
HuffmanNode* right = pq.top(); pq.pop();
HuffmanNode* parent = new HuffmanNode(left->freq + right->freq);
parent->left = left;
parent->right = right;
pq.push(parent);
}
return pq.top();
}
// 生成哈夫曼编码(整数到二进制字符串)
void generateCodes(HuffmanNode* root, string code, unordered_map<int, string>& huffmanCode) {
if (!root) return;
// 叶子节点
if (!root->left && !root->right) {
huffmanCode[root->data] = code;
return;
}
generateCodes(root->left, code + "0", huffmanCode);
generateCodes(root->right, code + "1", huffmanCode);
}
// 计算带权路径长度(WPL)
int calculateWPL(HuffmanNode* root, int depth = 0) {
if (!root) return 0;
// 叶子节点
if (!root->left && !root->right) {
return root->freq * depth;
}
return calculateWPL(root->left, depth + 1) + calculateWPL(root->right, depth + 1);
}
// 编码整数数组
string encode(const vector<int>& nums, unordered_map<int, string>& huffmanCode) {
string encoded = "";
for (int num : nums) {
encoded += huffmanCode[num];
}
return encoded;
}
// 译码
vector<int> decode(HuffmanNode* root, const string& encoded) {
vector<int> decoded;
HuffmanNode* current = root;
for (char bit : encoded) {
if (bit == '0') {
current = current->left;
} else {
current = current->right;
}
// 到达叶子节点
if (!current->left && !current->right) {
decoded.push_back(current->data);
current = root;
}
}
return decoded;
}
// 释放内存
void deleteHuffmanTree(HuffmanNode* root) {
if (!root) return;
deleteHuffmanTree(root->left);
deleteHuffmanTree(root->right);
delete root;
}
// 打印哈夫曼树(前序遍历)
void printHuffmanTree(HuffmanNode* root, int depth = 0) {
if (!root) return;
for (int i = 0; i < depth; i++) cout << " ";
if (!root->left && !root->right) {
cout << "叶子节点[" << root->data << ":" << root->freq << "]" << endl;
} else {
cout << "内部节点(" << root->freq << ")" << endl;
}
printHuffmanTree(root->left, depth + 1);
printHuffmanTree(root->right, depth + 1);
}
int main() {
// 示例1:整数数据及其频率
vector<pair<int, int>> nums = {
{1, 5}, {2, 9}, {3, 12}, {4, 13}, {5, 16}, {6, 45}
};
cout << "=== 构建哈夫曼树(整数数据) ===" << endl;
cout << "数据 (数值, 权重):" << endl;
for (auto& p : nums) {
cout << " " << p.first << " : " << p.second << endl;
}
HuffmanNode* root = buildHuffmanTree(nums);
cout << "\n哈夫曼树结构:" << endl;
printHuffmanTree(root);
// 生成哈夫曼编码
unordered_map<int, string> huffmanCode;
generateCodes(root, "", huffmanCode);
cout << "\n哈夫曼编码:" << endl;
for (auto& p : huffmanCode) {
cout << p.first << " : " << p.second << endl;
}
// 计算WPL
int wpl = calculateWPL(root);
cout << "\n带权路径长度(WPL): " << wpl << endl;
// 编码示例
vector<int> testNums = {1, 2, 3, 4, 5, 6};
cout << "\n原始数据: ";
for (int num : testNums) cout << num << " ";
cout << endl;
string encoded = encode(testNums, huffmanCode);
cout << "编码结果: " << encoded << endl;
vector<int> decoded = decode(root, encoded);
cout << "译码结果: ";
for (int num : decoded) cout << num << " ";
cout << endl;
// 示例2:统计整数数组中各数字出现频率
cout << "\n=== 统计整数频率 ===" << endl;
vector<int> input = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 1, 1, 2};
cout << "输入数组: ";
for (int num : input) cout << num << " ";
cout << endl;
unordered_map<int, int> freqMap;
for (int num : input) {
freqMap[num]++;
}
vector<pair<int, int>> freqNums;
cout << "\n频率统计:" << endl;
for (auto& p : freqMap) {
freqNums.push_back(p);
cout << " " << p.first << " 出现 " << p.second << " 次" << endl;
}
HuffmanNode* root2 = buildHuffmanTree(freqNums);
unordered_map<int, string> huffmanCode2;
generateCodes(root2, "", huffmanCode2);
cout << "\n哈夫曼编码:" << endl;
for (auto& p : huffmanCode2) {
cout << p.first << " : " << p.second << endl;
}
string encoded2 = encode(input, huffmanCode2);
cout << "\n编码后长度: " << encoded2.length() << " 位" << endl;
cout << "原始定长编码长度: " << input.size() * 3 << " 位 (假设用3位表示0-7)" << endl;
cout << "压缩率: " << (double)encoded2.length() / (input.size() * 3) * 100 << "%" << endl;
// 译码验证
vector<int> decoded2 = decode(root2, encoded2);
cout << "\n译码验证: ";
for (int num : decoded2) cout << num << " ";
cout << endl;
// 释放内存
deleteHuffmanTree(root);
deleteHuffmanTree(root2);
}
总结
二叉树不仅是一种数据结构,更是一种递归式分治思维的具象化模型。它连接了数学、算法、系统、工程、AI等多个领域,是所有程序员必须熟练掌握的核心基础。因此, 应熟练掌握对树的各种函数操作与实际用例,并融入进项目开发中。

浙公网安备 33010602011771号