ybt 1364:二叉树遍历(flist)
层次遍历序列第一个元素,一定是整棵树的根结点。在中序遍历序列中找到该根结点元素,其左边就是左子树的中序遍历序列,右边就是右子树的中序遍历序列。
接下来我们需要构造左右子树的层次遍历序列。
易知左子树、右子树的层次遍历序列是原树层次遍历序列的子序列。
(子序列:在一个序列中,顺序取出部分元素(可以不连续),按原顺序排列得到的序列)
该字符串中只有小写英文字母,而且没有重复字符。我们可以使用散列思想,根据左右子树的中序遍历序列预处理出一个数组bool isLeft[128]
,isLeft[i]
表示ASCII码为i的字母是否属于左子树。如一个字符不属于左子树,那么就属于右子树。
遍历层次遍历序列
得到左右子树的层次遍历序列后,递归调用本函数,构造出左右子树。得到左右子树根结点的地址。
申请结点作为当前树的根结点,设根结点的左右孩子,返回根结点地址。
主函数中调用该建树函数,传入中序遍历序列和层次遍历序列,即可得到整棵树的根结点地址。而后进行先序遍历。
复杂度分析:
isLeft
数组的过程,复杂度 O ( l ) O(l) O(l)因此,生成一棵子树的递归调用的时间复杂度为 O ( l ) O(l) O(l)。
树中每层各结点中序遍历序列合在一起就是完整的中序遍历序列。
因此树中每层各结点的 l l l的加和为 n n n,树的层数为 O ( l o g n ) ∼ O ( n ) O(logn)\sim O(n) O(logn)∼O(n),因此时间复杂度为 O ( n l o g n ) ∼ O ( n 2 ) O(nlogn)\sim O(n^2) O(nlogn)∼O(n2)
观察层次遍历序列和中序遍历序列的关系。
层次遍历序列第一个元素,一定是整棵树的根结点。在中序遍历序列中找到该根结点元素,其左边就是左子树的中序遍历序列,右边就是右子树的中序遍历序列。
层次遍历序列的下一个元素就是左子树的根结点,可以将左子树的中序遍历序列拆为两部分。再下一个元素就是右子树的根结点,可以将右子树的中序遍历序列再拆为两部分。继续做同样的事情。
为了使每次在层次遍历序列中取到的元素对应中序遍历序列的根结点,那么每次得到的中序遍历序列也得像做层次遍历一样,不断入队。
具体做法为:
当队列为空,遍历完整个层次遍历序列后,树就建好了。对该树做先序遍历。
复杂度分析:
设中序、层次遍历序列长度为n,也就是树的总结点数为n。
#include
using namespace std;
#define N 30
struct Node
{
char val;
int left, right;
};
Node node[N];
int p;
int createTree(string sm, string sl)//以中序遍历序列sm, 层次遍历序列sl构建二叉树
{
if(sm == "" || sl == "")
return 0;
int j;
for(j = 0; j < sm.length(); ++j)
if(sm[j] == sl[0])
break;
string sm_left = sm.substr(0, j), sm_right = sm.substr(j+1), sl_left, sl_right;
bool isLeft[128] = {};//isLeft[i]:ASCII码为i的字符是不是属于左子树
for(int i = 0; i < sm_left.length(); ++i)
isLeft[sm_left[i]] = true;
for(int i = 1; i < sl.length(); ++i)//略去根结点sl[0],从sl[1]开始遍历
{
if(isLeft[sl[i]])
sl_left.push_back(sl[i]);
else
sl_right.push_back(sl[i]);
}
int np = ++p;
node[np].val = sl[0];
node[np].left = createTree(sm_left, sl_left);
node[np].right = createTree(sm_right, sl_right);
return np;
}
void preOrder(int r)
{
if(r == 0)
return;
cout << node[r].val;
preOrder(node[r].left);
preOrder(node[r].right);
}
int main()
{
string s_mid, s_lev;//s_mid:中序遍历序列 s_lev:层次遍历序列
cin >> s_mid >> s_lev;
int root = createTree(s_mid, s_lev);
preOrder(root);
return 0;
}
#include
using namespace std;
#define N 105
struct QNode
{
string s;//中序遍历序列
int p;//该中序遍历序列对应的子树的根结点地址
QNode(){}
QNode(string a, int b):s(a), p(b){}
};
struct Node
{
char val;
int left, right;
};
Node node[N];
string s_lev, s_mid;//s_lev:层次遍历序列 s_mid:中序遍历序列
int p, lev_i;//p:node数组中已分配的最后一个结点的下标 lev_i:s_lev的下标
void createTree()
{
queue<QNode> que;
que.push(QNode(s_mid, ++p));//根结点的地址为1
while(que.empty() == false)
{
QNode u = que.front();
que.pop();
int i;
for(i = 0; i < u.s.length(); ++i)//寻找中序遍历序列中根结点元素的下标
if(u.s[i] == s_lev[lev_i])
break;
node[u.p].val = s_lev[lev_i++];//lev_i++:下一次在层次遍历序列中看下一个字符
string sl = u.s.substr(0, i), sr = u.s.substr(i+1);//sl:左子树的中序遍历序列 sr:右子树的中序遍历序列
if(sl.length() > 0)//如果存在左子树
{
int lp = ++p;//给左子树根结点分配地址
node[u.p].left = lp;
que.push(QNode(sl, lp));//取u.s中ri左侧字符串作为左子树的中序遍历序列
}
if(sr.length() > 0)//如果存在右子树
{
int rp = ++p;//给右子树根结点分配地址
node[u.p].right = rp;//设根结点的左右子树
que.push(QNode(sr, rp));//取u.s中ri右侧字符串作为右子树的中序遍历序列
}
}
}
void preOrder(int r)
{
if(r == 0)
return;
cout << node[r].val;
preOrder(node[r].left);
preOrder(node[r].right);
}
int main()
{
cin >> s_mid >> s_lev;
createTree();
preOrder(1);
return 0;
}