当前位置:   article > 正文

详细讲解二叉树先序-中序-后序递归和非递归遍历以及层次遍历_先中后序遍历二叉树,递归和非递归算法思想

先中后序遍历二叉树,递归和非递归算法思想

二叉树有先序遍历(根左右)、中序遍历(左根右)和后序遍历(左右根)和层次遍历几种遍历方式。这几种遍历方式是其他二叉树解题的基础,所以必须先掌握。

递归遍历二叉树:

        因为二叉树本身就是用递归定义的,因此采用递归的方法实现三种遍历代码简洁且容易理解,但其开销比较大。

二叉树的先序、中序和后序遍历

先序遍历:任何子树的处理顺序都是:先根结点,再左子树,然后右子树 (根左右)

中序遍历:任何子树的处理顺序都是:先左子树,再根节点,然后右子树 (左根右)

后序遍历:任何子树的处理顺序都是:先左子树,再右子树,然后根结点 (左右根)

二叉树的节点定义如下:

  1. struct Node{
  2. int val;
  3. Node* left;
  4. Node* right;
  5. Node() {
  6. val = 0;
  7. left = nullptr;
  8. right = nullptr;
  9. }
  10. Node(int v){
  11. val = v;
  12. left = nullptr;
  13. right = nullptr;
  14. }
  15. };

先序遍历:

  1. void preOrderTraversalRecursion(Node* x){
  2. if(x == nullptr) return;
  3. cout<<x->val<<endl;
  4. preOrderTraversalRecursion(x->left);
  5. preOrderTraversalRecursion(x->right);
  6. }

中序遍历:

  1. void inOrderTraversalRecursion(Node* x){
  2. if(x == nullptr) return;
  3. inOrderTraversalRecursion(x->left);
  4. cout<<x->val<<endl;
  5. inOrderTraversalRecursion(x->right);
  6. }

后序遍历:

  1. void postOrderTraversalRecursion(Node* x){
  2. if(x == nullptr) return;
  3. postOrderTraversalRecursion(x->left);
  4. postOrderTraversalRecursion(x->right);
  5. cout<<x->val<<endl;
  6. }

总结一下:

        二叉树的递归遍历实际上是对二叉树的递归序(二叉树每个节点都会遍历三次),在第i次到达的时候打印根节点。(第1次到达就是先序遍历,第2次到达就是中序遍历,第3次到达就是后序遍历)

非递归遍历二叉树:

        非递归遍历二叉树就是用自己实现的栈替代系统栈。注:任何递归函数都可以改成非递归。

先序遍历:

        注意因为栈是先进后出的,所以是先压右子树再压左子树,这样就能保证出栈是左边先出右边后出。

  1. void preOrderTraversalIteration(Node* x){//先序遍历二叉树迭代法:
  2. if(x == nullptr ) return;
  3. cout<<"preOrderTraversalIteration"<<endl;
  4. stack<Node*> s;//用栈模拟递归
  5. s.push(x);//先把头节点压栈
  6. while (!s.empty()){ //栈非空就继续循环
  7. Node* cur = s.top(); //栈中弹出节点
  8. s.pop();
  9. cout<<cur->val<<endl;
  10. //cur有右节点就压入栈,有左节点就压入栈(因为栈是后进先出,所以按照根左右的遍历,应该先压右子树节点)
  11. if(cur->right){
  12. s.push(cur->right);
  13. }
  14. if(cur->left){
  15. s.push(cur->left);
  16. }
  17. }
  18. }

后序遍历:

        上面已经实现了非递归先序遍历(根左右),那么只要稍微加工就可以得到非递归后序遍历。将非递归先序遍历的压栈顺序改一下,改成先压左子树再压右子树,那么最终的遍历顺序是<根右左>,然后在每次弹出打印时候改成不打印,进入另外一个temp栈,当整个操作完成后。从temp栈中弹出元素,那么<根右左>就变成了<左右根>,这就是后序遍历。

  1. void postOrderTraversalIteration(Node* x){
  2. if(x == nullptr) return;
  3. cout<<"postOrderTraversalIteration"<<endl;
  4. stack<Node*> s,tmp;//s是类似于辅助先序遍历的栈;tmp是保存遍历元素,最后输出就是后序遍历
  5. s.push(x);
  6. while (!s.empty()){
  7. Node* cur = s.top();
  8. s.pop();
  9. tmp.push(cur);
  10. if(cur->left){
  11. s.push(cur->left);
  12. }
  13. if(cur->right){
  14. s.push(cur->right);
  15. }
  16. }
  17. while (!tmp.empty()){
  18. Node* t = tmp.top();
  19. cout<<t->val<<endl;
  20. tmp.pop();
  21. }
  22. }

中序遍历:

  1. void inOrderTraversalIteration(Node* x){//二叉树中序遍历迭代法实现
  2. if(x == nullptr) return;
  3. cout<<"inOrderTraversalIteration"<<endl;
  4. stack<Node* > s;
  5. Node* cur = x;
  6. Node* tmp = nullptr;
  7. //栈不为空或者cur不为空,那么循环继续
  8. while (!s.empty() || cur){
  9. if(cur){ //以cur为头节点的树,整条左边进栈(包括cur这个根节点),直到遇到cur的左边为nullptr结束
  10. s.push(cur);
  11. cur = cur->left;
  12. }else {
  13. cur = s.top(); //从栈中弹出cur,经过上面if判断cur的左边是nullptr,访问cur后,让cur=cur->right;
  14. s.pop();
  15. cout<<cur->val<<endl;
  16. cur = cur->right;
  17. }
  18. }
  19. }

层次遍历二叉树:

        层次遍历二叉树需要用队列实现,就是宽度优先遍历,每层入队然后出队让左右孩子分别入队。

  1. vector<vector<int> > levelOrder(Node* root) {
  2. // write code here
  3. vector<vector<int> > v;
  4. if(root == nullptr) return v;
  5. Node* cur = root;
  6. queue<Node*> q;
  7. q.push(cur);
  8. while(!q.empty()){
  9. int size = q.size(); //先获取当前层的节点数
  10. vector<int> ans;
  11. for(int i = 0;i<size;++i){ //将当前层的节点遍历,同时将它的孩子节点加入队列
  12. cur = q.front();
  13. q.pop();
  14. ans.push_back(cur->val);
  15. if(cur->left){
  16. q.push(cur->left);
  17. }
  18. if(cur->right){
  19. q.push(cur->right);
  20. }
  21. }
  22. v.push_back(ans);
  23. //二叉树层次遍历正序收集节点;如果要倒序收集,那么每次在首部位置插入即可
  24. }
  25. return v;
  26. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/625873
推荐阅读
相关标签
  

闽ICP备14008679号