zoukankan      html  css  js  c++  java
  • 树的遍历非递归实现

    树的非递归遍历


    思路


    观察如图的过程

    我们可以发现其实遍历的过程都是一样的,不一样是处理各个节点的时机

    • 前序遍历是第一次访问到节点时处理节点
    • 中序遍历是第二次访问到节点时处理节点
    • 后序遍历是第三次访问到节点时处理节点

    那么事情就变的简单了我们只要用栈来保存访问的节点即可,然后根据第几次访问来判断是否处理


    代码


    先序遍历

    void preorder(tree t) {
      Node *p;
      stack<Node*> node_stack;
      if(t != NULL) {
        p = t;
        while(p || node_stack.size() != 0){
          if(p != NULL) {
    	printf("%d
    ",p->data);         //第一次访问
    	node_stack.push(p);
    	p = p->left;
          }else {//此时节点为空将要访问栈顶元素的右节点
    	p=node_stack.top();
    	p = p->right;
    	node_stack.pop();
          }
        }
      }
    }
    

    中序遍历

    void inorder(tree t) {
      Node *p;
      stack<Node*> node_stack;
      if(t != NULL) {
        p = t;
        while(p || node_stack.size() != 0) {
          if(p != NULL) {
    	node_stack.push(p);
    	p = p->left;
          } else {
    	p = node_stack.top();
    	node_stack.pop();
    	printf("%d
    ",p->data);    //p为空,此时栈顶元素为其父节点,进行第二次访问
    	p = p->right;
          }
        }
      }
    }
    

    后序遍历

    
    void postorder(tree t) {
      Node *p;
      stack<Node*> node_stack;
      map<Node*,int> times;
      if(t) {
        p = t;
        while(p || node_stack.size() != 0) {
          if(p != NULL) {
    	node_stack.push(p);
    	p = p -> left;
    	continue;
          }
          p = node_stack.top();
          if(times.find(p) == times.end()) {
    	times[p] = 1;
    	p = p -> right;
          } else {
    	node_stack.pop();
    	printf("%d
    ",p -> data);
    	p = NULL;    //必须设置不然栈顶又会被push一次    
          }
        }
      }
    }
    

    这一种思想就是上面的不过用一个hash表来判断是第几次访问
    注意 p 必须设置为NULL不然会将栈顶元素再次push进来一次


    void postorder(tree t) {
      stack<Node*> st;
      Node* pre=NULL;
      if(t) {
        st.push(t);
        while(!st.empty()) {
          t = st.top();
          if(((t->left == NULL) && (t->right == NULL))
    	 ||
    	 (pre != NULL)){
    	printf("%d
    ",t->data);
    	st.pop();
    	pre = t;
          }else {
    	if(t->right != NULL) st.push(t -> right);
    	if(t->left != NULL) st.push(t->left);
          }
        }
      }
    }
    

    这一个思路和上面不同,考虑一个点如果它的左右子树为空则可以处理了
    另一种可以处理该节点的情况就是两个子节点都访问过
    否则将右子树先push进来然后将左子树push进来访问的时候就是按照左右根的顺序了

    那么问题就是如何判断这两个子节点已经访问过了呢

    直接用 pre != NULL 即可,它表示子节点不是两个都空时也可以处理该点
    有些解法还加了 pre = t->left || pre = t->right,这是没有必要的,因为在压入的时候就已经保证该节点一定在该点子节点的下面
    如果都访问该节点了,那么子节点都必然访问过了


    相比于第一种解法,第二种应该还更优雅一点,而且它和层序遍历的队列实现法结构很像

  • 相关阅读:
    【转】我是一个线程
    前端之 JS 实现全选、反选、取消选中
    Python文件操作——逐行插入内容
    angularJs实现数据双向绑定的原理
    手机连接电脑调试页面
    工程化框架之feather
    网页上线后音频不能自动播放
    FormData对象
    地图热区自适应
    需求移交会
  • 原文地址:https://www.cnblogs.com/tclan126/p/8695570.html
Copyright © 2011-2022 走看看