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