剑指offer中树的总结(基于层次遍历)

剑指offer中树的总结(基于层次遍历)

 说明:本人全都手撸过以下代码,并调试过的,也在XXX网上,经过 测试,有些是从网上抄袭的。我不是计算机科班 出身,学习起来可能稍有些吃力,但是就是这么一道道 题目刷起来,慢慢就会对解决某类型题目有点思路。最初接触的时候确实是无从下手,不知道怎么回事儿,简单的 递归能看明白,直到现在还是有些复杂的递归程序理解起来还是有些吃力,但是个人觉得,当理解不了,别人的程序的时候,就一行一行的走断点,多走几遍就会理解,当再遇到一个比当前更深入的程序时,就会觉得当前的这段小程序 很简单。所以,我把有些类似的题目总结起来,这样能更好的掌握。

    

  第一类型的题目:树的层次遍历及拓展应用。

         基础题:从上往下打印出二叉树的每个节点,同层节点从左至右打印。

        

 public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
         
//    	所有结点的list
    	ArrayList<Integer> allNodeList = new ArrayList<Integer>();
    	LinkedList<TreeNode> allNodeTmp = new LinkedList<TreeNode>();
//    	为空判断,并处理
    	if (root==null) {
			return allNodeList;
		}
    	allNodeTmp.add(root);
    	while (!allNodeTmp.isEmpty()) {
    		TreeNode currentNode =  allNodeTmp.remove();
			allNodeList.add(currentNode.val);
			if (currentNode.left!=null) {
                allNodeTmp.add(currentNode.left);
			}
			if (currentNode.right!=null) {
				allNodeTmp.add(currentNode.right);
			}
		}
    	return allNodeList;
    }
这是本类型中的基础题,以下几个题目都是在此基础上进行扩展的。

所以,将这道基础题思路是:

   1、定义一个返回值ArrayList<Integer>,用来存储最终的返回结果。

   2、定义一个LinkedList<Integer>用来临时存储每个结点,类似用C实现时的循环队列功能。

   3、这里是add再remove,而且此时的LinkedList的remove方法,恰好是remove了最先进入list中的数据,先进先出的特点。

扩展题一:

   从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

   示例程序:

    

ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
		/**
		 * 定义三个变量:ArrayList<ArrayList<Integer>> result 用来存储所有的行
		 * 2、定义每行的临时存放的ArrayList<Integer>
		 * 3、定义一个LinkedList存放遍历的结点。此处不能使用ArrayList 因为ArrayList的remove()方法需要参数,没有默认的remove()
		 */
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        ArrayList<Integer> currentRowlist = new ArrayList<Integer>();
        LinkedList<TreeNode> nodeListTmp = new LinkedList<TreeNode>();
        /**
         * 判断根结点是否为null
         */
        if(pRoot == null)
            return result;
//        添加根结点
        nodeListTmp.add(pRoot);
//        定义两个变量
        int currentRowNumSizeFlag = 1, nextRowNodeSize = 0;
//        判断是否还有结点
        while(!nodeListTmp.isEmpty()) {
//        	如果有结点,取出该结点,
            TreeNode t = nodeListTmp.remove();
//            改行的node值减1
            currentRowNumSizeFlag--;
//            将该结点的值添加到每行list中
            currentRowlist.add(t.val);
//            判断是否有左右孩子,如果有则添加到所有结点的list中
            if(t.left != null) {
            	nodeListTmp.add(t.left);
//            	改行结点数+1
            	nextRowNodeSize++;
            }
            if(t.right != null) {
            	nodeListTmp.add(t.right);
            	nextRowNodeSize++;
            }
//            如果对改行的所有结点遍历结束,完成了添加到每行结点数,则输出每行结点数,添加到每行的所有行的list中
            if(currentRowNumSizeFlag == 0) {
            	result.add(new ArrayList<Integer>(currentRowlist));
//                清空临时存放每行结点list
                currentRowlist.clear();
//                将下一层的所有结点数赋值给当前行node个数
                currentRowNumSizeFlag = nextRowNodeSize;
//                下一层结点数重新赋值为0
                nextRowNodeSize = 0;
            }
        }
        return result;
    }

  

 相比基础程序添加了两个控制变量currentRowNodeSizeFlag、nextRowNodeSize,一个临时行list存储当前行的currentRowNodeList. 

扩展题二:

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

示例程序:

  public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
//    	最终的返回结果
    	ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
    	if (pRoot==null) {
			return result;
		}
//    	当前访问的行
    	ArrayList<Integer> currentRowList = new ArrayList<Integer>();
//    	所有结点 注意此处应当是TreeNode 不是Integer类型的变量
//    	LinkedList<Integer> allNodeTmp = new LinkedList<Integer>();
    	LinkedList<TreeNode> allNodeTmp = new LinkedList<TreeNode>();
    	
//    	定义 当前行中的结点,下一行的结点
    	int currentRowNodeSize =1;
    	int nextRowNodeSize = 0;
    	allNodeTmp.add(pRoot);
    	boolean leftFirst = true;
    	while(!allNodeTmp.isEmpty()){
//    		取出当前行
    		TreeNode currentNode = allNodeTmp.remove();
//    		将当前的val添加currentRowList中
    		currentRowList.add(currentNode.val);
//    		当前行的size-1
    		currentRowNodeSize--;
//    		如果当前结点的左右孩子不为null,(添加到currentRowNodeList中,此处是错误的) 添加到allNodeTmp中,
//    		下行的nodesize++
    			if (currentNode.left!=null) {
        			allNodeTmp.add(currentNode.left);
        			nextRowNodeSize++;
        		}
        		if (currentNode.right!=null) {
        			allNodeTmp.add(currentNode.right);
        			nextRowNodeSize++;
        		}
        		
    	
//    		如果当前行中所有结点遍历完,则打印当前行
    		if (currentRowNodeSize==0) {
    			if (!leftFirst) {
					ArrayList<Integer> revserList = new ArrayList<Integer>();
					for (int i = currentRowList.size()-1; i >=0 ; i--) {
						
						revserList.add(currentRowList.get(i));
					}
					result.add(new ArrayList<Integer>(revserList));
				}else {
					
					result.add(new ArrayList<Integer>(currentRowList));
				}
//				此处一定要清空currentRowList
				currentRowList.clear();
				currentRowNodeSize=nextRowNodeSize;
				nextRowNodeSize = 0;
				leftFirst = !leftFirst?true:false;
			}
    		
    	}
		return result;
        
    }

在上面三道题的基础上再来看:  求二叉树定的宽度,就很简单了。

以上的这道题,没有用递归实现,之后会将递归单独总结。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值