任意给出一个序列,判断他属不属于二叉树的前序序列,或者还是中序序列,文章采用的是非重构树的方法。
如图:遇到空节点用#表示
实现思路:根据前序遍历的定义:先访问当前节点,再依次访问其左右子树,化成序列为:"9,3,4,#,#,1,#,#,2,#,6,#,#"
中序遍历:先访问根节点的左子树,再访问当前根节点,最后访问根节点的右子树。化成序列为:“#,4,#,3,#,1,#,9,#,2,#,6,#”
那么从定义入手,判断q前序序列,先访问自身,那么首个字符一定是根节点,我们把空节点‘’#’也当成一个没有子节点的根节点,那么他和非空节点的区别就是访问完空节点后,只需占一个位置,后续不用在创建位置,而当访问完非空节点后,既需要删除自身的一个位置,又需要为后续访问它的子节点创建两个位置进行访问,如此一来,把位置数依次放入栈中,占据一个位置后,栈内的头部数据就减一,创建一个位置就push(1),若栈内有0就弹出,按照这个逻辑就实现了前序遍历的原理,若序列保持相应的顺序,则最后栈内的“位置”被占完也就表明他是前序序列。
至于中序序列:只需要每次遍历完左子树后创建两个位置去遍历根节点和右子树,以此类推,就可判断序列是否为中序序列,注意:一开始需要创建一个位置去遍历左子树,最后一个右节点遍历结束后不需要在创建新位置。
#include<iostream>
#include<stack>
#include<string>
using namespace std;
class Solution {
public:
static bool isValidSerialization(string preorder) {//验证是否是前序序列
int l = preorder.size();
stack<int> stk;
stk.push(1);//初始化要先建立一个位置给根节点
int i = 0;
while (i < l) {
if (stk.empty()) {
return false;
}
if (preorder[i] == ',') {
i++;
}
else if (preorder[i] == '#') {//遇到空节点直接删除一个位置
stk.top() -= 1;
if (stk.top() == 0) {
stk.pop();
}
i++;
}
else {
while (i < l && preorder[i] != ',') {//防止出现多位数
i++;
}
stk.top() -= 1;
if (stk.top() == 0) {
stk.pop();
}
stk.push(2);//遇到非空根节点删除一个位置的同时要创建两个子节点
}
}
return stk.empty();//如果栈中所有位置都被使用完证明序列符合前序序列
}
static bool isValid(string inorder) //验证是否是中序序列
{
int l = inorder.size();
stack<int> stk;
int i = 0;
stk.push(1);
while (i < l)
{
if (stk.empty()) {
return false;
}
if (inorder[i] == ',') {
i++;
}
else if (inorder[i] == '#') {//遇到空节点直接删除一个位置,同时创建两个位置分别留给根节点和右子节点
stk.top() -= 1;
if (stk.top() == 0) {
stk.pop();
}
if (i != l - 1) {
stk.push(2);
}
i++;
}
else
{
while (i < l &&inorder[i] != ',') {//防止出现多位数
i++;
}
stk.top() -= 1;
if (stk.top() == 0) {
stk.pop();
}
}
}
return stk.empty();//如果栈中所有位置都被使用完证明序列符合中序序列
}
};
int main() {
string s1 = "9,3,4,#,#,1,#,#,2,#,6,#,#";
cout << Solution::isValidSerialization(s1) << endl;
string s2 = "9,#,#,1";
cout << Solution::isValidSerialization(s2) << endl;
string s3 = "#,13,#,16,#,13,#,16,#,13,#,16,#,13,#";
cout << Solution::isValid(s3) << endl;
注意:此方法暂时只应用于前序和中序判断,后序和层序遍历数据结构规律分情况所以此方法实现较为难,可以采用重构树的方法去实现。