创建
struct BinaryTree
{
T key;
BinaryTree* lchild;
BinaryTree* rchild;
bool IsSecondPop=false; //主要是方便后序遍历时是否第二次进栈
};
BinaryTree* CreateBinaryTree() //先序建树 若碰到‘#’就是表示为空,如果不是空,就先建左子树,后建立右子树
{
T in;
cin >> in;
BinaryTree *p = new BinaryTree();
if (in=='#')
{
p = NULL;
return p;
}
else
{
p->key = in;
p->lchild = CreateBinaryTree();
p->rchild = CreateBinaryTree();
}
return p;
}
前序遍历:
借助栈。
判断当前节点是否为空,否,把当前节点压入栈,访问该节点,并左路下降pnode指向pnode->left
若为空,指向右子树(pnode->right),弹出栈顶元素。
void preOrdernorecursion(BinaryTree* pnode)
{
if (pnode == nullptr)
return;
stack <BinaryTree*> A;
while (pnode || !A.empty())
{
if (pnode)
{
cout << pnode->key << endl;
A.push(pnode);
pnode = pnode->lchild;
}
else
{
pnode = A.top()->rchild;
A.pop();
}
}
}
中序遍历:
借助栈。 判断是否为空,否,把当前节点压入栈,并左路下降pnode指向pnode->left 。
若为空,取栈顶元素,访问该元素节点,并指向右子树(pnode->right),弹出栈顶元素。
void InOrdernorecursion(BinaryTree* pnode)
{
if (pnode == nullptr)
return;
stack <BinaryTree*> A;
while (pnode || !A.empty())
{
if (pnode)
{
A.push(pnode);
pnode = pnode->lchild;
}
else
{
cout << A.top()->key << endl;
pnode = A.top()->rchild;
A.pop();
}
}
}
- 前序和中序都是左子树非空左路下降同时压栈,若为空转向右子树,同时弹栈。
- 区别就是何时访问节点元素,前序是在压入的时候同时访问,而中序是在弹出的时候访问。
后序遍历:
一个节点会入两次栈,左路下降的时候第一次入栈,直到为空,弹出当前节点,这是第一次出栈,然后直接再压进去栈,之后判断其右子树,然后继续入栈。如果右子树也为空了,此时第二次弹出,弹出的同时访该节点。
void PostOrdernorecursion(BinaryTree* pnode)
{
if (pnode == nullptr)
return;
stack <BinaryTree*> mystack;
BinaryTree *flagpNode;
while (pnode || !mystack.empty())
{
while (pnode)
{
flagpNode= pnode;
//flagpNode.IsSecondPop = false; //第一次进栈
mystack.push(flagpNode);
pnode = pnode->lchild;
}
flagpNode = mystack.top(); //左边没有左子树了,就要出栈了
mystack.pop(); //第一次出栈
if (!flagpNode->IsSecondPop) //如果是第一次出栈,就把它再压进去,因为要判断右边还有没有子树
{
flagpNode->IsSecondPop = true; //把进栈bool设为true
mystack.push(flagpNode);
pnode = flagpNode->rchild; //转到右子树
}
else //如果是第二次出栈,就访问它
{
cout << flagpNode->key << endl;
}
}
}
如果要求不能改变二叉树的结构,则需要自己从新构造一个具有判断是否第二次进栈bool变量的新结构体。如下:
void PostOrdernorecursion(BinaryTree* pnode)
{
if (pnode == nullptr)
return;
struct FlagBT //自己设立的结构体
{
BinaryTree* p;
bool issecondpop;
};
stack <FlagBT> mystack;
FlagBT flagpNode;
while (pnode||!mystack.empty())
{
while (pnode)
{
flagpNode.p = pnode;
flagpNode.issecondpop = false; //第一次进栈
mystack.push(flagpNode);
pnode = pnode->lchild;
}
flagpNode = mystack.top(); //左边没有左子树了,就要出栈了
mystack.pop(); //第一次出栈
if (!flagpNode.issecondpop) //如果是第一次出栈,就把它再压进去,因为要判断右边还有没有子树
{
flagpNode.issecondpop = true; //把进栈bool设为true
mystack.push(flagpNode);
pnode = flagpNode.p->rchild;
}
else //如果是第二次出栈,就访问它
{
cout << flagpNode.p->key << endl;
}
}
}
层次遍历:
借助队列:
void BreadthOrder(BinaryTree* pnode)
{
if (pnode == nullptr)
return;
queue <BinaryTree*> A;
A.push(pnode);
while (pnode)
{
cout << pnode->key << endl;
if (pnode->lchild)A.push(pnode->lchild);
if (pnode->rchild)A.push(pnode->rchild);
A.pop();
if (!A.empty()) pnode = A.front();
else pnode = nullptr;
}
}
但是很多有分层输出的要求,需要记录每一层的终点,待补充……
还是需要一个队列来记录,但是在每一层一开始会根据当前队列的大小来标记为last,当小于这个last时,把这层的节点打印出来,然后这个节点弹出队列,同时把他的左右孩子压入队列中。这样没打印出新的一层,这一层的节点都弹出了队列,同时下一层(这一层的左右孩子)又压入队列,周而复始,直到队列为空。
vector<vector<BinaryTree*>> BreadthOrder_Layer(BinaryTree* pnode)
{
vector<vector<BinaryTree*>> res;
if (pnode == nullptr)
return res;
queue <BinaryTree*> q;
q.push(pnode); //先把根节点压入队列
while (!q.empty())
{
vector <BinaryTree*> layer; //这一层的节点数组
int cur = 0; //遍历标记
int last = q.size(); //结束遍历标记
while (cur++ < last)
{
BinaryTree* s = q.front(); //队列先入先出
layer.push_back(s); //把这个节点压入当前层的数组
//cout << s->key << ' ';
q.pop(); //压入layer后在队列中弹出
if (s->lchild)q.push(s->lchild); //左右孩子压入
if (s->rchild)q.push(s->rchild);
}
res.push_back(layer); //当前层压入结果数组
//cout << endl;
}
return res;
}