c++ 二叉树

二叉树定义

二叉树(Binary Tree)是一种树形结构,每个节点最多有两个子节点。通常,二叉树的节点包括以下几个部分:

  • (Value):节点的内容。
  • 左子树(Left Child):指向左子节点的指针。
  • 右子树(Right Child):指向右子节点的指针。

二叉树的基本结构在C++中通常通过结构体来表示:

struct TreeNode {
    int val;       // 节点值
    TreeNode* left;  // 左子节点
    TreeNode* right; // 右子节点
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}  // 构造函数
};

二叉树的类型

满二叉树(Full Binary Tree)

  • 每个节点要么没有子节点,要么恰好有两个子节点。
  • 也就是说,除了叶子节点,其他节点的左右子树都有节点。

完全二叉树(Complete Binary Tree)

  • 除了最底层外,其他层的节点数都达到最大,并且最底层的节点都集中在最左边。
  • 完全二叉树的特点是:第 i 层最多有 2^(i-1) 个节点,第 n 层最多有 2^(n-1) 个节点。

二叉搜索树(Binary Search Tree, BST)

  • 二叉搜索树是一种特殊的二叉树,满足:对于树中的每个节点,左子树的节点值小于该节点,右子树的节点值大于该节点。

平衡二叉树(Balanced Binary Tree)

  • 平衡二叉树的定义是,任意节点的左右子树的高度差不超过1。AVL树是平衡二叉树的一种。

满二叉树与完全二叉树的区别

  • 满二叉树要求每个节点都有0或2个子节点,而完全二叉树只要求除了最后一层,其他层都满。
  • 完全二叉树的叶子节点必须从左到右排列。

二叉树的操作

前序遍历(Preorder Traversal)

遍历顺序:根 -> 左 -> 右

  • 递归实现
void preorder(TreeNode* root) {
    if (!root) return;
    cout << root->val << " ";
    preorder(root->left);
    preorder(root->right);
}
  • 非递归实现
void preorderNonRecursive(TreeNode* root) {
    if (root == NULL) return;
    stack<TreeNode*> s;
    s.push(root);
    while (!s.empty()) {
        TreeNode* node = s.top();
        s.pop();
        cout << node->val << " ";  // 访问节点
        if (node->right) s.push(node->right); // 右子树先入栈
        if (node->left) s.push(node->left);   // 左子树后入栈
    }
}

中序遍历(Inorder Traversal)

遍历顺序:左 -> 根 -> 右

  • 递归实现
void inorder(TreeNode* root) {
    if (!root) return;
    inorder(root->left);
    cout << root->val << " ";
    inorder(root->right);
}
  • 非递归实现
void inorderNonRecursive(TreeNode* root) {
    stack<TreeNode*> s;
    TreeNode* node = root;
    while (node != NULL || !s.empty()) {
        while (node != NULL) {
            s.push(node);
            node = node->left;
        }
        node = s.top();
        s.pop();
        cout << node->val << " ";  // 访问节点
        node = node->right;
    }
}

后序遍历(Postorder Traversal)

左 -> 右 -> 根。

  • 递归实现
void postorder(TreeNode* root) {
    if (!root) return;
    postorder(root->left);
    postorder(root->right);
    cout << root->val << " ";
}
  • 非递归实现
void postorderNonRecursive(TreeNode* root) {
    if (root == NULL) return;
    stack<TreeNode*> s1, s2;
    s1.push(root);
    while (!s1.empty()) {
        TreeNode* node = s1.top();
        s1.pop();
        s2.push(node);
        if (node->left) s1.push(node->left);
        if (node->right) s1.push(node->right);
    }
    while (!s2.empty()) {
        cout << s2.top()->val << " ";  // 访问节点
        s2.pop();
    }
}

层序遍历(Level-order Traversal)

层序遍历是广度优先遍历,即按层次访问二叉树的节点。

void levelOrder(TreeNode* root) {
    if (!root) return;
    queue<TreeNode*> q;
    q.push(root);
    while (!q.empty()) {
        TreeNode* node = q.front();
        q.pop();
        cout << node->val << " ";
        if (node->left) q.push(node->left);
        if (node->right) q.push(node->right);
    }
}

二叉搜索树(BST)的插入操作

二叉搜索树(BST)具有以下特性:左子树节点值小于根节点值,右子树节点值大于根节点值。

TreeNode* insert(TreeNode* root, int val) {
    if (!root) return new TreeNode(val);
    if (val < root->val) {
        root->left = insert(root->left, val);
    } else {
        root->right = insert(root->right, val);
    }
    return root;
}

判断二叉树是否是平衡二叉树

bool isBalanced(TreeNode* root) {
    return height(root) != -1;
}

int height(TreeNode* root) {
    if (root == NULL) return 0;
    int leftHeight = height(root->left);
    if (leftHeight == -1) return -1;  // 如果左子树不平衡
    int rightHeight = height(root->right);
    if (rightHeight == -1) return -1;  // 如果右子树不平衡
    if (abs(leftHeight - rightHeight) > 1) return -1;  // 如果左右子树高度差大于1,不平衡
    return max(leftHeight, rightHeight) + 1;
}

查找节点

TreeNode* search(TreeNode* root, int val) {
    if (!root || root->val == val) return root;
    if (val < root->val) return search(root->left, val);
    return search(root->right, val);
}

二叉树删除

删除节点时,分为三种情况:

  1. 要删除的节点是叶子节点(没有子节点)。
  2. 要删除的节点只有一个子节点。
  3. 要删除的节点有两个子节点。
TreeNode* deleteNode(TreeNode* root, int val) {
    if (root == NULL) return root;

    // 找到要删除的节点
    if (val < root->val) {
        root->left = deleteNode(root->left, val);
    } else if (val > root->val) {
        root->right = deleteNode(root->right, val);
    } else {
        // 当前节点就是要删除的节点
        if (root->left == NULL) {
            TreeNode* temp = root->right;
            delete root;
            return temp;
        } else if (root->right == NULL) {
            TreeNode* temp = root->left;
            delete root;
            return temp;
        }

        // 如果当前节点有两个子节点
        TreeNode* temp = findMin(root->right);  // 找到右子树中的最小节点
        root->val = temp->val;  // 替换当前节点的值
        root->right = deleteNode(root->right, temp->val);  // 删除右子树中的最小节点
    }
    return root;
}

// 找到最小节点(左子树最深的节点)
TreeNode* findMin(TreeNode* root) {
    while (root && root->left) {
        root = root->left;
    }
    return root;
}

二叉树的性质

二叉树的高度(深度)

  • 树的高度是从根节点到最远叶子节点的最长路径的长度。
  • 叶子节点的高度为0,根节点的高度是树的高度。
int maxDepth(TreeNode* root) {
    if (!root) return 0;
    return 1 + max(maxDepth(root->left), maxDepth(root->right));
}

二叉树的节点数

  • 如果二叉树的高度为 h,则该树的最大节点数为 2^h - 1
  • 二叉树的节点数等于树的左子树和右子树节点数的和加1。

完全二叉树的节点数

  • 完全二叉树的节点数可以通过层次遍历计算。

二叉树应用

二叉搜索树(Binary Search Tree, BST)

二叉搜索树是一种特殊的二叉树,满足以下性质:

  • 对于树中的每个节点,左子树的节点值小于该节点,右子树的节点值大于该节点。
  • 二叉搜索树的操作如查找、插入和删除可以在平均O(logN)时间内完成。
#include <iostream>
using namespace std;

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

// 插入节点
TreeNode* insertBST(TreeNode* root, int val) {
    if (!root) return new TreeNode(val);
    if (val < root->val) {
        root->left = insertBST(root->left, val);
    } else {
        root->right = insertBST(root->right, val);
    }
    return root;
}

// 查找节点
TreeNode* searchBST(TreeNode* root, int val) {
    if (!root || root->val == val) return root;
    if (val < root->val) return searchBST(root->left, val);
    return searchBST(root->right, val);
}

// 中序遍历
void inorder(TreeNode* root) {
    if (!root) return;
    inorder(root->left);
    cout << root->val << " ";
    inorder(root->right);
}

int main() {
    TreeNode* root = nullptr;
    root = insertBST(root, 50);
    insertBST(root, 30);
    insertBST(root, 70);
    insertBST(root, 20);
    insertBST(root, 40);
    insertBST(root, 60);
    insertBST(root, 80);

    cout << "Inorder Traversal: ";
    inorder(root); // 输出:20 30 40 50 60 70 80

    int val = 40;
    TreeNode* node = searchBST(root, val);
    if (node) {
        cout << "\nNode with value " << val << " found." << endl;
    } else {
        cout << "\nNode with value " << val << " not found." << endl;
    }

    return 0;
}

最大深度(高度)

二叉树的最大深度是从根节点到最远叶子节点的最长路径的长度。

int maxDepth(TreeNode* root) {
    if (!root) return 0;
    return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}

int main() {
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->left->left = new TreeNode(4);
    root->left->right = new TreeNode(5);

    cout << "Max Depth: " << maxDepth(root) << endl; // 输出:3
    return 0;
}

平衡二叉树(AVL树)

平衡二叉树(AVL树)是一种自平衡的二叉搜索树。对于AVL树,每个节点的左右子树的高度差的绝对值不超过1。

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    int height;  // 高度
    TreeNode(int x) : val(x), left(nullptr), right(nullptr), height(1) {}
};

// 获取节点的高度
int height(TreeNode* node) {
    return node ? node->height : 0;
}

// 获取平衡因子
int getBalance(TreeNode* node) {
    return node ? height(node->left) - height(node->right) : 0;
}

// 右旋
TreeNode* rightRotate(TreeNode* y) {
    TreeNode* x = y->left;
    TreeNode* 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;
}

// 左旋
TreeNode* leftRotate(TreeNode* x) {
    TreeNode* y = x->right;
    TreeNode* T2 = y->left;
    y->left = x;
    x->right = T2;
    x->height = max(height(x->left), height(x->right)) + 1;
    y->height = max(height(y->left), height(y->right)) + 1;
    return y;
}

// 插入节点并保持平衡
TreeNode* insert(TreeNode* node, int val) {
    if (!node) return new TreeNode(val);
    if (val < node->val) {
        node->left = insert(node->left, val);
    } else if (val > node->val) {
        node->right = insert(node->right, val);
    } else {
        return node; // 相等的值不插入
    }

    // 更新当前节点的高度
    node->height = max(height(node->left), height(node->right)) + 1;

    // 获取平衡因子
    int balance = getBalance(node);

    // 左子树不平衡
    if (balance > 1 && val < node->left->val) return rightRotate(node);
    
    // 右子树不平衡
    if (balance < -1 && val > node->right->val) return leftRotate(node);
    
    // 左右子树不平衡
    if (balance > 1 && val > node->left->val) {
        node->left = leftRotate(node->left);
        return rightRotate(node);
    }
    
    // 右左子树不平衡
    if (balance < -1 && val < node->right->val) {
        node->right = rightRotate(node->right);
        return leftRotate(node);
    }

    return node;
}

int main() {
    TreeNode* root = nullptr;
    root = insert(root, 30);
    root = insert(root, 20);
    root = insert(root, 40);
    root = insert(root, 10); // 会触发左旋

    cout << "Root after balancing: " << root->val << endl; // 输出:20
    return 0;
}

最小公共祖先(Lowest Common Ancestor, LCA)

给定二叉树的两个节点,找出它们的最低公共祖先。

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);

    if (left && right) return root;  // 如果p和q分别位于左右子树
    return left ? left : right;  // 否则返回非空的那个子树
}

int main() {
    TreeNode* root = new TreeNode(3);
    root->left = new TreeNode(5);
    root->right = new TreeNode(1);
    root->left->left = new TreeNode(6);
    root->left->right = new TreeNode(2);
    root->right->left = new TreeNode(0);
    root->right->right = new TreeNode(8);
    root->left->right->left = new TreeNode(7);
    root->left->right->right = new TreeNode(4);

    TreeNode* p = root->left;  // 节点5
    TreeNode* q = root->left->right->right;  // 节点4

    TreeNode* lca = lowestCommonAncestor(root, p, q);
    cout << "Lowest Common Ancestor: " << lca->val << endl; // 输出:5
    return 0;
}

路径总和(Path Sum)

给定一个二叉树和一个目标值,判断是否存在从根到叶的路径,使得路径上的所有节点值之和等于目标值。

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);
}

int main() {
    TreeNode* root = new TreeNode(5);
    root->left = new TreeNode(4);
    root->right = new TreeNode(8);
    root->left->left = new TreeNode(11);
    root->right->left = new TreeNode(13);
    root->right->right = new TreeNode(4);
    root->left->left->left = new TreeNode(7);
    root->left->left->right = new TreeNode(2);
    root->right->right->right = new TreeNode(1);

    int target = 22;
    cout << "Has Path Sum: " << hasPathSum(root, target) << endl; // 输出:1 (true)
    return 0;
}

Huffman 编码

Huffman 编码是一种贪心算法,用于数据压缩,特别是在无损数据压缩中。它通过给频率较高的字符分配较短的编码,而给频率较低的字符分配较长的编码,从而减少了数据的总体大小。

Huffman 编码是基于 二叉树 的原理构建的,具体来说,它使用 二叉树 来表示字符及其对应的编码。通过构建一个特定的二叉树(Huffman 树),可以生成最优的编码方案。

Huffman 编码步骤

  1. 统计字符频率:计算每个字符在文本中出现的频率。
  2. 构建优先队列:使用最小堆(优先队列)来存储频率和字符的对应关系,频率最低的节点具有最高的优先级。
  3. 构建 Huffman 树:
    • 每次从队列中取出两个频率最小的节点,合并成一个新的节点,并将新节点的频率设置为两个节点频率的和。将合并后的节点重新插入队列。
    • 重复这个过程,直到队列中只剩下一个节点,这个节点就是 Huffman 树的根节点。
  4. 生成编码:从 Huffman 树的根节点开始遍历,左边分配 0,右边分配 1,直到到达叶子节点,叶子节点的路径就是对应字符的 Huffman 编码。

二叉树和 Huffman 编码算法

#include <iostream>
#include <queue>
#include <unordered_map>
#include <vector>

using namespace std;

// 定义 Huffman 树的节点结构
struct HuffmanNode {
    char ch;                  // 字符
    int freq;                 // 字符的频率
    HuffmanNode* left;        // 左子节点
    HuffmanNode* right;       // 右子节点

    // 构造函数
    HuffmanNode(char c, int f) : ch(c), freq(f), left(nullptr), right(nullptr) {}

    // 重载比较运算符,用于优先队列
    bool operator>(const HuffmanNode& other) const {
        return freq > other.freq;
    }
};

// 构建 Huffman 树
HuffmanNode* buildHuffmanTree(const unordered_map<char, int>& freqMap) {
    priority_queue<HuffmanNode, vector<HuffmanNode>, greater<HuffmanNode>> minHeap;

    // 将频率表中的字符和频率加入到优先队列
    for (const auto& pair : freqMap) {
        minHeap.push(HuffmanNode(pair.first, pair.second));
    }

    // 构建 Huffman 树
    while (minHeap.size() > 1) {
        // 取出频率最小的两个节点
        HuffmanNode* left = new HuffmanNode(minHeap.top());
        minHeap.pop();
        HuffmanNode* right = new HuffmanNode(minHeap.top());
        minHeap.pop();

        // 创建一个新节点,频率为左子树和右子树的频率之和
        HuffmanNode* newNode = new HuffmanNode('\0', left->freq + right->freq);
        newNode->left = left;
        newNode->right = right;

        // 将新节点加入到队列中
        minHeap.push(*newNode);
    }

    // 返回最终的 Huffman 树的根节点
    return new HuffmanNode(minHeap.top());
}

// 递归生成编码
void generateHuffmanCodes(HuffmanNode* root, const string& str, unordered_map<char, string>& huffmanCodes) {
    if (!root) return;

    // 如果是叶子节点,则保存该字符的编码
    if (root->ch != '\0') {
        huffmanCodes[root->ch] = str;
    }

    // 递归地为左子树和右子树生成编码
    generateHuffmanCodes(root->left, str + "0", huffmanCodes);
    generateHuffmanCodes(root->right, str + "1", huffmanCodes);
}

// 主函数:执行 Huffman 编码
int main() {
    string input = "this is an example for huffman encoding";

    // 统计每个字符的频率
    unordered_map<char, int> freqMap;
    for (char ch : input) {
        freqMap[ch]++;
    }

    // 构建 Huffman 树
    HuffmanNode* root = buildHuffmanTree(freqMap);

    // 生成 Huffman 编码
    unordered_map<char, string> huffmanCodes;
    generateHuffmanCodes(root, "", huffmanCodes);

    // 输出生成的 Huffman 编码
    cout << "Huffman Codes:\n";
    for (const auto& pair : huffmanCodes) {
        cout << pair.first << ": " << pair.second << endl;
    }

    // 编码字符串
    string encodedStr = "";
    for (char ch : input) {
        encodedStr += huffmanCodes[ch];
    }

    cout << "\nEncoded String: " << encodedStr << endl;

    return 0;
}

表达式树

表达式树(Expression Tree)是一种特殊的二叉树,其中每个叶子节点代表操作数(如常数或变量),而每个非叶子节点代表操作符(如 +, -, *, / 等)。表达式树用于表示数学表达式,能够方便地执行数学运算、转换为后缀表达式(逆波兰表示法)等操作。

构建表达式树

  1. 叶子节点:表示操作数(例如,数字、变量)。
  2. 非叶子节点:表示操作符(例如,+, -, *, / 等)。

通过二叉树的结构可以轻松地表示表达式,并且通过树的遍历可以方便地求值或者生成不同的表示形式。

如何构建表达式树

假设给定一个中缀表达式(如 a + b * c),我们可以通过以下步骤构建表达式树:

  1. 中缀表达式解析:根据操作符的优先级和括号来构建树,低优先级操作符会成为根节点,高优先级操作符会作为子节点。
  2. 递归构建树:从最内层的操作开始,逐步构建表达式树。

表达式树实现

#include <iostream>
#include <stack>
#include <cctype>
#include <cmath>

using namespace std;

// 定义表达式树的节点
struct TreeNode {
    char value;  // 操作符或者操作数
    TreeNode* left;
    TreeNode* right;
    TreeNode(char val) : value(val), left(nullptr), right(nullptr) {}
};

int precedence(char op) {
    if (op == '+' || op == '-') return 1;
    if (op == '*' || op == '/') return 2;
    return 0; // 其他操作符(比如括号)的优先级
}

// 创建表达式树的函数
TreeNode* constructExpressionTree(const string& expression) {
    stack<TreeNode*> values;  // 存储操作数的栈
    stack<TreeNode*> operators;  // 存储操作符的栈

    for (char ch : expression) {
        if (isspace(ch)) continue;  // 跳过空格
        if (isdigit(ch)) {  // 如果是操作数
            values.push(new TreeNode(ch));
        } else if (ch == '(') {
            operators.push(new TreeNode(ch));  // 左括号
        } else if (ch == ')') {
            // 处理右括号:直到遇到左括号
            while (!operators.empty() && operators.top()->value != '(') {
                TreeNode* opNode = operators.top();
                operators.pop();
                TreeNode* right = values.top();
                values.pop();
                TreeNode* left = values.top();
                values.pop();
                opNode->left = left;
                opNode->right = right;
                values.push(opNode);  // 将合并的节点压入值栈
            }
            operators.pop();  // 弹出 '('
        } else {  // 如果是操作符
            while (!operators.empty() && precedence(operators.top()->value) >= precedence(ch)) {
                TreeNode* opNode = operators.top();
                operators.pop();
                TreeNode* right = values.top();
                values.pop();
                TreeNode* left = values.top();
                values.pop();
                opNode->left = left;
                opNode->right = right;
                values.push(opNode);
            }
            operators.push(new TreeNode(ch));  // 当前操作符入栈
        }
    }

    // 处理栈中剩余的操作符
    while (!operators.empty()) {
        TreeNode* opNode = operators.top();
        operators.pop();
        TreeNode* right = values.top();
        values.pop();
        TreeNode* left = values.top();
        values.pop();
        opNode->left = left;
        opNode->right = right;
        values.push(opNode);
    }

    return values.top();  // 返回根节点
}

void inorderTraversal(TreeNode* root) {
    if (!root) return;
    inorderTraversal(root->left);
    cout << root->value;
    inorderTraversal(root->right);
}

int evaluateExpressionTree(TreeNode* root) {
    if (!root) return 0;

    // 如果是操作数(数字)
    if (!root->left && !root->right) {
        return root->value - '0';  // 将字符转换为整数
    }

    // 递归计算左右子树的值
    int leftValue = evaluateExpressionTree(root->left);
    int rightValue = evaluateExpressionTree(root->right);

    // 根据操作符计算结果
    switch (root->value) {
        case '+': return leftValue + rightValue;
        case '-': return leftValue - rightValue;
        case '*': return leftValue * rightValue;
        case '/': return leftValue / rightValue;
    }
    return 0;  // 如果是无效操作符
}

int main() {
    string expression = "3 + 5 * (2 - 8)";  // 示例中缀表达式
    TreeNode* root = constructExpressionTree(expression);

    cout << "Inorder traversal (original expression): ";
    inorderTraversal(root);
    cout << endl;

    int result = evaluateExpressionTree(root);
    cout << "Result of expression evaluation: " << result << endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值