[基本算法] 二叉树遍历方法
二叉树的遍历包括前序、中序、后序、层次遍历。实现方式一般为递归和循环两种。
接下来给出遍历算法代码,均使用C++编写。
二叉树结点
使用leetcode常见的TreeNode
1 struct TreeNode { 2 int val; 3 TreeNode *left; 4 TreeNode *right; 5 TreeNode() : val(0), left(nullptr), right(nullptr) {} 6 TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 7 TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 8 };
先序遍历
先访问根节点,然后访问左子树,最后访问右子树。
递归版本
1 void preOrder(TreeNode *root){ 2 if(root!=nullptr){ 3 cout<<root->val<<endl; 4 preOrder(root->left); 5 preOrder(root->right); 6 } 7 }
非递归版本一,将左右子节点压入栈中,下一次弹出栈顶进行遍历:
1 void preOrderLoop(TreeNode *root){ 2 stack<TreeNode*> stk; 3 stk.push(root); 4 5 TreeNode *p; 6 while(!stk.empty()){ 7 p=stk.top(); 8 stk.pop(); 9 10 if(p==nullptr) 11 continue; 12 13 cout<<p->val<<endl; 14 stk.push(p->right); 15 stk.push(p->left); 16 } 17 }
非递归版本二,遍历左节点之前将子节点压入栈中,当碰到nullptr时弹出栈顶,从其右子节点开始遍历:
(栈中保存的是可能还没访问右子节点的节点)
1 void preOrderLoop2(TreeNode *root){ 2 stack<TreeNode*> stk; 3 TreeNode *p=root; 4 5 while(p!=nullptr || !stk.empty()){ 6 while(p!=nullptr){ 7 cout<<p->val<<endl; 8 // 将根节点压入栈中,便于之后遍历右子树 9 stk.push(p); 10 p=p->left; 11 } 12 // 若p为null,则从栈中取出节点访问其右节点 13 if(!stk.empty()){ 14 p=stk.top(); 15 stk.pop(); 16 p=p->right; 17 } 18 } 19 }
中序遍历
先访问左子树,然后访问根节点,最后访问右子树
递归版本
1 void inOrder(TreeNode *root){ 2 if(root!=nullptr){ 3 inOrder(root->left); 4 cout<<root->val<<endl; 5 inOrder(root->right); 6 } 7 }
非递归版本,遍历节点时不先访问值,而是将节点存入栈中,然后继续访问左节点。
当遍历的节点为空时,从栈中弹出节点,访问值并继续访问右节点。
1 void inOrderLoop(TreeNode *root){ 2 stack<TreeNode*> stk; 3 TreeNode *p=root; 4 while(p!=nullptr || !stk.empty()){ 5 while(p!=nullptr){ 6 stk.push(p); 7 p=p->left; 8 } 9 // 当p==nullptr,则左子树已完全访问,应访问根节点和右子树 10 if(!stk.empty()){ 11 p=stk.top(); 12 stk.pop(); 13 cout<<p->val<<endl; 14 p=p->right; 15 } 16 } 17 }
上面的代码有循环嵌套,可以进行简化:
1 void inOrderLoop2(TreeNode *root){ 2 stack<TreeNode*> stk; 3 TreeNode *p=root; 4 while(p!=nullptr || !stk.empty()){ 5 if(p!=nullptr){ 6 stk.push(p); 7 p=p->left; 8 } 9 else{ 10 p=stk.top(); 11 stk.pop(); 12 cout<<p->val<<endl; 13 p=p->right; 14 } 15 } 16 }
后序遍历
先访问左子树,然后访问右子树,最后访问根节点
递归版本
1 void postOrder(TreeNode *root){ 2 if(root!=nullptr){ 3 postOrder(root->left); 4 postOrder(root->right); 5 cout<<root->val<<endl; 6 } 7 }
非递归版本,使用lastVisit保存上次访问的右节点,若从栈中弹出的节点右节点为空或为上次访问的节点,则访值:
1 void postOrderLoop(TreeNode *root){ 2 stack<TreeNode*> stk; 3 TreeNode *p=root; 4 TreeNode *lastVisit=p; 5 6 while(p!=nullptr || !stk.empty()){ 7 while(p!=nullptr){ 8 stk.push(p); 9 p=p->left; 10 } 11 //左子树访问完毕,出栈访问右子树 12 p=stk.top(); 13 //检查是否右子树为空或者已被访问 14 if(p->right==nullptr || p->right==lastVisit){ 15 cout<<p->val<<endl; 16 stk.pop(); 17 lastVisit=p; 18 p=nullptr; 19 } 20 else{ 21 p=p->right; 22 } 23 } 24 }
层次遍历
按照二叉树的层次进行遍历,一般是非递归版本:
1 void levelOrder(TreeNode *root){ 2 vector<TreeNode*> vec; 3 TreeNode *p; 4 vec.push_back(root); 5 6 while(vec.size()>0){ 7 vector<TreeNode*> temp; 8 for(TreeNode* v:vec){ 9 cout<<v->val<<endl; 10 if(v->left!=nullptr) temp.push_back(v->left); 11 if(v->right!=nullptr) temp.push_back(v->right); 12 } 13 swap(vec,temp); 14 } 15 }
浙公网安备 33010602011771号