C++实现 二叉树广义表表示和二叉树的转换

数据结构课上老师问二叉树怎么创建,我突然蒙蔽了一下。如果一个一个节点的指定,不仅慢而且内容太多不好处理。感觉广义表的表示方法比较好用,但是好像很少有写这个代码的,老师上课的代码也是一闪而过,所以就有了这篇文章。

首先给出二叉树定义:

struct node
{
    char ch;
    node *l, *r;

    node(char CH)
    {
        l = r = NULL;
        ch = CH;
    }
};

然后是广义表表示的内容:

const int maxn = 1e5 + 10;

char ch[maxn];
int len;

说起来二叉树,肯定会想着第一个解法是递归。 

定义是:dfs(node * &root,int &p) p用来指向字符串,root 是根节点。

这是调试了半天才整出来的代码,把注释删去了之后,确实很短,很符合递归的特点。

但是理解的成本相当高,不推荐。主要的难点是遇见逗号和右括号都需要return,不方便处理。


void dfs(node *&root, int &p)
{
    // 如果遇见逗号有两种情况,一个是左边是( 另一个是左边是字母
    // 如果是括号,说明后边有字母,不应该再加,把这个逗号留给上一层处理,不然会漏掉字母

    if (ch[p] == ',')
        return;
    else if (ch[p] == ')' || p >= len)
    {
        ++p;
        return;
    }
    else
    {
        root = new node(ch[p++]);

        if (ch[p] == '(')
        {
            dfs(root->l, ++p);

            if (ch[p] == ',')
                ++p;
            // 如果直接++p或者直接p
            //++p 可能会导致 (,C) 在返回时p指向C,导致漏过C
            // p 可能会导致 D(B,C) 在返回时p指向逗号,而后面还有一个字母,导致直接返回,漏掉字母

            dfs(root->r, p);
        }
        else // 如果字母后面是,或者) 直接返回上一层,统一处理
        {
            ++p;
            return;
        }
    }
}

反而是非递归解法更简单,更容易理解。

// 非递归写法,确实反而比递归写法更加简单而且容易理解
void another_answer(node *&root)
{
    if (len == 0) // 特判
    {
        root = NULL;
        return;
    }

    int n = 0; // n用来迭代字符串
    stack<node *> st;

    node *p = root;        // p指针用来迭代
    p = new node(ch[n++]); // 同时让n指向下一个

    // st.push(root);//不要直接push进去

    while (n < len)
    {

        if (ch[n] == '(') // 如果有(,保存本地,进入下一层(先左边)
        {
            st.push(p);
            p = p->l;
        }
        else if (ch[n] == ',') // 如果有逗号,显然是左边的结束了,进行上一层的右边
        {
            st.top()->l = p; // 保存左边的信息
            p = st.top();
            // st.pop(); //不要pop,因为上一层还有用
            p = p->r;
        }
        else if (ch[n] == ')') // 如果是) ,说明这一层结束了
        {
            st.top()->r = p; // 保存后边
            p = st.top();    // 返回上一层
            st.pop();        // pop这一层
        }
        else
        {
            p = new node(ch[n]); // 是字母,建立新节点
        }

        ++n;
    }

    root = p; // 修改,因为是引用型参数
}

然后可以用前序遍历和中序遍历验证

void preshow(node *&root)
{
    if (root == NULL)
        return;
    else
    {
        cout << root->ch;
        preshow(root->l);
        preshow(root->r);
    }
}

void midshow(node *&root)
{
    if (root == NULL)
        return;
    else
    {
        midshow(root->l);
        cout << root->ch;
        midshow(root->r);
    }
}

比如:输入是 A(B(,C),D(E,F))

就可以得到输出是:

ABCDEF

BCAEDF

实际上,如果输入的是数字反而更具有普遍性,毕竟英文字母就那么几个(雾)。数字的处理办法也就是再写一个字符串转化成数字的函数罢了,略略略。

顺便再来一个二叉树转化成广义表:


void convert(node*&root,string &str)
{
    str.push_back(root->ch);

    if(root->l&&root->r)
    {
        str.push_back('(');
        convert(root->l,str);
        str.push_back(',');
        convert(root->r,str);
        str.push_back(')');
    }
    else if(root->l)
    {
        str.push_back('(');
        convert(root->l,str);
        str.push_back(',');
        str.push_back(')');
    }
    else if(root->r)
    {
        str.push_back('(');
        str.push_back(',');
        convert(root->r,str);
        str.push_back(')');
    }
    else
        return;

    return ;
}

最后是全部的代码:

#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <stack>

#define ll long long

using namespace std;

struct node
{
    char ch;
    node *l, *r;

    node(char CH)
    {
        l = r = NULL;
        ch = CH;
    }
};

const int maxn = 1e5 + 10;

char ch[maxn];
int len;

void convert(node*&root,string &str)
{
    str.push_back(root->ch);

    if(root->l&&root->r)
    {
        str.push_back('(');
        convert(root->l,str);
        str.push_back(',');
        convert(root->r,str);
        str.push_back(')');
    }
    else if(root->l)
    {
        str.push_back('(');
        convert(root->l,str);
        str.push_back(',');
        str.push_back(')');
    }
    else if(root->r)
    {
        str.push_back('(');
        str.push_back(',');
        convert(root->r,str);
        str.push_back(')');
    }
    else
        return;

    return ;
}

// 非递归写法,确实反而比递归写法更加简单而且容易理解
void another_answer(node *&root)
{
    if (len == 0) // 特判
    {
        root = NULL;
        return;
    }

    int n = 0; // n用来迭代字符串
    stack<node *> st;

    node *p = root;        // p指针用来迭代
    p = new node(ch[n++]); // 同时让n指向下一个

    // st.push(root);//不要直接push进去

    while (n < len)
    {

        if (ch[n] == '(') // 如果有(,保存本地,进入下一层(先左边)
        {
            st.push(p);
            p = p->l;
        }
        else if (ch[n] == ',') // 如果有逗号,显然是左边的结束了,进行上一层的右边
        {
            st.top()->l = p; // 保存左边的信息
            p = st.top();
            // st.pop(); //不要pop,因为上一层还有用
            p = p->r;
        }
        else if (ch[n] == ')') // 如果是) ,说明这一层结束了
        {
            st.top()->r = p; // 保存后边
            p = st.top();    // 返回上一层
            st.pop();        // pop这一层
        }
        else
        {
            p = new node(ch[n]); // 是字母,建立新节点
        }

        ++n;
    }

    root = p; // 修改,因为是引用型参数
}

void dfs(node *&root, int &p)
{
    // 如果遇见逗号有两种情况,一个是左边是( 另一个是左边是字母
    // 如果是括号,说明后边有字母,不应该再加,把这个逗号留给上一层处理,不然会漏掉字母

    if (ch[p] == ',')
        return;
    else if (ch[p] == ')' || p >= len)
    {
        ++p;
        return;
    }
    else
    {
        root = new node(ch[p++]);

        if (ch[p] == '(')
        {
            dfs(root->l, ++p);

            if (ch[p] == ',')
                ++p;
            // 如果直接++p或者直接p
            //++p 可能会导致 (,C) 在返回时p指向C,导致漏过C
            // p 可能会导致 D(B,C) 在返回时p指向逗号,而后面还有一个字母,导致直接返回,漏掉字母

            dfs(root->r, p);
        }
        else // 如果字母后面是,或者) 直接返回上一层,统一处理
        {
            ++p;
            return;
        }
    }
}

void preshow(node *&root)
{
    if (root == NULL)
        return;
    else
    {
        cout << root->ch;
        preshow(root->l);
        preshow(root->r);
    }
}

void midshow(node *&root)
{
    if (root == NULL)
        return;
    else
    {
        midshow(root->l);
        cout << root->ch;
        midshow(root->r);
    }
}

int main()
{
    cin >> ch;
    len = strlen(ch);

    node *root;
    int p = 0;

    another_answer(root);
    //  dfs(root,p);

    preshow(root);
    cout << endl;

    midshow(root);
    cout<<endl;

    string ans;

    convert(root,ans);
    cout<<ans;
    cout << endl;
    system("pause");
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DogDu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值