自学算法之判断一个二叉树是否平衡/搜索/完全二叉树

话不多说,在面试中遇到过,一脸蒙蔽,被虐出翔…以下所述,仅仅是手撕代码时候使用,若是需要在线编程,可以根据该思路编写对应AC代码。

  • 如何判断一个二叉树是否平衡?要解决这个问题,首先要知道什么是平衡二叉树。
    1. 平衡二叉树定义如下:

      • 首先,是一个二叉树,且每个节点的左右子树的高度差的绝对值不超过1。
      • 其次,空树是平衡的。
    2. 试想要实现该代码,需要怎么记录其中的高度差、当前节点是否平衡、以及怎么统计每个节点的左右子树的高度,话不多说,解决思路如下:

      • 高度差和左右子树高度,其实我们只需要记录一个,除此之外,还需记录当前节点是否平衡,那么总共有两个变量,数据结构如下:

        	static class returnDate {
        		// 是否平衡
        		public boolean isBlance;
        		// 树高度
        		public int h;
        		public returnDate(boolean isBlance, int h) {
        			this.isBlance = isBlance;
        			this.h = h;
        		}
        	}
        
      • 确定返回数据结构后,我们可以使用深度遍历,判断每个节点是否平衡。

        1. 使用前序遍历,遍历二叉树所有节点。
        	public static returnDate process(TreeNode head) {
        		// 空=平衡
        		if(head == null) {
        			return new returnDate(true, 0);
        		}
        		returnDate leftDate = process(head.left);
        		// 判断左子树是否平衡
        		if(!leftDate.isBlance) {
        			return new returnDate(false, 0);
        		}
        		returnDate rightDate = process(head.right);
        		// 判断右子树是否平衡
        		if(!rightDate.isBlance) {
        			return new returnDate(false, 0);
        		}
        		// 高度差===判断平衡
        		if(Math.abs(leftDate.h - rightDate.h) > 1) {
        			return new returnDate(false, 0);
        		}
        		// 当前结点平衡,返回高度
        		// 当前左右子树最高加上当前节点
        		return new returnDate(true, Math.max(leftDate.h, rightDate.h) + 1);
        	}
        
  • 平衡二叉树已经解决,那么我再看怎么判断一个二叉树是BST树
    1. BST,即binary search tree二叉搜索树,其性质如下:
      • 若任意结点的左子树不空,则左子树上的所有节点均小于其根节点。
      • 若任意结点的右子树不空,则右子树上的所有节点均大于其根节点。
      • 任意节点的左、右子树皆为BST树。
      • 没有值相等的数,即数值不重复。
    2. 其实,有很多种方法解决该问题,笔者只提供其中一种。
      • 解决思路:如果某二叉树的中序遍历是升序的,则该树是BST树。
      • 中序遍历有递归和非递归两种实现方法,在此笔者选择非递归实现。
    	public static boolean isBST(TreeNode head) {
    		if(head == null) return true;
    		Stack<TreeNode> stack = new Stack<>();
    		TreeNode cur = head;
    		// 记录状态
    		boolean is_bst = true;
    		// 前一个节点
    		int pre = Integer.MIN_VALUE;
    		while(!stack.isEmpty() || cur != null) {
    			if(cur != null) {
    				stack.push(cur);
    				cur = cur.left;
    			} else {
    				cur = stack.pop();
    				// 迭代
    				if(is_bst) {
    					// 初始化前一个节点
    					pre = cur.val;
    					is_bst = false;
    				} else if(pre > cur.val) { 
    					return false;
    				} else {
    					// 记录前一个节点
    					pre = cur.val;
    				}
    				cur = cur.right;
    			}
    		}
    		return true;
    	}
    
  • 最后一个判断完全二叉树逻辑比较复杂,万事开头难,先从简单走起。
    1. 完全二叉树的定义:

      • 对于深度为K的,N个节点 二叉树,当且仅当其每一个节点都与深度为K的满二叉树中编号从1到N的结点。

      • 其实,看了其定义,笔者也看不懂。

      • 简单点,要画一个完全二叉树,必须先画根节点,再画左结点,最后画右结点,画的时候顺序必须严格一致。

      在这里插入图片描述

    2. 解决思路:

      • 先确定遍历方式:笔者使用层序遍历
      • 明确违反完全二叉树条件:
        1. 如果一个节点,有右子结点,没有左子结点,则一定不是完全二叉树。
        2. 如果一个节点左右子节点不全,即有左没右或两个都没有,若出现该状态,则接下来的节点都必须是叶子节点。
      	/**
      	 * 判断一个树是否是完全二叉树
      	 * 思路, 按层遍历
      	 * Q1,如果一个节点有右结点,而没有左结点,一定不是。
      	 * Q2,如果一个节点左右不全, 即有左没右,或左右都没有, 该节点之后出现的结点,必须是叶子结点。
      	 * @author commonsstring@gmail.com
      	 *
      	 */
      	public static boolean isCBT(TreeNode head) {
      		if(head == null) return true;
      		Queue<TreeNode> queue = new LinkedList<>();
      		queue.offer(head);
      		// 左右孩子
      		TreeNode left = null;
      		TreeNode right = null;
      		// 开启状Q2
      		boolean flag = false;
      		while(!queue.isEmpty()) {
      			TreeNode current = queue.poll();
      			left = current.left;
      			right = current.right;
      			// 开启状态后, 节点不会存在左右子节点
      			if(flag && (left != null || right != null) ||
      					(left == null && right != null)) {
      				return false;
      			}
      			// 层序遍历, 入队逻辑
      			if(left != null) {
      				queue.offer(left);
      			}
      			if(right != null) {
      				queue.offer(right);
      			}
      			// 遇到Q2, 开启状态, 即以后的每一个节点都是叶子节点
      			if(left == null || right == null) {
      				flag = true;
      			}
      		}
      		return true;
      	}
      
  • 最后,只要记住每种二叉树的性质,代码很好写,仅是效率的差别而已。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值