在二叉搜索树中查找有效序列

Find valid sequences in Binary search trees

假设1到1000之间的整数按二叉查找树排列,希望找到363这个数,下面的一些序列,哪个不是遍历的节点序列?

a) 2, 252, 401, 398, 330, 344, 397, 363 ;

b) 924, 220, 911, 244, 898, 258, 362, 363 ;

c) 925, 202, 911, 240, 912, 245, 363 ;

d) 2, 399, 387, 219, 266, 382,​​ 381, 278, 363 ;

e) 935, 278, 347, 621, 299, 392, 358, 363.

有必要做图案吗?以最小形式写 属性 以检查。 谢谢。

转到此处:https://www.cs.usfca.edu/~galles/visualization/BST.html 在左上角 'insert' 旁边一次输入每个数字,然后单击 'insert'。当您输入数字时,它将构建一个二叉树。

对每个序列执行此操作并比较它们的外观。

这是经过"a)"的路线:

这是一个单链。这是通过 "c)":

的尝试路线

它不是从上到下的单一路径,它有一个 wrong-turn 分支。如果 363 在树上,你就不会走错路,你直接去。

在 c) 中,911 在 240 上向左拆分,在 912 上向右拆分。

在 e) 中,347 在 621 上向右拆分,在 299 上向左拆分。

不可能是去363途中遍历的序列,因为每个链表中的一个节点不是去363的途中

[截图自https://www.cs.usfca.edu/~galles/visualization/BST.html]

范围 [min,max] - 初始化为 [1,1000]

key - 要搜索的目标键

seq[1,N] - 二叉搜索树中的数字序列

想法是跟踪有效范围 [min,max]。最初,从 1 到 1000 的所有数字都在范围内。如果你遇到一个键为 2 的节点,你的目标是 363,你会右转。当您右转时,您将在此子树中遇到的任何键都应大于 2。因此您将最小范围更新为 2。现在您的范围是 [3,1000]。遇到252,范围就变成了[253,1000]。在 401,你左转所以子树中的所有键都必须小于 401。所以你将最大值设置为 400,它变成 [253,400]。

现在在序列中的任何一点,您都需要检查值是否在范围内。如果不是,则存在违规行为。如果密钥匹配,则它必须是序列中的最后一个数字。

这是伪代码

boolean validate(seq[1,N],key,range)
    for i from 1 to N
        if(seq[i] < range.min || seq[i] > range.max)
            return false
        if(key < seq[i])
            range.max := key-1
        else if(key > seq[i])
            range.min := key+1
        else
            return i=N
    return true

直觉:

如果 sequence[i] > sequence[i+1] ,路径已经在左子树中搜索过,序列中的下一个元素必须不超过 sequence[i]

否则,路径包含右子树的元素,序列中的下一个元素必须不少于sequence[i]

我们可以使用上述逻辑编写一个蛮力解决方案,其结果的时间复杂度为 O(n^2)

# checks if any elements in the sequence is greater than val
def any_element_greater(sequence, val):
  for e in sequence:
    if e > val:
      return True
  return False

# checks if any elements in the sequence is lesser than val
def any_element_lesser(sequence, val):
  for e in sequence:
    if e < val:
      return True
  return False

# checks if the sequence is valid
def is_valid_seq(sequence):
  if len(sequence) < 2:
    return True
  
  prev = sequence[0]
  for idx, val in enumerate(sequence):
    if prev > val:
            # checks if the rest of the sequence is valid based on direction
      if any_element_greater(sequence[idx:], prev):
        return False
    elif prev < val:
            # checks if the rest of the sequence is valid based on direction
      if any_element_lesser(sequence[idx:], prev): 
        return False
    prev = val

  return True

优化:

我们可以使用 maxmin 变量来继承有效范围 - memoization

def valid_sequence(sequence, i=0, m_min=1, m_max=1000):
  '''
  Checks if the Sequence is a correct path in BST
  Parameters:
    sequence: path to the data in the BST
    i: current data we are validating in the path
    m_min: data should be more than m_min
    m_max: data should be less than m_max
  Returns:
    Boolean: If the sequence is a valid path in BST
  '''
    if len(sequence) == 0:
        return True

  data = sequence[i]

  # check if data is in valid range
  if not (m_min <= data <= m_max):
    return False

  # Base case, return if we reached the end
  if i == len(sequence) - 1:
    return True

  '''
  Adjust min, max for the next data elements
  depends on the next element in the sequence
  '''
  next = sequence[i+1]
  if next > data:
    m_min = max(m_min, data)
  else:
    m_max = min(m_max, data)

  return valid_sequence(sequence, i+1, m_min, m_max)

测试:

options = {
    'a': [2, 252, 401, 398, 330, 344, 397, 363],
    'b': [924, 220, 911, 244, 898, 258, 362, 363],
    'c': [925, 202, 911, 240, 912, 245, 363],
    'd': [2, 399, 387, 219, 266, 382, 381, 278, 363],
    'e': [935, 278, 347, 621, 299, 292, 358, 363]
}

for option in options:
  print(f'{valid_sequence(options[option])} \t - {option}. {options[option]}')

结果:

True     - a. [2, 252, 401, 398, 330, 344, 397, 363]
True     - b. [924, 220, 911, 244, 898, 258, 362, 363]
False    - c. [925, 202, 911, 240, 912, 245, 363]
True     - d. [2, 399, 387, 219, 266, 382, 381, 278, 363]
False    - e. [935, 278, 347, 621, 299, 292, 358, 363]

推理:

在选项c中,912大于911,而我们在240处进入左子树。所以它是无效的

在选项e中,我们从347开始,但我们在序列中遇到了299和292。所以无效