java实现判断是否为二叉搜索树

24 篇文章 0 订阅
23 篇文章 0 订阅

标题:java实现判断是否为二叉搜索树

一、分析
1)第一种:
对于二叉搜索树,首先想到的的是中序遍历,得到的是一个有序序列。
所以方法可以是使用中序的递归,非递归进行实现,只要看是否为有序即可。

  • 方法一:中序非递归遍历,保存最小值为long cur = Long.MIN_VALUE;每次遍历得到一个节点的值时,比较是否大于当前最小值cur,满足条件则更新当前最小值。
/**
	 * 使用非递归实现  迭代版的【非递归中序遍历】,简单,容易实现
	 * 执行用时:2 ms, 在所有 Java 提交中击败了31.16% 的用户
内存消耗:37.9 MB, 在所有 Java 提交中击败了94.63% 的用户
	 */
	public boolean isBst02(TreeNode head) {
		if(head == null) {
			return true;
		}
		Deque<TreeNode> s = new LinkedList<>();
		s.push(head);
		long cur = Long.MIN_VALUE;
		
		while(!s.isEmpty()) {
			TreeNode node = s.peek();
			if(node.left != null) {
				s.push(node.left);
			}else {
				node = s.pop();
				System.out.print(node.val + " ");
				//判断node是否 > cur
				if(node.val <= cur) {
					return false;
				}
				cur = node.val;
				
				while(node.right == null && !s.isEmpty()) {
					node = s.pop();
					System.out.print(node.val + " ");
					
					//判断node是否 > cur
					if(node.val <= cur) {
						return false;
					}
					cur = node.val;
				}
				if(node.right != null) {
					s.push(node.right);
				}else {
					break;
				}
			}
		}
		
		return true;
	}
  • 方法二:中序递归遍历:实现一,中序遍历后,将得到的节点存到list中,通过for循环进行遍历list,判断是否为有序的【即总是有后面一个小于前面一个节点的值】
/**
	 * 使用中序遍历 存到list中,再比较  【简单】
	 * 执行用时:2 ms, 在所有 Java 提交中击败了31.16% 的用户
内存消耗:38.2 MB, 在所有 Java 提交中击败了79.34% 的用户
	 */
	public boolean isBst03(TreeNode head, List<Integer> list) {
		for(int i = 1; i < list.size(); i++) {
			if(list.get(i) <= list.get(i - 1)) {
				return false;
			}
		}
		
		return true;
	}
/**
	 * 中序遍历
	 */
	public void inOrder(TreeNode head, List<Integer> list) {
		if(head == null) {
			return ;
		}else {
			this.inOrder(head.left, list);
			System.out.print(head.val + " ");
			list.add(head.val);
			this.inOrder(head.right, list);
		}
	}
  • 实现三:对中序递归遍历的升级版,使用一个对象保存当前已遍历到的节点的最小值【使用一个内部类,创建一个对象 new Cur(Long.MIN_VALUE))】,递归遍历完左边节点回退的时候【中序遍历的思想:递归遍历完左边节点回退时,System.out.print(node.val);】,取得当前节点的值,若小于当前最小值,则更新最小值,否则,说明不是有序的,return false;使用对象保存当前节点的最小值,是因为这样的话,递归回退的时候,最小值不会改变(类似 传递参数时count + 1 【count不会变】 与 count ++【count会改变】的差别)
class Cur{
		long cur;
		public Cur(long cur) {
			this.cur = cur;
		}
	}
	/**
	 * 因为二叉搜索树,中序遍历得到的是有序序列,
	 * 所以 使用中序遍历,每次比较node节点之间的最小值  与 node,即:cur min【保存node节点之前的最小值】 < node  
	 * 执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:38.1 MB, 在所有 Java 提交中击败了84.19% 的用户
	 * @param head
	 * @param cur_min
	 * @return
	 */
	public boolean isBst(TreeNode head, Cur cur_min) {
		if(head == null) {
			return true;
		}else {
			boolean b1 = this.isBst(head.left, cur_min);
			if(!b1) {
				return false;
			}
			if(head.val <= cur_min.cur) {
				return false;
			}
			cur_min.cur = head.val;
			boolean b2 = this.isBst(head.right, cur_min);
			if(!b1 || !b2) {
				return false;
			}
			
			return true;
		}
	}
  • 实现四:使用一个成员变量指向当前最小值的节点,思想和上面的实现二 一样,使得递归回退的时候,最小值不会改变。
/**
	 * 参考
	 * 注意TreeNode p;为一个成员变量
	 * 这样下面的程序才可以改变p
	 * 执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:38 MB, 在所有 Java 提交中击败了89.36% 的用户
	 * @param head
	 * @param cur_min
	 * @return
	 */
	TreeNode p;  //将这个p设置为成员遍历才行,这样下面的程序才可以改变p,不然随着函数的退回,p的值又回到了原来,所以这个的p不能作为形参,而是成员变量
	public boolean isBst011(TreeNode head) {
		if(head == null) {
			return true;
		}else {
			boolean b1 = this.isBst011(head.left);
			if(!b1) {
				return false;
			}
			if(p != null && head.val <= p.val) {
				return false;
			}
			p = head;
			boolean b2 = this.isBst011(head.right);
			if(!b1 || !b2) {
				return false;
			}
			
			return true;
		}
		
	}

2)第二种:
使用先序递归遍历:每次遍历的时候,使得 XXX < node.val < YYY

  • 实现一:每次遍历节点的时候,要求node满足 left最大值 < node.val < right最小值
/**
	 * 方法二:  【可以看看对称(镜像)二叉树的思想】
	 * 使用递归求解
	 * 执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
	内存消耗:38.2 MB, 在所有 Java 提交中击败了87.07% 的用户
	 */
	public boolean isBst03(TreeNode node) {
		if(node == null) {
			return true;
		}else {					//理论上 why:因为后面的多次重复过程,											
			if(node.left != null) {
				int max = this.rightMax(node.left);//左边节点的理论上(node.left一直向右)的最大值 < 该节点
				if(max >= node.val) {
					return false;
				}
			}
			if(node.right != null) {
				int min = this.leftMin(node.right);右边节点的理论上(node.right一直向左)的最小值 > 该节点
				if(min <= node.val) {
					return false;
				}
			}
			boolean res1 = this.isBst03(node.left);
			boolean res2 = this.isBst03(node.right);
			
			return res1 && res2;
		}
		
	}
	/**
	 * 寻找node节点理论上的最大值  找到node节点的最右边的值
	 * @param node
	 * @return
	 */
	public int rightMax(TreeNode node) {
		while(node.right != null) {
			node = node.right;
		}
		return node.val;
	}
	/**
	 * 寻找node节点理论上的最小值  找到node节点的最左边的值
	 * @param node
	 * @return
	 */
	public int leftMin(TreeNode node) {
		while(node.left != null) {
			node = node.left;
		}
		return node.val;
	}
  • 实现二:每次遍历节点的时候,要求node满足 node.left.val < node.val < node.right.val 同时 node.left.val > min && node.right.val < max
/**
	 * 注意:这个题目可以补全二叉树来做,得到 头结点上面的     min,max
	 * min < left < node.val < right < max
	 * 使用[先序遍历]
	 * 每次使node.val > left && node.val < right
	 * 同时left > min && right < max
	 * 
	 * 递归
	 */
	public boolean isBst03(TreeNode head, long max, long min) {
		if(head == null) {
			return true;
		}else {
			if(head.left != null) {//节点head 左边的值 < head.val ,  同时要 > min
				if(head.left.val >= head.val || head.left.val <= min) {
//					System.out.println("left:");
//					System.out.println(" head.val:" + head.val + " head.left.val:" + head.left.val + " min:" + min);					
					return false;
				}
			}
			if(head.right != null) { //节点head 右边的值 > head.val, 同时要 < max
				if(head.right.val <= head.val || head.right.val >= max) {
//					System.out.println("right;");
//					System.out.println(" head.val:" + head.val + " head.right.val:" + head.right.val + " max:" + max);
					return false;
				}
			}
			boolean b1 = this.isBst03(head.left, head.val, min);  //head.val
			min = head.val;
			boolean b2 = this.isBst03(head.right, max, min);
			
			return b1 && b2;
		}
		
	}
	
	/**
	 * 上面的简化
	 * 执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:37.9 MB, 在所有 Java 提交中击败了94.21% 的用户
	 * @param head
	 * @return
	 */
	public boolean isValidBST02(TreeNode head) {
        return this.isBst03(head, Long.MAX_VALUE, Long.MIN_VALUE);
    }
  • 实现三:类似实现二的简化版,每次遍历节点的时候,要求node满足 min < node.val < max
/**
	 * 使用参考 思想,类似上面的
	 * 使用先序遍历
	 * min < node.val < max
	 * 执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
		内存消耗:38.2 MB, 在所有 Java 提交中击败了77.43% 的用户
	 */
	public boolean isBst04(TreeNode head, long max, long min) {
		if(head == null) {
			return true;
		}else {
			if(head.val >= max || head.val <= min) {
				return false;
			}
			
			boolean b1 = this.isBst04(head.left, head.val, min);
			boolean b2 = this.isBst04(head.right, max, head.val);
			
			return b1 && b2;
			
		}
	}
	public boolean isValidBST03(TreeNode head) {
        return this.isBst04(head, Long.MAX_VALUE, Long.MIN_VALUE);
    }

那么实现二,实现三的min, max怎么确定呢?,见下图

  • 补全二叉树,找到min,max的规律【可以看看几个节点,进而确定】

在这里插入图片描述

完整实例代码如下;

/**
 * 判断是否为二叉搜索树:
 * 重点:其一:补全了二叉树的图,得到head的  max, min
 * 其二:可以使用成员遍历,及时保存最新的 最小值,不需要传递参数了  
 * --> 我之前的做法是使用一个内部类,传递参数实现  【一个对象】
 * 
 * @author dell
 *
 */
public class TestIsBst {
	
	class Cur{
		long cur;
		public Cur(long cur) {
			this.cur = cur;
		}
	}
	/**
	 * 因为二叉搜索树,中序遍历得到的是有序序列,
	 * 所以 使用中序遍历,每次比较node节点之间的最小值  与 node,即:cur min【保存node节点之前的最小值】 < node  
	 * 执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:38.1 MB, 在所有 Java 提交中击败了84.19% 的用户
	 * @param head
	 * @param cur_min
	 * @return
	 */
	public boolean isBst(TreeNode head, Cur cur_min) {
		if(head == null) {
			return true;
		}else {
			boolean b1 = this.isBst(head.left, cur_min);
			if(!b1) {
				return false;
			}
			if(head.val <= cur_min.cur) {
				return false;
			}
			cur_min.cur = head.val;
			boolean b2 = this.isBst(head.right, cur_min);
			if(!b1 || !b2) {
				return false;
			}
			
			return true;
		}
	}
	
	/**
	 * 参考
	 * 注意TreeNode p;为一个成员变量
	 * 这样下面的程序才可以改变p
	 * 执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:38 MB, 在所有 Java 提交中击败了89.36% 的用户
	 * @param head
	 * @param cur_min
	 * @return
	 */
	TreeNode p;  //将这个p设置为成员遍历才行,这样下面的程序才可以改变p,不然随着函数的退回,p的值又回到了原来,所以这个的p不能作为形参,而是成员变量
	public boolean isBst011(TreeNode head) {
		if(head == null) {
			return true;
		}else {
			boolean b1 = this.isBst011(head.left);
			if(!b1) {
				return false;
			}
			if(p != null && head.val <= p.val) {
				return false;
			}
			p = head;
			boolean b2 = this.isBst011(head.right);
			if(!b1 || !b2) {
				return false;
			}
			
			return true;
		}
		
	}
	
	/**
	 * 使用非递归实现  迭代版的【非递归中序遍历】,简单,容易实现
	 * 执行用时:2 ms, 在所有 Java 提交中击败了31.16% 的用户
内存消耗:37.9 MB, 在所有 Java 提交中击败了94.63% 的用户
	 */
	public boolean isBst02(TreeNode head) {
		if(head == null) {
			return true;
		}
		Deque<TreeNode> s = new LinkedList<>();
		s.push(head);
		long cur = Long.MIN_VALUE;
		
		while(!s.isEmpty()) {
			TreeNode node = s.peek();
			if(node.left != null) {
				s.push(node.left);
			}else {
				node = s.pop();
				System.out.print(node.val + " ");
				//判断node是否 > cur
				if(node.val <= cur) {
					return false;
				}
				cur = node.val;
				
				while(node.right == null && !s.isEmpty()) {
					node = s.pop();
					System.out.print(node.val + " ");
					
					//判断node是否 > cur
					if(node.val <= cur) {
						return false;
					}
					cur = node.val;
				}
				if(node.right != null) {
					s.push(node.right);
				}else {
					break;
				}
			}
		}
		
		return true;
	}
	
	/**
	 * 使用中序遍历 存到list中,再比较  【简单】
	 * 执行用时:2 ms, 在所有 Java 提交中击败了31.16% 的用户
内存消耗:38.2 MB, 在所有 Java 提交中击败了79.34% 的用户
	 */
	public boolean isBst03(TreeNode head, List<Integer> list) {
		for(int i = 1; i < list.size(); i++) {
			if(list.get(i) <= list.get(i - 1)) {
				return false;
			}
		}
		
		return true;
	}
	
	/**
	 * 注意:这个题目可以补全二叉树来做,得到 头结点上面的     min,max
	 * min < left < node.val < right < max
	 * 使用[先序遍历]
	 * 每次使node.val > left && node.val < right
	 * 同时left > min && right < max
	 * 
	 * 递归
	 */
	public boolean isBst03(TreeNode head, long max, long min) {
		if(head == null) {
			return true;
		}else {
			if(head.left != null) {//节点head 左边的值 < head.val ,  同时要 > min
				if(head.left.val >= head.val || head.left.val <= min) {
//					System.out.println("left:");
//					System.out.println(" head.val:" + head.val + " head.left.val:" + head.left.val + " min:" + min);					
					return false;
				}
			}
			if(head.right != null) { //节点head 右边的值 > head.val, 同时要 < max
				if(head.right.val <= head.val || head.right.val >= max) {
//					System.out.println("right;");
//					System.out.println(" head.val:" + head.val + " head.right.val:" + head.right.val + " max:" + max);
					return false;
				}
			}
			boolean b1 = this.isBst03(head.left, head.val, min);  //head.val
			min = head.val;
			boolean b2 = this.isBst03(head.right, max, min);
			
			return b1 && b2;
		}
		
	}
	/**调用上面
	 * 执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:38 MB, 在所有 Java 提交中击败了87.61% 的用户
	 * @param head
	 * @return
	 */
	public boolean isValidBST(TreeNode head) {
        if(head == null) {
			return true;
		}

        if(head.left != null && head.left.val >= head.val) {			
                return false;
            }
        if(head.right != null && head.right.val <= head.val) {
                return false;
        }

        //得到最小值
        long min = -1;
        TreeNode p = head;
        while(p.left != null){
            p = p.left;
        }
        min = p.val - 1L;  //注意是 -1L
//        System.out.println("min:" + (min + 1));
        return this.isBst03(head, Long.MAX_VALUE, min);
    }
	
	/**
	 * 上面的简化
	 * 执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:37.9 MB, 在所有 Java 提交中击败了94.21% 的用户
	 * @param head
	 * @return
	 */
	public boolean isValidBST02(TreeNode head) {
        return this.isBst03(head, Long.MAX_VALUE, Long.MIN_VALUE);
    }
	
	/**
	 * 使用参考 思想,类似上面的
	 * 使用先序遍历
	 * min < node.val < max
	 * 执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
		内存消耗:38.2 MB, 在所有 Java 提交中击败了77.43% 的用户
	 */
	public boolean isBst04(TreeNode head, long max, long min) {
		if(head == null) {
			return true;
		}else {
			if(head.val >= max || head.val <= min) {
				return false;
			}
			
			boolean b1 = this.isBst04(head.left, head.val, min);
			boolean b2 = this.isBst04(head.right, max, head.val);
			
			return b1 && b2;
			
		}
	}
	public boolean isValidBST03(TreeNode head) {
        return this.isBst04(head, Long.MAX_VALUE, Long.MIN_VALUE);
    }
	
	
	/**
	 * 初始化一个tree
	 * 类广度遍历
	 * @param a
	 * @return
	 */
	public TreeNode initTree(Integer[] a) {
		if(a == null || a.length == 0) {
			return null;
		}
		
		int t = 0;
		TreeNode p = new TreeNode(a[t]);  //至少有一个元素
		Queue<TreeNode> q = new LinkedList<>();
		q.offer(p);
		
		while(!q.isEmpty()) {
			TreeNode node = q.poll();
			if(t + 1 == a.length) {  //先判断数组中是否还有下一个元素
				return p;
			}else {
				t++;
				if(a[t] == null) {  //若下一个元素为null,则不需要创建新的节点
					node.left = null;
				}else {
					node.left = new TreeNode(a[t]);
					q.offer(node.left);
				}
			}
			if(t + 1 == a.length) {
				return p;
			}else {
				t++;
				if(a[t] != null){  //上面的简写,a[t] == null,不需要再赋值
					node.right = new TreeNode(a[t]);
					q.offer(node.right);
				}
			}
		}
		
		return p;
	}
	
	@Test
	public void test() {
//		Integer[] a = new Integer[] {1, 2, 3, 4, 5, 9, 10, null, 6, 7, 8, null, null, null, 11};
//		Integer[] a = new Integer[] {3, 9, 20, 16, 17, 15, 7, null, null, null, 18, null, null, null, null, 19, null};
//		Integer[] a = new Integer[] {6, 2, 8, 0, 4, 7, 9, null, null,3, 5};
		Integer[] a = new Integer[] {1, 1};
//		Integer[] a = new Integer[] {10,5,15,null,null,6,20};
//		Integer[] a = new Integer[] {3, 1, 5, 0, 2, 4, 6};
//		Integer[] a = new Integer[] {2147483647,-2147483648};
		
		TreeNode head = this.initTree(a);
		
		System.out.println("输出中序遍历:");
		List<Integer> list = new ArrayList<>();
		this.inOrder(head, list);
		System.out.println();
		
		System.out.println("使用递归实现");
		System.out.println(this.isBst(head, new Cur(Long.MIN_VALUE)));
		
		System.out.println("使用迭代实现,中序遍历非递归:");
		System.out.println(this.isBst02(head));
		
		System.out.println("使用中序遍历,将值存到list中");
		System.out.println(this.isBst03(head, list));
		
		System.out.println("递归: min < left < node.val < right < max:");
		System.out.println(this.isValidBST(head));
		
		System.out.println("递归:min < node.val < max:");
		System.out.println(this.isValidBST03(head));
		
		System.out.println("递归:参考:");
		System.out.println(this.isBst011(head));
		
		
	}
	/**
	 * 中序遍历
	 */
	public void inOrder(TreeNode head, List<Integer> list) {
		if(head == null) {
			return ;
		}else {
			this.inOrder(head.left, list);
			System.out.print(head.val + " ");
			list.add(head.val);
			this.inOrder(head.right, list);
		}
	}
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值