二叉树的前/中/后序非递归遍历和快排的非递归版本

二叉树

无论是哪种顺序的遍历,都属于深度优先的遍历,因此都需要用到stack,同时还需要一个游标,类似for循环的游标,只是for循环是从前往后的顺序,这里的游标是深度优先的顺序,stack则控制push和pop的顺序,以达到前中后序遍历的效果

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
 //root为根节点,将遍历的结果按顺序放入List中并返回
class Solution {
	//前序1
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> s = new Stack();
        TreeNode p = root;
        while(!s.empty()||p!=null){
        	//先访问父节点,同时将右子树入栈,一直往左下深入
            while(p!=null){
                result.add(p.val);
                s.push(p.right);
                p=p.left;
            }
            //父节点和左子树都访问过了,这时候就轮到栈顶的右子树了,也就是父->左->右的访问顺序
            //对于右子树依然是从子树的根节点开始,一直往左下深入
            //这里栈必定不为空,结合上面两个while条件可以很容易推断出来
            p=s.pop();
        }
        return result;
    }
	//前序2
	    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> s = new Stack();
        TreeNode p = null;
        s.push(root);
        while(!s.empty()){
            p=s.pop();
            if(p==null)
                continue;
            result.add(p.val);
            s.push(p.right);
            s.push(p.left);
        }
        return result;
    }
	//中序
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> s = new Stack();
        TreeNode p = root;
        while(!s.empty()||p!=null){
        	//仍然是一直向坐下深入,但这次push的不是p.right,而是p本身
        	//因为中序的顺序为左->父->右,所以需要先将左子树按序入栈,最终栈顶节点即树最左下的节点,也就是中序遍历的第一个节点
            while(p!=null){
                s.push(p);
                p=p.left;
            }
            p=s.pop();
            //到这里p没有左子树,但可能有右子树,按照左->父->右的顺序,左子树为空,因此访问父节点
            //然后对右子树进行同样的迭代
            result.add(p.val);
            p=p.right;
        }
        return result;
    }
	//后序
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> s = new Stack();
        TreeNode p = root,lastVisited=null;
        while(!s.empty()||p!=null){
        	//和中序一样
            while(p!=null){
                s.push(p);
                p=p.left;
            }
            //后序的顺序是先右节点再父节点,所以这里的关键就是:
            //如果栈顶节点没有右子树,那么直接访问栈顶节点,也就是父节点
            //或者栈顶节点有右子树,但这个右子树已经被访问过了,也是直接访问父节点
            //那么如何判断右子树有没有被访问过呢?
            //由于当前栈顶节点必定是上次访问节点(lastVisited中保存)的父节点(这点手动模拟push和pop的过程就知道了)
            //因此只需要判断上次访问的节点是不是栈顶节点的右孩子就可以了
            TreeNode cur =s.peek();
            if(cur.right==null || cur.right==lastVisited){
				lastVisited=s.pop();
                result.add(lastVisited.val);
            }else{
            	//走到这里说明右子树还没有访问过,则对右子树进行同样的迭代
                p=cur.right;
            }
        }
        return result;
    }
}

快排

和二叉树的前序非递归遍历基本是一模一样,只需要理解栈的执行过程就好了,这个执行过程画出来就是一颗二叉树的前序遍历

		public static void qSort(int[] a,int st,int ed){
		//每次划分的区间,对应二叉树的节点
		class Node{
			int s;
			int e;
			public Node(int s, int e) {
				this.s = s;
				this.e = e;
			}
		}
		Stack<Node> stack = new Stack<>();
		int p;
		//st < ed 对应前面的父节点判空
		while (st < ed || !stack.empty()) {
			while (st < ed) {
				//对应访问父节点
				p = partion(a,st, ed);
				//一样的将右节点压栈
				stack.push(new Node(p + 1, ed));
				//对应将迭代对象换为左节点
				ed = p - 1;
			}
			//一样的出栈
			Node node = stack.pop();
			st = node.s;
			ed = node.e;
		}
	}

	public int partion(int[] a, int st, int ed){
		int s=st,e=ed;
		int temp;
		while(s<e){
			while(s<e&&a[s]<=a[e])
				e--;
			if(s<e){
				temp=a[s];
				a[s]=a[e];
				a[e]=temp;
				s++;
			}
			while(s<e&&a[s]<=a[e])
				s++;
			if(s<e){
				temp=a[s];
				a[s]=a[e];
				a[e]=temp;
				e--;
			}
		}
		return s;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值