前中后递归版:
//二叉树的遍历:前序:中左右 中序:左中右 后序:左右中
void recur(btnode *p){ //递归版
if(p != nullptr){
cout<<p->date<<endl; // 前序 count放这
recur(p->left);
//中序cout放在这
recur(p->right);
//后序cout放在这
}
}
非递归版:
1. 先中后都是用栈,层用队列
2. 先序用1个栈,先压右再压左,后序两个栈,先压左再压右
3. 层序看问题,一个一个出用1个循环,要求1层1层出用两个循环
a)先序 中左右
先弹栈(访问),再压右孩子(不为空),最后压左孩子(不为空)
压栈顺序是右左,弹栈自然就是左右了!
void preorder(node *head){
if(head ==nullptr)
return;
node *cur=head;
stack<node *> s1;
s1.push(cur);
while(s1.size()){
cur=s1.top();
s1.pop();
cout<<cur->val<<endl; // 先序 访问节点
if(cur->right) // 先压右
s1.push(cur->right);
if(cur->left) // 再压左
s1.push(cur->left);
}
}
b)中序 左右中
用一个栈去模仿递归的过程,
从根结点开始(cur=root) 不断的做一个while循环, 把cur压栈,然后去它的左结点 cur =cur->left
左子树遍历完之后(也就是cur为空) 弹出一个,给cur 然后去cur的右子树,再遍历它的左子树
那么中序,就是第二次遇到这个结点 也就是弹栈的时候输出。其实先序也能由这种方法搞定,但是对于n叉树来说,用这种方法实现的先序,很难写!!,所以还是记上面这种方法。
(这种方法的先序,就是第一次遇到这个结点,也就是压栈的时候输出)
void inorder(btnode *head){
btnode *cur=head;
stack<btnod *> s1;
while(cur || !s1.empty()){ //结束的条件是当前为空且栈空
while(cur){
//cout<<cur->val<<endl; //前序
s1.push(cur);
cur=cur->left;
}
if(!s1.empty()){
cur = s1.top();
s1.pop();
cout<<cur->data<<endl; //中序,访问节点
cur = cur->right;
}
}
//一直往左走,不能走的时候,回栈中取出一个元素,然后去它的右边 ,然后继续往左走
}
c) 后序 左右中
用两个栈,栈1用来实现“中右左“的先序,同时栈1弹出的节点都压入栈2,这样栈2的弹出顺序就是“左右中”。
(记这个,同样为了实现n叉树的后序)
void postorder(btnode *head){
btnode *cur = head;
if(cur == nullptr)
return;
stack<btnode *> s1,s2;
s1.push(cur);
while(!s1.empty()){
cur=s1.top();
s1.pop(); // s1 pop的同时压入s2
s2.push(cur);
if(cur.left !=nullptr)
s1.push(cur.left);
if(cur.right != nullptr){
s1.push(cur.right);
}
}
while(!s2.empty()){
cout<<s2.top()->data<<endl; //后序访问节点
s2.pop();
}
}
另一种实现(but,不推荐记这个,为了n叉树的后序好实现,orz)
后序是左右中,所以轮到中之前,假如中有右孩子,最近一个必定是中的右孩子
后序非递归,本质上是要第3次遇到这个结点的时候输出,哪三次呢?
第一次是进入这个结点
第二次是从它的左子树返回
第三次是从它的右子树返回
后序遍历最重要的就是识别这个返回的动作是谁做的?假如是左子树做的,我们直接去右子树,如果是右子树做的,我们弹栈,并且去更高层。而做到这一点的办法就是当左子树遍历完了,栈顶元素的右孩子是不是等于最近一次弹出并打印的结点。
所以我们每次访问节点的时候,都记录下来(用一个节点指向这个节点),那么下一次要访问节点的时候,就可以知道上一次访问的是它的左孩子节点还是右孩子节点。(访问代表弹出并打印)具体的办法:
每次左子树遍历完,要访问栈顶元素的时候,把当前设为栈顶元素,然后做个判断
如果当前节点有右孩子且上次访问lastvisited 不等于这个右孩,那么不弹 转向右孩
如果不满足上面的条件,那么弹,且把最近访问的设为当前,当前设为nullptr(这点很重要,为了返回更高层)
d)层序
层序,很简单,用一个队列模拟即可。有两种层序:
1. 先头节点入队 然后进入循环 出队一个 左右分别入队。
2. 两个循环,每次小循环结束之后,之前push进来的节点全部pop干净。
第一种方法,用1个循环,每次把当前pop的节点的各个儿子加进来。
第二种方法,用2个循环,大循环的逻辑是把当前队列的所有节点都pop干净,内部循环的逻辑是加入每个节点的儿子。需要按层输出的层序,即 输出vector<vector<int>> 适合这种方法
1个循环:
void levelorder(btnode *head){
if(head ==nullptr)
return;
queue<btnode *> q;
btnode * cur;
q.push(head);
while(!q.empty()){
cur = q.front();
q.pop();
//队列弹出也不会返回元素 stack不会 orz!!!
cout<<cur->data<<endl;
if(cur->left)
q.push(cur->left);
if(cur->right)
q.push(cur->right);
}
}
2个循环:
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
if(root ==nullptr)
return {};
vector<vector<int>> res;
queue<TreeNode *> q1{{root}};
TreeNode *cur=nullptr;
while(q1.size()){
vector<int> onelevel;
for(int i=q1.size();i>0;--i){ //小循环要pop干净
cur=q1.front();
q1.pop();
onelevel.push_back(cur->val);
if(cur->left) q1.push(cur->left);
if(cur->right) q1.push(cur->right);
}
res.push_back(onelevel);
}
return res;
}
};