二叉搜索树的递归定义:
- 是一棵空树
- 是一棵由根节点、左子树、右子树组成的树,同时左子树和右子树都是二叉搜索树,且左子树上所有节点的数据域都小于等于根节点的数据域,右子树上所有节点的数据域都大于等于根节点的数据域
满足以上两个条件之一的二叉树,就是二叉搜索树
从这个定义我们可以看出,二叉搜索树强调的是数据域的有序性。也就是说,二叉搜索树上的每一棵子树,都应该满足 左孩子 <= 根结点 <= 右孩子 这样的大小关系。
查找数据域为某一特定值的节点
假设这个目标节点点的数据域值为 n,我们借助二叉搜索树数据域的有序性,可以有以下查找思路:
- 递归遍历二叉树,若当前遍历到的结点为空,就意味着没找到目标结点,直接返回。
- 若当前遍历到的结点对应的数据域值刚好等于
n,则查找成功,返回。 - 若当前遍历到的结点对应的数据域值大于目标值
n,则应该在左子树里进一步查找,设置下一步的遍历范围为root.left后,继续递归。 - 若当前遍历到的结点对应的数据域值小于目标值
n,则应该在右子树里进一步查找,设置下一步的遍历范围为root.right后,继续递归。function search(root, n) {// 若 root 为空,查找失败,直接返回if(!root) {return}// 找到目标结点,输出结点对象if(root.val === n) {console.log('目标结点是:', root)} else if(root.val > n) {// 当前结点数据域大于n,向左查找search(root.left, n)} else {// 当前结点数据域小于n,向右查找search(root.right, n)}}
插入新节点
function insert(root, n) {// 若 root 为空,说明当前是一个可以插入的空位if(!root) {// 用一个值为n的结点占据这个空位root = new TreeNode(n)return}// 查找成功,说明值为n的结点已经存在,不再重复创建,直接返回if(root.val === n) {return} else if(root.val > n) {// 当前结点数据域大于n,向左查找insert(root.left, n)} else {// 当前结点数据域小于n,向右查找insert(root.right, n)}}
删除指定节点
function delete(root, n) {// 如果没找到目标结点,则直接返回if(!root) {return}// 定位到目标结点,开始分情况处理删除动作if(root.val === n) {// 若是叶子结点,则不需要想太多,直接删除if(!root.left && !root.right) {root = null} else if(root.left) {// 寻找左子树里值最大的结点const maxLeft = findMax(root.left)// 用这个 maxLeft 覆盖掉需要删除的当前结点root.val = maxLeft.val// 覆盖动作会消耗掉原有的 maxLeft 结点delete(root.left, maxLeft.val)} else {// 寻找右子树里值最小的结点const minRight = findMin(root.right)// 用这个 minRight 覆盖掉需要删除的当前结点root.val = minRight.val// 覆盖动作会消耗掉原有的 minRight 结点delete(root.right, minRight.val)}} else if(root.val > n) {// 若当前结点的值比 n 大,则在左子树中继续寻找目标结点delete(root.left, n)} else {// 若当前结点的值比 n 小,则在右子树中继续寻找目标结点delete(root.right, n)}}// 寻找左子树最大值function findMax(root) {while(root.right) {root = root.right}return root}// 寻找右子树的最小值function findMin(root) {while(root.left) {root = root.left}return root}
二叉搜索树的验证
题目描述:给定一个二叉树,判断其是否是一个有效的二叉搜索树。 假设一个二叉搜索树具有如下特征: 节点的左子树只包含小于当前节点的数。 节点的右子树只包含大于当前节点的数。 所有左子树和右子树自身必须也是二叉搜索树。
示例 1: 输入:
2/ \1 3
输出: true
示例 2: 输入:
5/ \1 4/ \3 6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。 根节点的值为 5 ,但是其右子节点值为 4 。
/*** @param {TreeNode} root* @return {boolean}*/const isValidBST = function(root) {// 定义递归函数function dfs(root, minValue, maxValue) {// 若是空树,则合法if(!root) {return true}// 若右孩子不大于根结点值,或者左孩子不小于根结点值,则不合法if(root.val <= minValue || root.val >= maxValue) return false// 左右子树必须都符合二叉搜索树的数据域大小关系return dfs(root.left, minValue,root.val) && dfs(root.right, root.val, maxValue)}// 初始化最小值和最大值为极小或极大return dfs(root, -Infinity, Infinity)};
将排序数组转化为二叉搜索树
题目描述:将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例: 给定有序数组: [-10,-3,0,5,9], 一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
0/ \-3 9/ /-10 5
/*** @param {number[]} nums* @return {TreeNode}*/const sortedArrayToBST = function(nums) {// 处理边界条件if(!nums.length) {return null}// root 结点是递归“提”起数组的结果const root = buildBST(0, nums.length-1)// 定义二叉树构建函数,入参是子序列的索引范围function buildBST(low, high) {// 当 low > high 时,意味着当前范围的数字已经被递归处理完全了if(low > high) {return null}// 二分一下,取出当前子序列的中间元素const mid = Math.floor(low + (high - low)/2)// 将中间元素的值作为当前子树的根结点值const cur = new TreeNode(nums[mid])// 递归构建左子树,范围二分为[low,mid)cur.left = buildBST(low,mid-1)// 递归构建左子树,范围二分为为(mid,high]cur.right = buildBST(mid+1, high)// 返回当前结点return cur}// 返回根结点return root};
