1-树-验证二叉搜索树

终于开启了树的篇章,这是我数据库领域经常用到的算法,这是第一篇,力扣链接。

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左子树只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

示例 1:

1-树-验证二叉搜索树_第1张图片

输入:root = [2,1,3]
输出:true

这道题很有意思,我一开始的提交方法是:

func isValidBST(root *TreeNode) bool {
	if root == nil || (root.Left == nil && root.Right == nil) {
		return true
	}
	if root.Right == nil {
		if root.Left.Val < root.Val {
			return isValidBST(root.Left)
		}
	} else if root.Left == nil {
		if root.Right.Val < root.Val {
			return isValidBST(root.Right)
		}
	} else if root.Left.Val < root.Val && root.Right.Val > root.Val {
		return isValidBST(root.Left) && isValidBST(root.Right)
	}
	return false
}

我认为,只有右子叶大于根节点,左子叶小于根节点就行,其实不是。这里的要求二叉树是左树都得比根节点小,右树都得比根节点大。

所以这里的递归可能不是和当前节点比左右叶子结点,而是根最大最小的边界比根节点。

func isValidBST(root *TreeNode) bool {
	return helper(root, math.MinInt, math.MaxInt)
}

func helper(root *TreeNode, lower int, upper int) bool {
	if root == nil {
		return true
	}
	if root.Val <= lower || root.Val >= upper {
		return false
	}
	return helper(root.Left, lower, root.Val) && helper(root.Right, root.Val, upper)
}

当然,递归可以做的迭代也能搞。我们把树封装进数组,进行中序遍历,大概逻辑就是将左节点放入栈中,一个一个取出和父节点与右节点比较大小,右节点一定要比根节点大。

func isValidBST(root *TreeNode) bool {
	stack := make([]*TreeNode, 0)
	rootVal := math.MinInt
	for len(stack) > 0 || root != nil {
		for root != nil {
			stack = append(stack, root)
			root = root.Left
		}
		root = stack[len(stack)-1]
		stack = stack[:len(stack)-1]
		if root.Val <= rootVal {
			return false
		}
		rootVal = root.Val
		root = root.Right
	}
	return true
}

抛开这道题,我们讲一下遍历二叉树的三种方式吧:

在计算机科学中,对一棵树(特别是二叉树)的遍历是指按照某种顺序访问树中的每个节点,确保每个节点都被访问一次的过程。对于二叉树,最常见的遍历方式有三种:中序遍历、前序遍历和后序遍历。这些遍历方式可以递归或迭代地实现。

中序遍历 (Inorder Traversal)

在中序遍历中,我们按照以下顺序遍历树中的节点:

  1. 遍历左子树
  2. 访问根节点
  3. 遍历右子树

对于二叉搜索树(BST),中序遍历会按照升序访问所有节点,因为二叉搜索树的特点是左子节点的值小于根节点的值,根节点的值小于右子节点的值。

前序遍历 (Preorder Traversal)

在前序遍历中,节点的访问顺序如下:

  1. 访问根节点
  2. 遍历左子树
  3. 遍历右子树

前序遍历通常用于创建树的副本。当你访问节点之后立即复制节点,你可以通过前序遍历复制所有节点并创建一棵相同的树。

后序遍历 (Postorder Traversal)

后序遍历的顺序是:

  1. 遍历左子树
  2. 遍历右子树
  3. 访问根节点

后序遍历常用于删除或释放树中的节点。因为你在删除节点之前先访问其子节点,这样可以安全地删除每个节点。

例子

假设有一棵二叉树如下:

    A
   / \
  B   C
 / \   \
D   E   F

对这棵树进行不同的遍历会得到以下结果:

  • 中序遍历D, B, E, A, C, F。首先遍历左子树(D, B, E),然后是根节点(A),最后是右子树(C, F)。
  • 前序遍历A, B, D, E, C, F。首先访问根节点(A),然后是左子树(B, D, E),最后是右子树(C, F)。
  • 后序遍历D, E, B, F, C, A。首先是左子树(D, E, B),然后是右子树(F, C),最后是根节点(A)。

遍历可以通过递归或使用栈的迭代方法来实现。递归是最直观的方法,但在某些情况下(如树非常深时),迭代方法可能更优,因为它可以避免递归导致的栈溢出问题。

这里就不过多的展开,后续一定会遇到相应的题目。

你可能感兴趣的:(白话算法,算法,数据结构)