二叉树的主要实现与操作函数使用(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等多个领域,是所有程序员必须熟练掌握的核心基础。因此, 应熟练掌握对树的各种函数操作与实际用例,并融入进项目开发中。

posted @ 2026-04-11 22:29  阿尹想学会C++  阅读(10)  评论(0)    收藏  举报