二叉搜索树的插入

往一个二叉搜索树中插入一个节点后的结果并不唯一,例如对于下面的二叉搜索树:

alt

如果要插入节点2,可以将2作为3的左子节点:

alt

或者将2作为1的右子节点:

alt

对于第一种方法,类似于往单链表的中间插入节点,既要更新前继节点的 next 指针,又要将新的节点的 next 指针指向下一个节点;而对于第二种方法,只需要将新节点挂载到目标节点的左子节点或右子节点即可,实现上较为简洁,可分为非递归和递归两种解法。

非递归

整体算法分为两步:

  1. 找到要挂载的叶子节点
  2. 将新节点挂载到该叶子节点的左子节点或右子节点上

第一步等同于二叉搜索树的查找,从根节点开始,将目标值和当前节点的值进行比较,如果当前节点的值比目标值小,说明要找的节点在右子树中,移动到右子节点中查找;如果当前节点的值比目标值大,说明要找的节点在左子树中,移动到左子节点中查找。

找到目标叶子节点后,比较该叶子节点的值和目标值的大小,来决定新节点是作为左子节点还是右子节点插入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right


class Solution:
def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
if not root:
return TreeNode(val)

prev, current = None, root

while current:
prev = current

if current.val < val:
current = current.right
else:
current = current.left

if prev.val > val:
prev.left = TreeNode(val)
else:
prev.right = TreeNode(val)

return root

递归

一般的二叉树问题的递归解法遵循如下的模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def dfs(root):
if not root:
# 处理终止条件

# 一种情况是左右子树只处理一边
if some condition:
dfs(root.left)
else:
dfs(root.right)

# 另一种情况是左右子树都处理
dfs(root.left)
dfs(root.right)

return something

在当前的问题下,终止条件发生的条件为找到了目标叶子节点,此时需要新建一个节点;而对于递归的处理,这里适用于第一种情况,即左右子树只处理一边,判断条件为比较当前节点的值和目标值的大小,所以可以粗略的构造出程序的框架:

1
2
3
4
5
6
7
8
9
10
def dfs(root, val):
if not root:
create new node with val

if root.val > val:
dfs(root.left)
else:
dfs(root.right)

return something

下一个问题是,这个递归函数的返回值是什么?从终止条件的处理可以看到递归函数返回的是某个节点,联想到往一个二叉搜索树中插入一个节点后需要返回一个新的树,所以这里递归函数的返回值应该是根节点。

然而还缺少一步,就是新节点的挂载,目前新节点返回后并没有任何节点引用它,需要在终止条件的上层调用中处理,即每次递归调用时都重新赋值左子树或右子树的根节点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right


class Solution:
def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
if not root:
return TreeNode(val)

if root.val < val:
root.right = self.insertIntoBST(root.right, val)
else:
root.left = self.insertIntoBST(root.left, val)

return root