首页 > 编程语言 >二叉树常用代码合集【C++版】(详细注释)

二叉树常用代码合集【C++版】(详细注释)

时间:2024-11-09 12:08:02浏览次数:1  
标签:return cur get data C++ base 二叉树 str 合集

二叉树常用代码合集【C++版】(详细注释)
关键的地方有详细的注释。如果需要更详细的注释,可以丢给大模型再注释一遍。

#include <iostream>  
#include <memory>  
#include <string>  
#define mv(x) std::move(x)  
#define coutln(x) cout << x << endl  
  
using namespace std;  
  
template <typename E>  
class LinkStack {  
private:  
    typedef unsigned long long ull;  
    ull size = 0;  
  
private:  
    struct Node {  
        E data;  
        std::unique_ptr<Node> nxt; // 使用智能指针  
        explicit Node(E x) : data(x), nxt(nullptr) {}  
    };  
  
    struct LStack {  
        std::unique_ptr<Node> base;  
        Node* top; // top 仍然是裸指针,但它指向的是唯一拥有者的智能指针管理的对象  
    };  
  
    LStack stk;  
  
public:  
    LinkStack() {  
        stk.base = std::make_unique<Node>(E()); // 创建一个 base 节点  
        stk.top = stk.base.get(); // top 指向 base    }  
  
    ~LinkStack() {  
        // 智能指针会自动释放内存,因此不需要显式释放  
    }  
  
    [[maybe_unused]] [[nodiscard]] E &getTop() const {  
        if (!stk.top) {  
            throw std::out_of_range("Stack is empty.");  
        }  
        return stk.top->data;  
    }  
  
    void push(const E &x) {  
        auto newNode = std::make_unique<Node>(x); // 创建新节点  
        newNode->nxt = std::move(stk.base->nxt); // 转移 base 中的 nxt 链接  
        stk.base->nxt = std::move(newNode); // 新节点成为栈顶  
        stk.top = stk.base->nxt.get(); // top 更新为栈顶节点  
        ++size;  
    }  
  
    E pop() {  
        if (size == 0) {  
            throw std::out_of_range("Stack is empty.");  
        }  
        E x = stk.top->data;  
        stk.base->nxt = std::move(stk.top->nxt); // top 移动到下一个节点  
        stk.top = stk.base->nxt.get(); // 更新 top        --size;  
        return x;  
    }  
  
    E operator--() {  
        return pop();  
    }  
  
    LinkStack &operator+=(const E &rhs) {  
        push(rhs);  
        return *this;  
    }  
    E top() {  
        return stk.top->data;  
    }  
    bool empty() {  
        return !(bool )size;  
    }  
    ull get_size() {  
        return this->size;  
    }  
  
};  
  
template<typename E>  
class BiTree {  
private:  
    struct Node {  
        E data;  
        unique_ptr<Node> lchild, rchild;  
        Node(E x) : data(x), lchild(nullptr), rchild(nullptr) {}  
    };  
  
private:  
    unique_ptr<Node> root;  
public:  
    const string _sep; // 用一个空格分隔输出  
private:  
    string _in_ordered_traverse_str_;  
    string _pre_ordered_traverse_str_;  
    string _post_ordered_traverse_str_;  
public:  
    // 构造函数,根据根节点的数据初始化  
    BiTree(char root_data) {  
        if (root_data == '#') root = nullptr;  
        else {  
            root = make_unique<Node>(root_data);  
        }  
    }  
  
    // 构造函数,根据前序遍历的字符串生成二叉树  
    BiTree(string &pre_str) {  
        if (!pre_str.empty()) {  
            int index = 0;  
            generate_by_pre_str(root, pre_str, index);  
        }  
    }  
  
    // 中序遍历(非递归)  
    string &InOrderTraverse_nonRecursive() {  
        _in_ordered_traverse_str_ = "";  
        InOrderTraverse_1(root.get()); // 使用 get() 获取裸指针  
        return _in_ordered_traverse_str_;  
    }  
  
    string &InOrderTraverse() {  
        _in_ordered_traverse_str_ = "";  
        InOrderTraverse_2(root.get());  
        return _in_ordered_traverse_str_;  
    }  
  
    string &PreOrderTraverse() {  
        _pre_ordered_traverse_str_ = "";  
        PreOrderTraverse_1(root.get());  
        return _pre_ordered_traverse_str_;  
    }  
  
    string &PreOrderTraverse_nonRecursive() {  
        _pre_ordered_traverse_str_ = "";  
        PreOrderTraverse_2(root.get());  
        return _pre_ordered_traverse_str_;  
    }  
  
    string &PostOrderTraverse() {  
        _post_ordered_traverse_str_ = "";  
        PostOrderTraverse_1(root.get());  
        return _post_ordered_traverse_str_;  
    }  
  
    string &PostOrderTraverse_nonRecursive() {  
        _post_ordered_traverse_str_ = "";  
        PostOrderTraverse_2(root.get());  
        return _post_ordered_traverse_str_;  
    }  
    // 添加一个公共的显示接口  
    void displayTree() const {  
        displayTree(root.get());  
    }  
  
    [[nodiscard]] int get_depth() const {  
        return get_depth_2(root.get());  
    }  
    [[nodiscard]] int countNodes() const {  
        return coutNodes_nonRecursive(root.get());  
    }  
    [[nodiscard]] int countLeaves() const {  
        return countLeaves_nonRecursive(root.get());  
    }  
  
    [[nodiscard]] int evaluateInfixExpression() const {  
        return evaluateInfixExpression_(root.get());  
    }  
    [[nodiscard]] string toInfixExpression() const {  
        return toInfixExpression_2(root.get());  
    }  
private:  
    // 通过前序遍历字符串生成二叉树  
    void generate_by_pre_str(unique_ptr<Node>& base, string &pre_str, int& index) {  
        if (index >= pre_str.size() || pre_str[index] == '#') {  
            base = nullptr; // 设置为空  
            index++; // 移动到下一个字符  
        } else {  
            base = make_unique<Node>(pre_str[index]); // 创建新节点  
            index++; // 移动到下一个字符  
            generate_by_pre_str(base->lchild, pre_str, index); // 递归生成左子树  
            generate_by_pre_str(base->rchild, pre_str, index); // 递归生成右子树  
        }  
    }  
  
    // 中序遍历(非递归实现)  
    inline void InOrderTraverse_1(Node* base) {  
        /*实现逻辑:  
         * 使用stack模拟递归访问的路径。  
         * 中序遍历中的节点顺序是(left, current, right)  
         *         * 1. 一直往左访问,直到NULL(这里用data='#'表示)  
         * 2. 取出栈顶元素(记为current)(栈素储存的即是Node*)  
         * 3. 将current添加到_in_ordered_traverse_末尾  
         * 4. 访问右节点  
         * */        /*         * 简单来说,这段代码中,内层循环会一直向左枝前进,直到遇到nullptr  
         * 而stack是后入先出的,因此取出元素的过程刚好是(left, current)的顺序。  
         * 之后访问right,于是整个过程顺序就是(left, current, right)  
         * */        LinkStack<Node*> stack;  
        Node* current = base;  
  
        // 当栈不为空时,继续出栈。当current==NULL时,已经遍历到了最右的叶子以外了。  
        while (current != nullptr || !stack.empty()) {  
            // 将当前节点的左子节点依次压栈  
            while (current != nullptr) {  
                stack.push(current);  
                current = current->lchild.get();  
            }  
            // 当前节点为空(压栈循环被退出了),弹出栈顶元素并访问  
            current = stack.pop();  
//            coutln( current->data + _sep);  
            _in_ordered_traverse_str_ += current->data + _sep;  
            // 转向当前节点的右子节点  
            current = current->rchild.get();  
        }  
    }  
  
    // 中序遍历(递归)  
    void InOrderTraverse_2(Node* base) {  
        if (base/*!= nullptr*/) {  
            InOrderTraverse_1(base->lchild.get()); // 递归左子树  
            _in_ordered_traverse_str_ += base->data + _sep;  
            InOrderTraverse_1(base->rchild.get()); // 递归右子树  
        }  
    }  
  
    // 前序遍历(递归)  
    void PreOrderTraverse_1(Node *base) {  
        if (base /*!= nullptr*/) {  
            _pre_ordered_traverse_str_ += base->data + _sep;  
            PreOrderTraverse_1(base->lchild.get());  
            PreOrderTraverse_1(base->rchild.get());  
  
        }  
    }  
  
    // 前序遍历(非递归实现)  
    void PreOrderTraverse_2(Node *base) {  
        /*实现逻辑:  
         * 使用stack模拟递归访问的路径。  
         * 前序遍历的节点顺序是(current, left, right)  
         *         * 1. 一直往左访问,并记录数据,直到NULL(这里用data='#'表示)  
         * 2. 取出栈顶元素(记为current),访问它的右节点  
         * 3. 将current添加到_in_ordered_traverse_末尾  
         * 4. 访问右节点  
         * */        /*         * 简单来说,这段代码中,内层循环会一直向左枝前进,直到遇到nullptr  
         * 而stack是后入先出的,为了得到(current, left)的顺序,需要先记录数据再向左枝前进  
         * 之后再访问右枝。  
         * 于是整个过程顺序就是(current, left, right)  
         * */        LinkStack<Node *> stack;  
        Node *cur = base;  
        while (cur /*!= nullptr*/ || !stack.empty()) {  
            while (cur) {  
                _pre_ordered_traverse_str_ += cur->data + _sep;  
                stack.push(cur);  
                cur = cur->lchild.get();  
            }  
            cur = stack.pop();  
            cur = cur->rchild.get();  
  
        }  
    }  
  
    //后序遍历(递归)  
    void PostOrderTraverse_1(Node *base) {  
        if (base) {  
            PostOrderTraverse_1(base->lchild.get());  
            PostOrderTraverse_1(base->rchild.get());  
            _post_ordered_traverse_str_ += base->data + _sep;  
        }  
    }  
  
    //后序遍历(非递归)  
    void PostOrderTraverse_2(Node *base) {  
        if (!base) return;  
  
        LinkStack<Node *> stack;  
        Node *prev = nullptr;   // 上一个访问的节点  
        stack.push(base);  
  
        while (!stack.empty()) {  
            Node *cur = stack.top();  
            if ((!cur->lchild && !cur->rchild) || (prev && (prev == cur->lchild.get() || prev == cur->rchild.get()))) {  
                _post_ordered_traverse_str_ += cur->data + _sep;  
                stack.pop();  
                prev = cur;  
            } else {  
                // 先将右子节点压栈,再压左子节点  
                if (cur->rchild) stack.push(cur->rchild.get());  
                if (cur->lchild) stack.push(cur->lchild.get());  
            }  
        }  
    }  
  
    void displayTree(Node* node, const string& prefix = "", bool isLeft = true) {  
        if (!node) return;  
  
        // 输出当前节点  
        cout << prefix << (isLeft ? "|--" : u8"|-- ") << node->data << endl;  
  
        // 更新前缀,递归地处理左右子树  
        string newPrefix = prefix + (isLeft ? u8"|   " : u8"    ");  
        displayTree(node->lchild.get(), newPrefix, true);  
        displayTree(node->rchild.get(), newPrefix, false);  
    }  
  
    // 计算二叉树的深度(递归)  
    int get_depth_1(Node *base, int de) const {  
        if (!base) return de-1; // 如果已经到达了NULL,则这个路径的深度是上一个节点的深度  
        // 深度为左枝和右枝的最大深度  
        return max(get_depth_1(base->rchild.get(), de+1), get_depth_1(base->lchild.get(), de+1));;  
    }  
    int get_depth_2(Node *base) {  
        if (!base) return 0;  
        else return max(get_depth_2(base->lchild.get()), get_depth_2(base->rchild.get())) + 1;  
    }  
  
    int get_n_nodes_1() {  
        this->InOrderTraverse();  
        return this->_in_ordered_traverse_str_.length();  
    }  
  
    int coutNodes_Recursive(Node *base) {  
        // 节点个数 = 左子树节点个数 + 右子树节点个数 + 1(本节点)  
        if (!base) return 0;  
        else return coutNodes_Recursive(base->lchild.get()) + coutNodes_Recursive(base->rchild.get()) + 1;  
    }  
  
    int coutNodes_nonRecursive(Node *base) const {  
        LinkStack<Node *> stack;  
        Node *cur = base;  
        int n = 0;  
        while (cur /*!= nullptr*/ || !stack.empty()) {  
            while (cur) {  
                ++n;  
                stack.push(cur);  
                cur = cur->lchild.get();  
            }  
            cur = stack.pop();  
            cur = cur->rchild.get();  
        }  
        return n;  
    }  
  
    int countLeaves_Recursive(Node *base) const {  
        // 当前节点的分支下:总叶子数 = 左子树的叶子个数 + 右子树的叶子个数  
        if (!base->lchild && !base->rchild) return 1;   // 如果是叶子  
        else return countLeaves_Recursive(base->lchild.get()) + countLeaves_Recursive(base->rchild.get());  
    }  
  
    int countLeaves_nonRecursive(Node *base) const {  
        // 基于前序遍历计算 总叶子数  
        LinkStack<Node *> stack;  
        Node *cur = base;  
        int n = 0;  
        while (cur || !stack.empty()) {  
            while (cur) {  
                stack.push(cur);  
                cur = cur->lchild.get();  
            }  
            cur = stack.pop();  
            if (!cur->lchild && !cur->rchild) ++n;  
            cur = cur->rchild.get();  
        }  
        return n;  
    }  
private:  
    // 递归生成带括号的中缀表达式  
    [[nodiscard]] string toInfixExpression(Node* node) const {  
        if (!node) return "";  
  
        // 如果是叶节点,直接返回数据  
        if (!node->lchild && !node->rchild) {  
            return string(1, node->data);  
        }  
  
        // 左子树的表达式  
        string leftExpr = toInfixExpression(node->lchild.get());  
        // 右子树的表达式  
        string rightExpr = toInfixExpression(node->rchild.get());  
  
        // 如果节点有子节点,加上括号  
        return "(" + leftExpr + node->data + rightExpr + ")";  
    }  
  
    // 递归生成带括号的中缀表达式(简洁表达式版)  
    [[nodiscard]] string toInfixExpression_2(Node *base) const {  
        // 如果是叶节点,直接返回数据  
        if (!base->lchild && !base->rchild) {  
            return string(1, base->data);  
        }  
        // 左子树的表达式  
        string leftExpr = toInfixExpression_2(base->lchild.get());  
        // 右子树的表达式  
        string rightExpr = toInfixExpression_2(base->rchild.get());  
        if (rightExpr.length() == 1 || base->rchild->data == '*' || base->rchild->data == '/' || base->data == '+') {  
            // 如果满足任意一种情况(右节点是叶子,右节点运算符是*或/,当前节点运算符是+),则不用加括号。  
            // -+a##*b##-c##d##/e##f## -> a+b*(c-d)-e/f  
            return leftExpr + base->data + rightExpr;  
        } else {  
            return leftExpr + base->data + "(" + rightExpr + ")";  
        }  
    }  
  
    [[nodiscard]] int evaluateInfixExpression_(Node *base) const {  
//        if (!base) return 0;  
        // 如果是叶节点,直接返回数据  
//        coutln(base->data);  
        if (!base->rchild && !base->lchild) return (int ) (base->data & 0xf);    // ASCII转数字  
        else {  
            auto eval = [&](int a, int b, char opr) constexpr {  
                switch (opr) {  
                    case '+': return a + b;  
                    case '-': return a - b;  
                    case '*': return a * b;  
                    case '/': return a / b;  
                    default: return 0;  
                }  
            };  
            return eval(evaluateInfixExpression_(base->lchild.get()), evaluateInfixExpression_(base->rchild.get()), base->data);  
        }  
    }  
};  
  
  
int main() {  
    string str;  
    cin >> str; // 输入前序遍历字符串  
//    string debug_s1 = "ABC##DE#G##F###";  
    BiTree<char> bt(str); // 使用前序遍历字符串构造二叉树  
  
    coutln(bt.toInfixExpression());  
  
    return 0;  
}

标签:return,cur,get,data,C++,base,二叉树,str,合集
From: https://www.cnblogs.com/starless-sky/p/18536535

相关文章

  • C++算法练习-day38——106.从中序和后序遍历序列构造二叉树
    题目来源:.-力扣(LeetCode)题目思路分析题目要求根据一棵二叉树的中序遍历(inorder)和后序遍历(postorder)结果重建这棵二叉树。中序遍历的特点是左子树->根节点->右子树,而后序遍历的特点是左子树->右子树->根节点。利用这两个遍历的特点,我们可以递归地重建整棵树。后序......
  • RT DETR v2 TensorRT C++ 部署详解
    RT-DETRv2TensorRTC++部署详解概述随着深度学习技术的发展,目标检测算法在各种应用场景下展现出了卓越的表现。RT-DETRv2(Real-TimeDetectionTransformerv2)作为一款高效的实时目标检测模型,其结合了Transformer架构的优势与传统卷积神经网络(CNNs)的速度,为开发者提供了在......
  • 利用 C++ 开发经典 2D (超级马里奥)平台游戏(代码可用~)
    ......
  • 【华为OD技术面试手撕真题】82、环形链表II | 手撕真题+思路参考+代码解析(C & C++ & J
    文章目录一、题目......
  • 大整数相加[C++]
    0前言当我们遇到需要处理非常大的整数的情况时,标准的数据类型如int或longlongint可能无法满足需求,因为这些类型的数值范围有限。在这种情况下,我们需要一种方法来处理超出常规数据类型范围的大整数。本文将介绍如何使用C++实现大整数相加。1大整数相加的基本原理从最低位开......
  • 7-8 数据结构实验二 二叉树的遍历
    以二叉链表作存储结构,建立一棵二叉树。输出该二叉树的先序、中序、后序遍历序列,求出该二叉树的深度,并统计其叶子结点数。二叉链表的类型描述:typedefcharElemType;typedefstructBiNode{ElemTypedata;structBiNode*lchild,*rchild;}BiNode,*BiTree;......
  • 数学笑话合集
    Arxiv上一篇数学笑话收集,由数学家TanyaKhovanova撰写(2403.01010)文章内容摘要多年来,我一直在我的网站上收集和发布数学笑话。那里有超过400个笑话。在这篇论文中,它是我在G4G15会议上演讲的扩展版本,我想呈现其中的66个笑话。1数学家与幽默数学家非常逻辑和精确。这里有......
  • C++函数名后面有个const
    ‌函数名后面加const表示该函数是一个常成员函数,即该函数不会修改类的任何成员变量。‌在C++中,常成员函数通过在函数声明和定义后添加const关键字来标识。常成员函数不能修改类的任何成员变量,这保证了类的接口的稳定性。例如: classPoint{public:intGetX()const;//......
  • C++中的继承
    在C++中,继承的方式有三种:public、protected 和 private。它们控制了基类成员在派生类中的访问权限。以下是这三种继承方式的区别:1. public 继承基类的 public 成员在派生类中保持 public。基类的 protected 成员在派生类中保持 protected。基类的 private 成员......
  • C++中的std::shared_ptr
    std::shared_ptr 是C++11标准库中的智能指针类型,用于管理动态分配的对象。与传统指针不同,std::shared_ptr 自动管理内存,并在不再使用时自动释放对象,以避免内存泄漏。它是一种共享所有权的智能指针,即可以让多个 std::shared_ptr 指向同一个对象,并且会记录有多少个 std::shar......