数据结构 -- 树(附C++代码实现,前序,后序,中序遍历,树的深度,包括递归和非递归遍历 附完整C++代码)

一.什么是树呢?有什么比较出名的树的应用呢?

树是一种数据结构,它由若干个节点(node)组成,并通过边(edge)互相连接。树具有以下特点:

1. 每个节点只有一个父节点,除根节点外。
2. 每个节点可以有多个子节点。
3. 根节点是唯一的,没有父节点。
4. 除了根节点之外,每个节点都有唯一的路径连接到根节点。

树广泛用于计算机科学中,例如在搜索算法、操作系统、编译器以及数据库等领域中都有应用。常见的树结构有二叉树、AVL树、B树等。

我们这篇介绍的就是二叉树

二.什么是二叉树呢?二叉树的形态?

二叉树是一种特殊的树结构,每个节点最多有两个子节点,分别称为左子节点和右子节点。

结点的度:左右节点各为一个度

叶子结点或者终端结点:度为0的结点
树的度:结点的最大分支数
树的深度(高度)---层数
二叉树形态: 空树,根节点,根节点+左子树  根结点+右子树  根+左+右

根据树的结点和树的形状,分为几个形态:斜树,满二叉树和完全二叉树

三.C++实现中类的事件

        1.节点类的设计

class Node {
public:
    Node() : m_left(nullptr), m_right(nullptr) {};

    Node(char v) : m_value(v), m_right(nullptr), m_left(nullptr) {};
    char m_value;
    Node *m_left;
    Node *m_right;
};

        我们在节点类中定义了两个构造方法,有参和无参构造。成员分别是存储的数据,节点的左右孩子的指针。

        当然,正常情况我们使用的数据肯定不是某个类型,大概会是某个类。俺们这时写的小demo会是很简单的例子。 

        2.树类的设计

class Tree {
public:
    Tree() : m_root(nullptr) {};//树的构造方法

    void CreateTree(const char *&str) {
        CreateTree(m_root, str);
    }//构造树的方法,实现方法不在类中实现。代码量有点小大,不适合卸载类中。

    void CreateTree(Node *&root, const char *&str);//这个是真正实现构造树的方法

    void preOrder(Node *root);//树的前序递归遍历,实现也在类外。下同,不再赘述。

    void preOrder_1();//树的前序非递归遍历(栈实现),类外。

    void inOrder(Node *root);//树的中序递归遍历,类外。

    void inOrder_1();//树的中序非递归遍历(栈实现),类外。

    void PastOrder(Node *root);//树的后序递归遍历,类外。

    void PastOrder_1();//树的后序非递归遍历,类外。

    int Size() { return Size(m_root); }//树的节点个数,这个是调用下面的方法。

    int Size(Node *root);//树的个数

    int Height() { return Height(m_root); }//求树的高度,和Size()方法类似。

    int Height(Node *root);//树的高度求法具体实现。

    Node *Find(char v) { return Find(m_root, v); }//和上面两个方法实现类似。

    Node *Find(Node *root, char v);//树的高度求法具体实现。

    void LevelOrder();//树当前所在层的位置。

    Node *m_root;//树的根节点指针。
};

        每个函数功能在上面啰嗦过了昂,之后俺使用的时候不会就来看这个。

        3.树类的函数之 -- 构造树的函数

void Tree::CreateTree(Node *&root, const char *&str) {
    if (*str == '*') {
        root = nullptr;
    } else {
        root = new Node(*str);
        CreateTree(root->m_left, ++str);
        CreateTree(root->m_right, ++str);
    }
}

        我们构造树的时候用的字符串为"ABDH**I**EJ***CF**G**",大家如果想换成其他的话,也可以比如'#',只需要把第二行哪个字符换掉就可以了。

        至于传的参数,为什么是指针+const,为了减小值传递对内存的消耗,而const是为了递归深入的时候每一次共享当前位置。

        4.前序遍历的两个方法 -- 递归和非递归遍历树

        

void Tree::preOrder(Node *root) {
    if (root != nullptr) {
        cout << root->m_value << " ";
        preOrder(root->m_left);
        preOrder(root->m_right);
    }
    cout<<endl;
}
void Tree::preOrder_1() const {
    stack<Node*> pre;
    Node* top= nullptr;
    if(m_root!= nullptr){
        pre.push(m_root);
        while(!pre.empty()){
            top = pre.top();
            pre.pop();
            cout<<top->m_value<<" ";
            if(top->m_right!= nullptr){
                pre.push(top->m_right);
            }
            if(top->m_left!= nullptr){
                pre.push(top->m_left);
            }
        }
    }
    cout<<endl;
}

        上面的是递归遍历,这个就不多说了把。递归的过程就是栈帧的开辟和回退。

        说一下后面的非递归遍历把,我们需要一个栈来存储两个节点。为什么先将右孩子入栈呢?因为栈是后入先出的,我们需要先遍历左孩子┗|`O′|┛ 嗷~~。所以后入左孩子。每次都要将当前的栈顶元素保存起来,防止出栈后找不到。

        5.中序遍历树 -- 递归遍历和非递归遍历(和前序遍历基本一样,只是)

void Tree::inOrder(Node *root) {
    if (root != nullptr) {
        inOrder(root->m_left);
        cout << root->m_value << " ";
        inOrder(root->m_right);
    }
    cout<<endl;
}
void Tree::inOrder_1() {
    if (m_root != nullptr)
    {
        stack<Node*> ss;
        Node* p = m_root;
        Node* top = nullptr;
        while (p != nullptr || !ss.empty())
        {
            while (p != nullptr)
            {
                ss.push(p);
                p = p->m_left;
            }
            if (!ss.empty())
            {
                top = ss.top();
                ss.pop();
                cout << top->m_value << " ";
                p = top->m_right;
            }
        }
    }
    cout << endl;
}

        同样,递归的方法我们不去赘述。

        非递归的方法,我们同样是定义了一个栈。此时我们的循环进入条件加了一个或栈不为空,因为开始的话。我们栈中没有元素(前序是因为可以直接将根节点入栈,我们不需要加)OK,解释结束。循环内的while循环是先找到当前节点的左孩子,直到树的叶子。而if内是进行输出,每次输出栈的一个节点。

        6.后序遍历树 -- 递归遍历和非递归遍历

        

void Tree::PastOrder(Node *root) {
    if (root != nullptr) {
        PastOrder(root->m_left);
        PastOrder(root->m_right);
        cout << root->m_value << " ";
    }
}
void Tree::PastOrder_1() {
    if (m_root != nullptr)
    {
        stack<Node*> ss;
        Node* top, * pre = nullptr;
        ss.push(m_root);
        while (!ss.empty())
        {
            top = ss.top();
            if ((top->m_left == nullptr && top->m_right == nullptr) ||
                (pre != nullptr && top->m_left == pre) ||
                (pre != nullptr && top->m_right == pre))
            {
                ss.pop();
                cout << top->m_value << " ";
                pre = top;
            }
            else
            {
                if (top->m_right != nullptr)
                    ss.push(top->m_right);
                if (top->m_left != nullptr)
                    ss.push(top->m_left);
            }
        }
    }
    cout << endl;
}

        还是不说递归遍历┗|`O′|┛ 嗷~~(因为只有几行),细说一下非递归遍历吧。

        首先入栈的是根节点,因为根是最后输出的,栈也是先入后出,所以先入根。我们此时定义了一个pre指针,pre此时指向的是当前栈顶元素。因为出栈完左右孩子后又回到了左右孩子的父节点,防止他出现死循环,我们需要判断他的左右孩子是否已经输出了,输出的话用pre进行标记。

        7.树的高度 -- 递归遍历

int Tree::Height(Node *root) {
    if (root == nullptr) {
        return 0;
    } else {
        int lefth = Height(root->m_left);
        int righth = Height(root->m_right);
        return lefth > righth ? (lefth + 1) : (righth + 1);
    }
}

        相当于遍历了一次完整的树,每次遍历到叶子节点都得判断是否是最大的节点。

        8.树的结点个数 -- 递归遍历(特别像斐波那契数列的递归求解代码写法)

int Tree::Size(Node *root) {
    if (root == nullptr)
        return 0;
    else
        return Size(root->m_left) + Size(root->m_right) + 1;
}

        9.元素的查找 -- 递归查找

Node *Tree::Find(Node *root, char v) {
    if (root == nullptr)
        return nullptr;
    if (root->m_value == v)
        return root;
    Node *p = Find(root->m_left, v);
    if (p == nullptr)
        return Find(root->m_right, v);
    else
        return p;
}

        10.树的层级遍历 -- 每层每层遍历,非递归

        

void Tree::LevelOrder()
{
    if (m_root != nullptr)
    {
        queue<Node*> qq;
        qq.push(m_root);
        Node* front = nullptr;
        while (!qq.empty())
        {
            front = qq.front();
            qq.pop();
            cout << front->m_value << " ";
            if (front->m_left != nullptr)
                qq.push(front->m_left);
            if (front->m_right != nullptr)
                qq.push(front->m_right);
        }
        cout << endl;
    }
}

四.附录(完整代码) 

//
// Created by 18751 on 2023/6/3.
//
#include<iostream>
#include <stack>
#include<queue>

using namespace std;

class Node {
public:
    Node() : m_left(nullptr), m_right(nullptr) {};

    Node(char v) : m_value(v), m_right(nullptr), m_left(nullptr) {};
    char m_value;
    Node *m_left;
    Node *m_right;
};

class Tree {
public:
    Tree() : m_root(nullptr) {};

    void CreateTree(const char *&str) {
        CreateTree(m_root, str);
    }

    void CreateTree(Node *&root, const char *&str);

    void preOrder(Node *root);

    void preOrder_1() const;

    void inOrder(Node *root);

    void inOrder_1();

    void PastOrder(Node *root);

    void PastOrder_1();

    int Size() { return Size(m_root); }

    int Size(Node *root);

    int Height() { return Height(m_root); }

    int Height(Node *root);

    Node *Find(char v) { return Find(m_root, v); }

    Node *Find(Node *root, char v);

    void LevelOrder();

    Node *m_root;
};

void Tree::CreateTree(Node *&root, const char *&str) {
    if (*str == '*') {
        root = nullptr;
    } else {
        root = new Node(*str);
        CreateTree(root->m_left, ++str);
        CreateTree(root->m_right, ++str);
    }
}

void Tree::preOrder(Node *root) {
    if (root != nullptr) {
        cout << root->m_value << " ";
        preOrder(root->m_left);
        preOrder(root->m_right);
    }
    cout<<endl;
}
void Tree::preOrder_1() const {
    stack<Node*> pre;
    Node* top= nullptr;
    if(m_root!= nullptr){
        pre.push(m_root);
        while(!pre.empty()){
            top = pre.top();
            pre.pop();
            cout<<top->m_value<<" ";
            if(top->m_right!= nullptr){
                pre.push(top->m_right);
            }
            if(top->m_left!= nullptr){
                pre.push(top->m_left);
            }
        }
    }
    cout<<endl;
}
void Tree::inOrder(Node *root) {
    if (root != nullptr) {
        inOrder(root->m_left);
        cout << root->m_value << " ";
        inOrder(root->m_right);
    }
    cout<<endl;
}
void Tree::inOrder_1() {
    if (m_root != nullptr)
    {
        stack<Node*> ss;
        Node* p = m_root;
        Node* top = nullptr;
        while (p != nullptr || !ss.empty())
        {
            while (p != nullptr)
            {
                ss.push(p);
                p = p->m_left;
            }
            if (!ss.empty())
            {
                top = ss.top();
                ss.pop();
                cout << top->m_value << " ";
                p = top->m_right;
            }
        }
    }
    cout << endl;
}


void Tree::PastOrder(Node *root) {
    if (root != nullptr) {
        PastOrder(root->m_left);
        PastOrder(root->m_right);
        cout << root->m_value << " ";
    }
}
void Tree::PastOrder_1() {
    if (m_root != nullptr)
    {
        stack<Node*> ss;
        Node* top, * pre = nullptr;
        ss.push(m_root);
        while (!ss.empty())
        {
            top = ss.top();
            if ((top->m_left == nullptr && top->m_right == nullptr) ||
                (pre != nullptr && top->m_left == pre) ||
                (pre != nullptr && top->m_right == pre))
            {
                ss.pop();
                cout << top->m_value << " ";
                pre = top;
            }
            else
            {
                if (top->m_right != nullptr)
                    ss.push(top->m_right);
                if (top->m_left != nullptr)
                    ss.push(top->m_left);
            }
        }
    }
    cout << endl;
}

int Tree::Height(Node *root) {
    if (root == nullptr) {
        return 0;
    } else {
        int lefth = Height(root->m_left);
        int righth = Height(root->m_right);
        return lefth > righth ? (lefth + 1) : (righth + 1);
    }
}

int Tree::Size(Node *root) {
    if (root == nullptr)
        return 0;
    else
        return Size(root->m_left) + Size(root->m_right) + 1;
}

Node *Tree::Find(Node *root, char v) {
    if (root == nullptr)
        return nullptr;
    if (root->m_value == v)
        return root;
    Node *p = Find(root->m_left, v);
    if (p == nullptr)
        return Find(root->m_right, v);
    else
        return p;
}

void Tree::LevelOrder()
{
    if (m_root != nullptr)
    {
        queue<Node*> qq;
        qq.push(m_root);
        Node* front = nullptr;
        while (!qq.empty())
        {
            front = qq.front();
            qq.pop();
            cout << front->m_value << " ";
            if (front->m_left != nullptr)
                qq.push(front->m_left);
            if (front->m_right != nullptr)
                qq.push(front->m_right);
        }
        cout << endl;
    }
}

int main() {
    Tree t;
    const char* str = "ABDH**I**EJ***CF**G**";
    t.CreateTree(str);
    t.inOrder(t.m_root);
    cout<<endl;
    t.inOrder_1();
    t.PastOrder_1();
    t.LevelOrder();
    t.PastOrder_1();
}

//虚负凌云万丈才

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值