二叉树的解决方法

二叉树的问题的解决办法主要采用递归的方法解决


主要有两种:

递归遍历

递归分治

90%的问题可以采用递归分治的办法解决。

1.递归遍历的方法

定义一个全局变量,将该变量作为一个结果参数传入一个定义的空返回值类型的辅助函数。

递归遍历的方法,是一种不断得递归深入遍历,亲力亲为。遍历的顺序很重要。


2.递归分治的方法

分治的办法通常可以不用定义辅助函数,或者定义一个有返回结果的辅助。分治递归左右子树的顺序不重要

分治的方法,是将该任务分配左右子节点。

分治法,关键是怎样利用左右子树的结果,即假设已经获得左右子树的结果,怎样利用左右子树的结果获得全局的结果。

思考一个关系:左右子树的结果和整棵树的结果的关系是什么?


递归的边界条件:首先考虑,root为空的情况

再考虑叶子节点的方法


1.前序遍历的三种写法

private static class TreeNode {
		int val;
		TreeNode left;
		TreeNode right;
		public TreeNode(int val) {
			this.val = val;
			left = null;
			right = null;
		}
	}
	public List<Integer> preorderTraversal_1(TreeNode root) {
		List<Integer> result = new ArrayList<Integer>();
		if (root == null) {
			return result;
		}
		Stack<TreeNode> stack = new Stack<TreeNode>();
		stack.push(root);
		while(!stack.isEmpty()) {
			TreeNode node = stack.pop();
			result.add(node.val);
			if (node.right != null) {
				stack.push(node.right);
			}
			if (node.left != null) {
				stack.push(node.left);
			}
		}
		return result;
	}
	
	//用遍历递归的方式
	public List<Integer> preorderTraversal_2(TreeNode root) {
		List<Integer> result = new ArrayList<Integer>();
		preorderCore(root, result);
		return result;
	}
	public void preorderCore(TreeNode root, List<Integer> result) {
		if (root == null) {
			return ;
		}
		result.add(root.val);
		if (root.left != null) {
			preorderCore(root.left, result);
		}
		if (root.right != null) {
			preorderCore(root.right,result);
		}
	}
	
	//用分而治之递归的方式
	public List<Integer> preorderTraversal_3(TreeNode root) {
		List<Integer> result = new ArrayList<Integer>();
		if (root == null) {
			return result;
		}
		List<Integer> left = preorderTraversal_3(root.left);
		List<Integer> right = preorderTraversal_3(root.right);
		
		result.add(root.val);
		result.addAll(left);
		result.addAll(right);
		return result;
	}


2.求树的最大深度

public class Solution1 {
		//分治法
		public int maxDepth(TreeNode root) {
			if (root == null) {
				return 0;
			}
			int left = maxDepth(root.left);
			int right = maxDepth(root.right);
			return left > right ? (left + 1) :(right + 1);
		}
	}
	
	public class Solution_1 {
		
		//递归遍历法
		//1.先定义一个全局变量depth用来保存根性最终的树高结果。2.定义一个局部变量保存当前的树高
		
		private int depth;
		public int maxDepth1(TreeNode root) {
			depth = 0;
			helper (root, 1);
			return depth;
		}
		private void helper(TreeNode root, int curDepth) {
			if (root == null) {
				return ;
			}
			if (curDepth > depth) {
				depth = curDepth;
			}
			helper(root.left, curDepth + 1);
			helper(root.right, curDepth + 1);
		}
	}


3.求二叉树的所有路径

public class Solution2 {
		/*
		 * 二叉树的所有路径:     用分治的方法
		 * 递归的拆解:
		 * 思考这样一个问题:整棵树的结果和左右子树的结果的联系是什么? 即假设已经找到二叉树的左子树的所有路径和右子树的所有路径和根,
		 * 就是不管三七二十一,先把左节点和右节点传入递归函数中,获得左子树和右子树的所有路径,思考怎样将他和根连接起来?
		 * 那么现在怎么获得整棵树的所有路径?
		 * 可以这样解决;
		 * 将根和左子树的所有路径都连接起来
		 * 将根和右子树的所有路径都连接起来
		 * 
		 * 递归的出口:
		 * 通常递归的出口根是否为空
		 * 现在要思考当root就是叶子节点时,也要加入到路径中,即当根的左右子树为空时,也要写递归出口
		 */	
		public List<String> binaryTreePaths(TreeNode root) {
			List<String> result = new ArrayList<String>();
			if (root == null) {
				return result;
			}
			if (root.left == null && root.right == null) {
				result.add("" + root.val);
			}
			
			List<String> left = binaryTreePaths(root.left);
			List<String> right = binaryTreePaths(root.right);
			
			for (String path : left) {
				result.add(root.val + "->" + path);
			}
			for (String path : right) {
				result.add(root.val + "->" +path);
			}
			
			return result;
		}
	}
	
	
	public class Soulution_2 {
		/*
		 * 用递归遍历的方法,定义一个空返回值的辅助函数。
		 * 递归遍历的辅助函数写法这样去思考:
		 * 只有根节点的情况下,怎样写它的路径,则将这个初始路径作为参数传入辅助函数,最后递归遍历
		 * 根据这个初始的路径来递归遍历出所有的路径。
		 * 那么这种情况下就至少有三个参数传入:1.root,2.只有根节点的路径表示方式,path,3.全局参数,结果result
		 */
		public List<String> binaryTreePaths(TreeNode root) {
			List<String> result = new ArrayList<String>();
			if (root == null) {
				return result;
			}
			helper (root, String.valueOf(root.val), result);
			return result;
		}
		private void helper(TreeNode root, String path, List<String> result) {
			if (root == null) {
				return ;
			}
			if (root.left == null && root.right ==null) {
				result.add(path);
			}
			if (root.left != null) {//递归遍历调用函数,先左子树再右子树
				helper(root.left, path + "->" + String.valueOf(root.left.val), result);
			}
			if (root.right != null) {
				helper(root.right, path + "->" + String.valueOf(root.right.val), result);
			}
		}
	}


4.求出最小和子树

	public class Solution3 {
	    /*
		 * 求最小和子树,采用分治和遍历递归的结合方式
	     */
		//首先依据遍历的特点,定义全局的的最小值和全局的节点。
	    private int curSum = Integer.MAX_VALUE;
	    private TreeNode sub = null;
	    public TreeNode findSubtree(TreeNode root) {
	        helper(root);
	        return sub;
	    }
	    private  int helper(TreeNode root) {//分治的方法求出和,  遍历的方式与全局比较
	        if (root == null) {
	            return 0;
	        }
	        //依据分治的思想,分别求出左右子树的和,再求出当前的和,最后返回的值是当前的子树和,以便下次调用。
	        int sum = root.val + helper(root.left) + helper(root.right); //用分治的方法
	        if (sum <= curSum) { //打擂台
	            curSum = sum;
	            sub = root;
	        }
	        return sum;//返回当前子树的和
	    }	
	}


public class Solution {
    /*
     * 采用完全分治的思想
     */
    private class resultType {
        public TreeNode sub;
        public int minSum;
        public int sum;
        public resultType(TreeNode sub, int minSum, int sum) { //如何定义构造函数
            this.sub = sub;
            this.minSum = minSum;
            this.sum = sum;
        }
    }

    public TreeNode findSubtree(TreeNode root) {
        resultType result = helper(root);
        return result.sub;
    }
    private resultType helper(TreeNode root) {
        if (root == null) {
            return new resultType(null, Integer.MAX_VALUE, 0);
        }
        resultType left = helper(root.left);
        resultType right = helper(root.right);
        resultType result = new resultType(
            root,
            root.val + left.sum + right.sum,
            root.val + left.sum + right.sum
        );
        if (left.minSum <= result.minSum) {
            //result.sub = root.left; //不能直接判断此时的最小节点就是root.left
            result.sub = left.sub;
            result.minSum = left.minSum;
        }
        if (right.minSum <= result.minSum) {
            //result.sub = root.right;//不能直接就是root.right
            result.sub = right.sub;
            result.minSum = right.minSum;
        }
        return result;
    }
}

4. 当用分治的方法写辅助函数需要返回两个值的时候,可以重新定义结果类ResultType用来保存返回的结果

	/*
	 * 应用ResultType的典型
	 * 平衡二叉树的判断,左子树和右子树都是平和二叉树,并且左右子树的高度差不超过1
	 * 平衡二叉树的高度是一个logN
	 * 1.需要知道左右子树是否是平衡二叉树
	 * 2.需要知道整颗二叉树的高度。
	 * 两个条件需要同时满足才返回它是平衡的,因此写的时候,对两个条件分别判断,当其中之一不满足的时候直接返回false.
	 * 因此结果需要返回两个值
	 * 当需要返回多个值的时候就用一个resultType类
	 */
	
	public class Solution4 {
		private class ResultType {
			private boolean isBalanced;
			private int high;
			public ResultType(boolean isBalanced, int high) {
				this.isBalanced = isBalanced;
				this.high = high;
			}
		}
		
		public boolean isBalanced(TreeNode root) {
			return helper(root).isBalanced;
		}
		
		private ResultType helper(TreeNode root) {
			if (root == null) {
				return new ResultType(true, 0);
			}
			ResultType left = helper(root.left);
			ResultType right = helper(root.right);
			
			if(!left.isBalanced || !right.isBalanced) {
				return new ResultType(false, -1);
			}
			
			if(Math.abs(left.high - right.high) > 1) {
				return new ResultType(false, -1);
			}
			
			return new ResultType(true, Math.max(left.high, right.high)+ 1);
		}
	}
	public class Solution4_1 {
	    /*
	     * 不用ResultType时的计算办法
	     * 主要利用二叉树的高度,只要用二叉树的高度最后不是-1,(用-1来代表非平衡二叉树)那么就是平衡二叉树
	     * 1.也是用分治的方法求出二叉树的高度,但是要在里面进行判断,如果左右子树的高度差超过1,或者左右子树是非平衡二叉树(-1),那么返回-1.
	     */
	    public boolean isBalanced(TreeNode root) {
	        return find(root) != -1;
	    }
	    public int find(TreeNode root) {
	        if (root == null) {
	    		return 0;
	    	}
	    	int left = find( root.left );
	    	int right = find( root.right);
	    	if (left == -1 || right == -1 || Math.abs(left - right) > 1) {
	    	    return -1;
	    	}
	    	return Math.max(left, right) + 1;
	    }
	}

5. 结合ResType和分治+递归遍历结合的方式

	/*
	 * 最大平均数的子树
	 * 同样取思考一个问题,整个二叉树的平均值和左子树右子树的平均值由什么关系(没什么关系?尴尬)
	 * 与求最小和子树一样,用分治+递归遍历的方法,但是此时需要返回两个数,用来计算平均值(一个是子树的和,一个是子树的个数),
	 * 所以需要用重新定义一个ResulType用来保存子树的和与子树的个数
	 * 遇到这种二叉树的最大最小子树问题,就要想到打擂台方法,需要定义一个全局的变量,比较当前值取过去值。
	 * 现在定义这个全局变量有一个特殊的地方,就是要定义一个类型也是ResultType的值来保存过去的最大平均数。一个是TreeNode节点。
	 */
	public class Solution5 {
		private class ResultType {
			public int sum, size;
			public ResultType(int sum, int size) {
				this.sum = sum;
				this.size = size;
			}
		}
		private ResultType subtreeResult = null; //定义一个全局的变量用来保存之前的最大平均数(用sum和size)
		private TreeNode node = null;
		
		public TreeNode findSubtree2(TreeNode root) {
			helper(root);
			return node;
		}
		private ResultType helper(TreeNode root) {
			if (root == null) {
				return new ResultType(0,0);
			}
			ResultType left = helper(root.left);
			ResultType right = helper(root.right);
			
			ResultType result = new ResultType(
					left.sum + right.sum +root.val,
					left.size + right.size +1);
			
			if (node == null || result.sum * subtreeResult.size > subtreeResult.sum * result.size) {
				subtreeResult = result;
				node = root;
			}
			return result;
		}
	}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二叉树的遍历过程中,可能会遇到以下几个问题: 1. 如何区分三种遍历方式:前序遍历、中序遍历和后序遍历? 解决方法:在二叉树的遍历过程中,需要先确定遍历的顺序,然后按照顺序访问每个节点。前序遍历的顺序是“根节点-左子树-右子树”,中序遍历的顺序是“左子树-根节点-右子树”,后序遍历的顺序是“左子树-右子树-根节点”。 2. 如何实现递归遍历? 解决方法:递归遍历是二叉树遍历的常用方法之一。递归遍历的思路是,先遍历当前节点,然后递归遍历左子树和右子树。在实现递归遍历时,需要注意递归结束的条件,即如果当前节点为空,则返回。 3. 如何实现非递归遍历? 解决方法:非递归遍历是二叉树遍历的另一种方法,它使用栈来实现。具体实现方法是,先将根节点入栈,然后循环执行以下操作:从栈中取出一个节点,并访问它;如果该节点有右子树,则将右子树入栈;如果该节点有左子树,则将左子树入栈。 4. 如何实现层次遍历? 解决方法:层次遍历是二叉树遍历的一种特殊方式,它使用队列来实现。具体实现方法是,先将根节点入队,然后循环执行以下操作:从队列中取出一个节点,并访问它;如果该节点有左子树,则将左子树入队;如果该节点有右子树,则将右子树入队。重复执行以上操作,直到队列为空。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值