注:本文的代码实现使用的是 JS(JavaScript),为前端中想使用JS练习算法和数据结构的小伙伴提供解题思路。
题目描述
给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回任意有效的结果 。
输入示例
示例1
输入:root = [4,2,7,1,3], val = 5
输出:[4,2,7,1,3,5]
示例2
输入:root = [40,20,60,10,30,50,70], val = 25
输出:[40,20,60,10,30,50,70,null,null,25]
示例3
输入:root = [4,2,7,1,3,null,null,null,null,null,null], val = 5
输出:[4,2,7,1,3,5]
提示:
- 给定的树上的节点数介于 0 和 10^4 之间
- 每个节点都有一个唯一整数值,取值范围从 0 到 10^8
- −108<=val<=108-10^8 <= val <= 10^8−108<=val<=108
- 新值和原始二叉搜索树中的任意节点值都不同
解题思路
二叉搜索树的性质,对任意子树的根节点,都有:左子树根节点的值小于当前根节点的值,右子树根节点的值大于当前根节点的值。代码中一些地方是可以简写的,比如if(root === null)
的效果等价于if(!null)
,只不过为了能够清晰地表达出思路,所以并未简化。
1. 递归版
其实对于二叉搜索树来说,新的节点可以插入到叶节点的下面,不必在中间插入。因此,只需要搜索到合适的叶节点(比较根节点与val
的大小,根小于val
,搜索右子树;大于,则搜索左子树),最后比较要插入的值和叶节点的值大小决定是成为该叶节点的左节点还是右节点,即:
- 如果该子树不为空,则问题转化成了将
val
插入到对应子树上。 - 否则,在此处新建一个以
val
为值的节点,并链接到其父节点root
上。
var insertIntoBST = function(root, val) {
// 这个 if 语句有两个作用
// 1. 初始时给定一棵空树,则创建节点并返回
// 2.遍历到符合要求的叶子节点位置,创建节点
if(root === null) return new TreeNode(val)
// 开始递归遍历
else {
// 注意,这里一定要将函数的返回值赋值给对应根节点的 left 或 right
if(root.val > val)
// 根节点的数值大于目标节点的数值,则向左子树中寻找合适的插入位置
root.left = insertIntoBST(root.left, val)
else
// 根节点的数值小于目标节点的数值,则向右子树中寻找合适的插入位置
root.right = insertIntoBST(root.right, val)
}
// 不论该根节点的左右子树是否发生变化,均需要返回该根节点
return root
};
2. 迭代版
我们也可以使用迭代的方式去找到适合插入的位置。
var insertIntoBST = function(root, val) {
// 创建需要插入的节点
insertNode = new TreeNode(val)
// 初始时给定一棵空树,则返回创建的节点
if(root === null) return insertNode
// 根指针的拷贝,若直接使用root进行搜索,则最后返回的那棵树的根节点就找不到了
let node = root
//迭代
while(node !== null){
// 目标值小于当前节点值,则向左子树中寻找合适的插入位置
if(val < node.val){
// 左子树为空,则插入该节点
if(node.left === null){
node.left = insertNode
// 不要忘记退出循环,节点都插完了,还遍历的啥劲
break
}
else
// 左子树不为空,接着遍历左子树
node = node.left
}
// 目标值大于当前节点值,则向右子树中寻找合适的插入位置
else{
// 右子树为空,则插入该节点
if(node.right === null){
node.right = insertNode
break
}
else
// 右子树不为空,接着遍历右子树
node = node.right
}
}
// 返回插入后的树
return root
}