C++数据结构学习笔记(二)根据输入字符构造二叉树并进行前序遍历,中序遍历,后序遍历,层序遍历(链表)

                 强烈建议小白去B站搜索:懒猫老师


        二叉树可以通过顺序存储结构、链式存储结构、二维数组、邻接表的结构来存储,本文为较为简单直观,容易理解的链式存储结构的实现 。二叉链表的结构如图所示。

         首先定义一个节点结构体BiNode,data为数据域(可以改为模板类,能够兼容所有数据类型,此处使用char类型只做简单实现),lchild和rchild分别指向左右孩子。

struct BiNode
{
    char data;
    BiNode* lchild;
    BiNode* rchild;
};

        然后定义二叉树类BiTree,包含有参、无参构造函数,析构函数,以及四种遍历函数。定义的root根节点为私有成员变量,所以此处public的下的函数是不能直接对root进行访问的,public的函数再次调用private中的函数去对二叉树进行各种操作。这么写的好处是让类完全封装起来,只留下外部调用接口,安全性更高(我也不太懂,自己感觉的)。

class BiTree
{
private:
    BiNode* root;//指向根节点的头指针
public:
    BiTree(string &s) { root = Creat(root,s); };//构造函数,建立一颗二叉树
    BiTree() { root = Creat(root); };//构造函数,建立一颗二叉树
	~BiTree(){ Release(root);};//析构函数
    void PreOrder() { PreOrder(root); };//前序遍历二叉树
    void InOrder() { InOrder(root); };  //中序遍历二叉树
    void PostOrder() { PostOrder(root); };//后序遍历二叉树
    void LeverOrder();                     //层序遍历二叉树
private:
    BiNode* Creat(BiNode* bt);
    BiNode* Creat(BiNode* bt, string& s);//构造函数调用
    void Release(BiNode* bt);//析构函数调用
    void PreOrder(BiNode* bt);//前序遍历函数调用
    void InOrder(BiNode* bt);//中序遍历函数调用
    void PostOrder(BiNode* bt);//后序遍历函数调用
};

        无参构造函数直接返回空树,有参构造函数通过Public中的BiTree()调用的时候只需要有一个string类型变量s做参数即可,s为字符串。s为所需要二叉树的扩展二叉树的前序遍历序列。(为了建立一颗二叉树,将二叉树中的每个节点的空指针引出一个虚节点,其值为一个特定值,如“#”,以标识其为空,把这样处理后的二叉树称为原二叉树的扩展二叉树。)

        s传入到构造函数中,用迭代器取出字符串中的首个字符存入到ch中并删掉s的首个字符(意思就是挨个取),首先判断传入字符是否为“#”,若为“#”则说明该节点为空,直接返回。节点不为空的时候创建新节点并为data域赋值,然后递归调用Creat()依次构造左右子树。

//无参构造函数调用
BiNode* BiTree::Creat(BiNode* bt)
{
    bt = NULL;
    return bt;
}
//有参构造函数调用
BiNode* BiTree::Creat(BiNode* bt,string &s)
{
    string::iterator it = s.begin();
    char ch = *it;
    s.erase(0, 1);
    if (ch == '#')
        bt = NULL;
    else
    {
        bt = new BiNode;
        bt->data = ch;
        bt->lchild = Creat(bt->lchild,s);
        bt->rchild = Creat(bt->rchild,s);
    }
    cout << ch;
    return bt;
}

        前中后序遍历通过递归实现(也可以通过非递归实现,递归比较简单好理解) ,思想基本都一样。以前序遍历为例,传入参数为根节点,首先判断根节点是否为空,若为空则说明为空树(第二次递归调用时说明该节点为空,可以理解为空子树);若不为空则首先读取数据域,然后再递归依次读取左右子树。中序、后续同理,只是读取顺序有变化。

void BiTree::PreOrder(BiNode* bt)
{
    if (bt == NULL)
        return;
    else
    {
        cout << bt->data;
        PreOrder(bt->lchild);
        PreOrder(bt->rchild);
    }
}

void BiTree::InOrder(BiNode* bt)
{
    if (bt == NULL)
        return;
    else
    {
        InOrder(bt->lchild);
        cout << bt->data;
        InOrder(bt->rchild);
    }
}

void BiTree::PostOrder(BiNode* bt)
{
    if (bt == NULL)
        return;
    else
    {
        PostOrder(bt->lchild);
        PostOrder(bt->rchild);
        cout << bt->data;
    }
}

        层序遍历通过队列来实现,首先建立一个存取数据类型为BiNode的队列 ,用于暂存节点。遍历开始也是首先判断是否为空树,若不为空树则节点入队列。当队列不为空时,首先将队列的头节点——即最先入队的节点出队,输出该节点信息,然后判断该节点是否有左右子树,若有左右子树则依次入队。由于队是先进先出,所以能够实现按层从左到右读取。如果还是理解不了再次强烈建议B站观看懒猫老师视频!!!

void BiTree::LeverOrder()
{
    queue<BiNode*> q;
    if (this->root == NULL)
        return;
    q.push(this->root);
    while (!q.empty())
    {
        BiNode* q1 = q.front();
        q.pop();
        cout << q1->data;
        if (q1->lchild != NULL)
            q.push(q1->lchild);
        if (q1->rchild != NULL)
            q.push(q1->rchild);
    }
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 好的,我来为您介绍一下按照先序遍历的顺序建立链表的方法。 先序遍历的顺序是:根节点、左子树、右子树。因此,按照先序遍历的顺序建立链表的方法是: 1. 如果当前节点为空,则返回 NULL。 2. 创建一个新节点,将其值设置为当前节点的值。 3. 递归调用函数建立左子树,并将返回值设置为当前节点的左子节点。 4. 递归调用函数建立右子树,并将返回值设置为当前节点的右子节点。 5. 返回当前节点。 以下是按照先序遍历的顺序建立链表的示例代码: ``` #include <iostream> using namespace std; // 二叉树结构体 struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; // 按照先序遍历的顺序建立链表 TreeNode* buildTree() { int val; cin >> val; if (val == -1) { return NULL; } TreeNode* root = new TreeNode(val); root->left = buildTree(); root->right = buildTree(); return root; } // 先序遍历二叉树 void preorderTraversal(TreeNode* root) { if (root == NULL) { return; } cout << root->val << " "; preorderTraversal(root->left); preorderTraversal(root->right); } int main() { TreeNode* root = buildTree(); cout << "先序遍历结果:"; preorderTraversal(root); return 0; } ``` 输入样例: ``` 1 2 -1 -1 3 -1 -1 ``` 输出样例: ``` 先序遍历结果:1 2 3 ``` 希望这个方法能够帮助到您! ### 回答2: 按照先序遍历的顺序建立链表的方法如下: 1. 首先,创建一个指向二叉树结点的指针p,用于遍历二叉树。 2. 接下来,从先序遍历的序列中依次取出一个结点的值,创建一个新的二叉树结点,并将该结点的值赋给新结点的数据域。 3. 然后,判断链表是否为空。如果为空,将新结点作为链表的根结点。如果不为空,进行下一步。 4. 判断p指针所指向的结点的左子树是否为空。如果为空,将新结点作为p所指向结点的左子树。如果不为空,将p指针移动至其左子树。 5. 重复步骤2~4,直到先序遍历序列中的所有结点都被处理完毕。 这样,就能按照先序遍历的顺序建立一个链表。 需要注意的是,如果先序遍历序列中的某个位置的值为空(用特定符号表示),则表示该位置是一个空结点。在处理空结点时,可以将其左子树和右子树都设置为空。 这种建立链表的方法适用于已知先序遍历序列的情况,并且假设没有重复的结点值。如果有重复的结点值,则无法准确建立链表。 ### 回答3: 按照先序遍历的顺序建立链表,可以按照以下步骤进行: 1. 首先定义链表的节点结构,包括数据域和指向左右孩子的指针。 2. 初始化一个空的链表。 3. 读取先序遍历的序列,每读取一个元素,就创建一个新节点,并将读取的元素赋值给节点的数据域。 4. 对于每个新创建的节点,如果该节点是根节点,则直接将其插入为空的链表。 5. 如果该节点不是根节点,需要找到该节点的双亲节点。利用栈保存已经创建的节点,栈顶元素即为当前节点的双亲节点。将该节点链接到其双亲节点的左子树或右子树,根据先序遍历的规则判断。 6. 重复步骤3-5,直到读取完所有的元素。 7. 最后,返回构建完成的链表。 例如,假设先序遍历序列为A,B,D,#,#,E,#,G,#,#,C,#,F,#,#,#,其中#表示空节点。 根据以上步骤,首先创建节点A,将其作为根节点插入链表。 接下来创建节点B,将其链接到节点A的左子树。 再创建节点D,将其链接到节点B的左子树。 然后跳过两个连续的空节点。 继续创建节点E,将其链接到节点D的右子树。 再创建节点G,将其链接到节点E的左子树。 然后跳过两个连续的空节点。 继续创建节点C,将其链接到节点A的右子树。 接着跳过一个空节点。 最后创建节点F,将其链接到节点C的右子树。 完成上述操作后,得到的链表表示的先序遍历序列为A,B,D,#,E,G,#,#,#,C,#,F,#,#,#。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值