二叉树的先序遍历,中序遍历,后序遍历,层次遍历(递归和非递归)
先序遍历
二叉树的先序遍历是进行先根遍历,接下来是左子树,然后是右子树这样的顺序。
void preorder(BiTree T){
if(T!=NULL){
visit(T);
preorder(T->left);
preorder(T->right);
}
}
非递归算法如下
struct Command{
string s;
TreeNode* node;
Command(string s,TreeNode* node):s(s),node(node){}
};
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
if(root == NULL) return res;
stack<Command> stack;
stack.push(Command("go",root));
while(!stack.empty()){
Command command = stack.top();
stack.pop();
if(command.s == "print")
res.push_back(command.node->val);
else{
//assert(command == "go");
if(command.node->right)
stack.push(Command("go",command.node->right));
if(command.node->left)
stack.push(Command("go",command.node->left));
stack.push(Command("print",command.node));
}
}
return res;
}
};
中序遍历
二叉树的中序遍历是先遍历左子树,再访问根节点,接下来遍历右子树。
void inorder(BiTree T){
if(T!=NULL){
inorder(T->left);
visit(T);
inorder(T->right);
}
}
借助栈,将二叉树的递归算法转换成非递归算法。先访问根节点的所有左节点并且将它们一一进栈。然后出栈一个结点p,此时的p没有左孩子节点或者左孩子结点均已访问过,访问p,然后扫描该结点的右孩子,将其进栈,再扫描该右孩子结点的所有左节点并一一进栈。直到栈空为止。
void inorder2(BiTree T){
InitStack(S);
BiTree p=T;
while(p||!IsEmpty(S)){
if(p){
push(S,p);
p=p->left;
}
else{
pop(S,p);visit(p);
p=p->right;
}
}
}
这是教科书上传统的中序遍历的非递归算法,下面给出一种通用的模拟栈的入栈出栈过程的把递归算法改成非递归算法的过程。
struct Command{
string s;
TreeNode* node;
Command(string s,TreeNode* node):s(s),node(node){}
};
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
if(root == NULL) return res;
stack<Command> stack;
stack.push(Command("go",root));
while(!stack.empty()){
Command command = stack.top();
stack.pop();
if(command.s == "print")
res.push_back(command.node->val);
else{
//assert(command == "go");
if(command.node->right)
stack.push(Command("go",command.node->right));
stack.push(Command("print",command.node));
if(command.node->left)
stack.push(Command("go",command.node->left));
}
}
return res;
}
};
后序遍历
二叉树的后序遍历是先访问左子树,再访问右子树,接下来访问根节点。
void postorder(BiTree T){
if(T!=NULL){
postorder(T->left);
postorder(T->right);
visit(T);
}
}
后序遍历的非递归遍历二叉树的顺序是先访问左子树,再访问右子树,最后访问根节点。当用堆栈来存储结点,必须分清返回根节点时,是从左子树返回的,还是从右子树返回的。需要使用辅助指针r,指向最近访问过的结点。或者再结点中增加一个标志域,记录是否已被访问。
void postorder2(BiTree T){
InitStack(S);
p=T;
r=NULL;
while(p||!IsEmpty(S)){
if(p){ //走到最左边
push(S,p);
p=p->left;
}
else{
GetTop(S,p);
if(p->right&&r->left!=r){
p=p->right;
push(S,p);
p-p->left;
}
else{
pop(S,p);
visit(p->data);
r=p;
p=NULL;
}
}
}
}
struct Command{
string s;
TreeNode* node;
Command(string s,TreeNode* node):s(s),node(node){}
};
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
if(root == NULL) return res;
stack<Command> stack;
stack.push(Command("go",root));
while(!stack.empty()){
Command command = stack.top();
stack.pop();
if(command.s == "print")
res.push_back(command.node->val);
else{
//assert(command == "go");
stack.push(Command("print",command.node));
if(command.node->right)
stack.push(Command("go",command.node->right));
if(command.node->left)
stack.push(Command("go",command.node->left));
}
}
return res;
}
};
层次遍历
层次遍历需要借助一个队列,先将二叉树根节点入队,然后出队,访问该结点,如果它有左子树,则将左子树根节点入队,如果它有右子树,则将右子树根节点入队。然后出队,对出队结点进行访问,直到队列为空。
void levelorder(BiTree T){
InitQueue(Q);
BiTree p;
EnQueue(Q,T);
while(!IsEmpty(Q)){
DeQueue(Q,p);
visit(p);
if(p->left!=NULL)
EnQueue(Q,p->left);
if(p->right!=NULL)
EnQueue(Q,p->right);
}
}