二叉树的遍历方式

我们都知道二叉树的遍历方式有先序遍历,中序遍历,后序遍历。

先序遍历

他们最基本的实现方式是递归实现,那如果我们想要用非递归方式来实现呢?

首先,先序遍历的遍历方式是 根-左-右

递归方式代码实现:
 

public List<Integer> inorderTraversal(TreeNode root){
  List<Integer> list=new LinkedList<>();
  preSort(root,list);
  return list;
}
public void preSort(TreeNode root,List<Integer> list){
  if(root==null) return;
  list.add(root.val);//访问根节点
  preSort(root.left, list);//访问左节点
  preSort(root.right, list);//访问右节点
}

非递归方式实现:

思路:

1.将根节点进栈

2.弹出栈顶元素,并保存之

3.右孩子进栈,然后左孩子进栈

为什么先是右孩子进栈,然后左孩子进栈?

原因在于为了保证栈顶元素始终是先序序列顺序!这里充分利用了栈的后进先出的特性!

	public List<Integer> inorderTraversal(TreeNode root){
		List<Integer> list=new LinkedList<>();
		Stack<TreeNode> stack=new Stack<>();
		if(root==null) return list;
		stack.push(root);
		TreeNode cur;
		while(!stack.isEmpty()) {
			cur=stack.pop();//获取栈顶元素并将该元素出栈
			list.add(cur.val);//根节点
			if(cur.right!=null) stack.push(cur.right);//右孩子进栈
			if(cur.left!=null) stack.push(cur.left);//左孩子进栈
		}
		return list;
	}

中序遍历

递归实现,和先序遍历一样,十分的简单:

public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        inorderHelper(root, result);
        return result;
    }
    
private void inorderHelper(TreeNode root, List<Integer> result) {
        if(root == null) return;
        inorderHelper(root.left, result); // 递归遍历左子树
        result.add(root.val); // 访问根节点
        inorderHelper(root.right, result); // 递归遍历右子树
    }

非递归实现

思路:

1.查找最左边的结点,在这个过程中,每个根节点都要入栈

2.获取栈顶元素,并将值保存起来

3.访问右子树,重复123

	public List<Integer> inorderTraversal(TreeNode root){
		List<Integer> list=new ArrayList<>();
		Stack<TreeNode> stack=new Stack<>();
		if(root==null) return list;
		TreeNode cur=root;
		while(cur!=null || !stack.isEmpty()) {
			//访问左子树,直到到达最左结点
			while(cur!=null) {
				stack.push(cur);
				cur=cur.left;
			}
			//获取最左结点,并从栈中弹出来
			cur=stack.pop();
			//将值加入到集合中
			list.add(cur.val);
			//访问右子树
			cur=cur.right;
		}
		return list;
	}

后序遍历

递归实现,同上:

public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        inorderHelper(root, result);
        return result;
    }
    
private void inorderHelper(TreeNode root, List<Integer> result) {
        if(root == null) return;
        inorderHelper(root.left, result); // 递归遍历左子树
        inorderHelper(root.right, result); // 递归遍历右子树
        result.add(root.val); // 访问根节点
    }

非递归实现:
思路1:

1.访问最左边的结点,并将经过的每个根节点入栈

2.获取栈顶元素(最左边的元素),然后判断是否有右子树或右子树是否已经被遍历

原因:因为不同的结点对右子树的处理不一样,如结点A有右子树,A此时需要访问它。结点A的父亲结点B有右子树,但是当我们访问到结点B时,其右子树已经访问过了,这时候就不能再访问它了。所以我们每访问到一个结点,该结点不存在右子树或右子树已经被访问,就将标记结点更新

3.如果该结点存在右子树且尚未被访问,则访问右子树,重复123

public List<Integer> inorderTraversal(TreeNode root){
	List<Integer> list=new ArrayList<>();
	Stack<TreeNode> stack=new Stack<>();
	if(root==null) return list;
	TreeNode cur=root;
        //标记结点,用来记录该结点是否已经被访问
	TreeNode pre=null;
	while(cur!=null || stack.isEmpty()) {
		//找到最左边的结点
		while(cur!=null) {
			stack.push(cur);
			cur=cur.left;
		}
		//这里不能用pop(),因为后面我们需要判断这个结点是否存在右子树
		cur=stack.peek();
		//判断是否存在右子树或者右子树是否已经遍历过
		if(cur.right==null || cur.right==pre) {
			stack.pop();
			list.add(cur.val);
			pre=cur;
			cur=null;
		//如果存在右子树且右子树没有被遍历,则访问右子树	
		}else cur=cur.right;
	}
	return list;
}

思路2:借鉴先序遍历的思想

主要改变就是:先左孩子入栈,然后右孩子入栈

原因:使栈顶元素始终是按照根右左的顺序排序,最后只需将集合反转即可!

public ArrayList<Integer> postorderTraversal(TreeNode root) {
     ArrayList<Integer> list=new ArrayList<>();
     Stack<TreeNode> stack=new Stack<>();
     if(root==null) return list;
     TreeNode cur;
     stack.push(root);
     while(!stack.isEmpty()){
         cur=stack.pop();
         list.add(cur.val);
         if(cur.left!=null) stack.push(cur.left);
         if(cur.right!=null) stack.push(cur.right);
     }
     Collections.reverse(list);
     return list;
}

参考博客:https://segmentfault.com/a/1190000016674584?utm_source=tag-newest&tdsourcetag=s_pctim_aiomsg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值