查找给定整数序列的排列数,这些整数产生相同的二叉搜索树

时间:2009-11-09 15:07:35

标签: algorithm binary-search-tree

给定一个整数数组arr = [5, 6, 1]。当我们以相同的顺序构造具有此输入的BST时,我们将以“5”作为根,“6”作为右子,“1”作为左子。

现在,如果我们的输入更改为[5,1,6],我们的BST结构仍然是相同的。

给定一个整数数组,如何找到输入数组的不同排列数,导致与原始数组顺序上形成的BST相同的BST?

3 个答案:

答案 0 :(得分:12)

您的问题等同于计算给定BST的拓扑排序数量的问题。

例如,对于BST

  10
 /  \
5   20
 \7 | \
    15 30

可以像这样用手计算一组拓扑排序:每次排序10次。从20开始的子树的拓扑排序数为2:(20,15,30)和(20,30,15)。从5开始的子树只有一个排序:(5,7)。这两个序列可以以任意方式交织,导致2×10交错,从而产生20个输入,产生相同的BST。下面列举了前10个案例(20,15,30):

 10 5 7 20 15 30
 10 5 20 7 15 30
 10 5 20 15 7 30
 10 5 20 15 30 7
 10 20 5 7 15 30
 10 20 5 15 7 30
 10 20 5 15 30 7
 10 20 15 5 7 30
 10 20 15 5 30 7
 10 20 15 30 5 7

案例(20,30,15)类似 - 您可以检查以下任何一个输入是否产生相同的BST。

此示例还提供了一个递归规则来计算排序数。对于叶子,数字为1.对于具有一个子节点的非叶节点,该数字等于子节点的拓扑排序数。对于具有子树大小| L |的两个子节点的非叶节点和| R |,分别具有l和r顺序,数字等于

  l x r x INT(|L|, |R|)

其中INT是| L |的可能交错数和| R |元素。这可以通过(| L | + | R |)轻松计算出来! /(| L |!x | R |!)。对于上面的示例,我们得到以下递归计算:

  Ord(15) = 1
  Ord(30) = 1
  Ord(20) = 1 x 1 x INT(1, 1) = 2  ; INT(1, 1) = 2! / 1 = 2
  Ord(7) = 1
  Ord(5) = 1
  Ord(10) = 1 x 2 x INT(2, 3) = 2 x 5! / (2! x 3!) = 2 x 120 / 12 = 2 x 10 = 20

这解决了这个问题。

注意:此解决方案假定BST中的所有节点都有不同的密钥。

答案 1 :(得分:1)

感谢antti.huima的解释!这有助于我理解。这是一些C ++:

#include <vector>
#include <iostream>

using namespace std;

int factorial(int x) {
  return (x <= 1) ? 1 : x * factorial(x - 1);
}

int f(int a, int b) {
  return factorial(a + b) / (factorial(a) * factorial(b));
}

template <typename T>
int n(vector<T>& P) {
  if (P.size() <= 1) return 1;
  vector<T> L, R;
  for (int i = 1; i < P.size(); i++) {
    if (P[i] < P[0])
      L.push_back(P[i]);
    else
      R.push_back(P[i]);
  }
  return n(L) * n(R) * f(L.size(), R.size());
}

int main(int argc, char *argv[]) {
  vector<int> a = { 10, 5, 7, 20, 15, 30 };
  cout << n(a) << endl;
  return 0;
}

答案 2 :(得分:-1)

您可以向后执行此操作:给定BST,枚举所有可能产生此BST的整数数组...

你不能(使用非确定性......)

  1. 发出root并将其添加到发出的集合中。
  2. 不确定地从树中选择一个不在发射集中的项目, 但是其父级是,并将其添加到已发射的集合中并将其发出。
  3. 重复2直到全部发射。
  4. 非确定性会给你所有这样的数组。然后你可以数数。