编程之美-分层遍历二叉树

61 篇文章 10 订阅
7 篇文章 0 订阅

问题:给定一个二叉树,要求按分层遍历该二叉树,即从上到下按层次访问该二叉树(每一层将单独输出一行),每一层要求访问的顺序为从左到右,并将节点依次编号。那么分层遍历如图的二叉树,正确的输出应该为:

1
2  3
4  5  6
7  8


书中还给出了问题2:打印二叉树中的某层次的节点(从左到右),其中根结点为第0层,成功返回true,失败返回false

分析与解法

关于二叉树的问题,由于其本身固有的递归特性,通常可以用递归解决。为了解决给出的问题,我们可以发现。当解决问题2时,问题就迎刃而解了。现先分析问题2

假设要访问二叉树第K层的节点,那么其实可以把它转换为分别访问“已该二叉树根结点的左右子节点为根结点的两颗子树”中层次为k-1的节点。如题目中的二叉树,给定k=2,即要求访问原二叉树中第2层的节点(根结点为第0层),可以把它转换为分别访问以节点2、3为根结点的两颗子树中的k-1=1层的节点。

所以遍历第k层节点的代码如下(把最终结果存放在了list里):

public boolean printNodeAtLevel(TreeNode root, int level, List<Integer> list) {
    	if (root == null || level < 0) {
    		return false;
    	}
    	if (level == 0 && root != null) {
    		list.add(root.val);
    		return true;
    	}
    	boolean left = printNodeAtLevel(root.left, level-1, list);
    	boolean rigth = printNodeAtLevel(root.right, level-1, list);
    	return left || rigth;
    }


所以分层遍历的问题也就解决了,只需要从第0层,一直调用到二叉树的最后一层,就可以完全遍历了。但是二叉树的最大层数怎么求。也是递归的代码:

public int maxDepth(TreeNode root) {
    	if (root == null) {
    		return 0;
    	}
    	int left = 1+maxDepth(root.left); 
    	int rigth = 1+maxDepth(root.right);
    	return left > rigth ? left : rigth;
    }
好了,问题就解决了。好像发现这样的效率太低了。把求深度和遍历某层次的代码合并,得到如下代码:

public List<List<Integer>> levelOrder2(TreeNode root) {
    	List<List<Integer>> lists = new ArrayList<List<Integer>>();
    	if (root == null)
    		return lists;
    	for (int level = 0; ; level++) {
    		List<Integer> list = new ArrayList<>();
    		if (!printNodeAtLevel(root, level, list))
    			break;
    		lists.add(0, list);
    	}
    	return lists;
    }

但是发现,这种效率实在是太低了,因为每次访问都需要从跟节点访问,直到访问完成。下面的代码采用了游标的方式:cur代表当前访问的节点,last代表当前层次的最后一个节点。代码比较好懂,如下:

public List<List<Integer>> levelOrder3(TreeNode root) {
    	List<List<Integer>> lists = new ArrayList<List<Integer>>();
    	if (root == null)
    		return lists;
    	int cur = 0, last = 1;
    	List<TreeNode> queue = new ArrayList<>();
    	queue.add(root);
    	while (cur < queue.size()) {
    		last = queue.size();
    		List<Integer> list = new ArrayList<>();
    		while (cur < last) {
    			TreeNode node = queue.get(cur);
    			list.add(node.val);
    			if (node.right != null) {
    				queue.add(node.right);
    			}
    			if (node.left != null) {
    				queue.add(node.left);
    			}
    			cur++;
    		}
    		lists.add(list);
    	}
    	return lists;
    }

好,至此关于编程之美上面的关于本题的解法就没了。我接下来在说两种方案,是我在没看本书解答时,自己写的。

方法一,是用了两个队列,一个队列存储k-1层的节点,另一个队列存储k层的节点。代码如下:

public List<List<Integer>> levelOrder1(TreeNode root) {
    	List<List<Integer>> lists = new ArrayList<List<Integer>>();
    	if (root == null)
    		return lists;
    	LinkedList<TreeNode> preQueue = new LinkedList<>();
    	LinkedList<TreeNode> curQueue = new LinkedList<>();
    	preQueue.offer(root);
    	curQueue.offer(root);
    	while (!curQueue.isEmpty()) {
    		List<Integer> list = new ArrayList<>();
    		while (!curQueue.isEmpty()) {
    			TreeNode node = curQueue.poll();
        		list.add(node.val);
        	}
    		lists.add(list);
    		while (!preQueue.isEmpty()) {
    			TreeNode node = preQueue.poll();
    			if (node.left != null) {
    				curQueue.offer(node.left);
    			}
    			if (node.right != null) {
    				curQueue.offer(node.right);
    			}
    		}
    		preQueue = new LinkedList<>(curQueue);
    	}
    	return lists;
    }

个人感觉也挺高效的,但是两个队列,实现有点浪费。于是,改造成了如下代码

public List<List<Integer>> levelOrder4(TreeNode root) {
    	List<List<Integer>> lists = new ArrayList<List<Integer>>();
    	if (root == null)
    		return lists;
    	Queue<TreeNode> queue = new LinkedList<>();
    	queue.offer(root);
    	while (!queue.isEmpty()) {
    		int levelNum = queue.size();
    		List<Integer> list = new LinkedList<>();
    		for (int i = 0; i < levelNum; i++) {
    			TreeNode node = queue.poll();
    			if (node.left != null) {
    				queue.offer(node.left);
    			}
    			if (node.right != null) {
    				queue.offer(node.right);
    			}
    			list.add(node.val);
    		}
    		lists.add(0, list);
    	}
    	return lists;
    }

个人感觉,这是四种里面最好的办法了。。。。。你认为呢?
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值