从无序数组构造的两个二叉搜索树的相等性

时间:2012-12-30 17:09:50

标签: algorithm time-complexity binary-search-tree

给定两个大小为N的未排序数组,我们将确定从它们构造的二进制搜索树是否相等。

因此,数组的元素被选中并插入到基本(无平衡,无红黑,无任何)二叉搜索树中。 直接给出两个数组,我们可以确定它们是否会产生相同的二进制搜索树。

有一个明显的O(N 2 )最坏情况时间复杂度解决方案:构造两棵树,并比较它们的相等性。

是否有O(N)或O(N log N)解决方案?

我想要提取的问题的想法是:BST的构造取决于元素的相对位置。例如,如果在一个数组中有一个紧邻20的值为51的元素,则在另一个数组中必须没有20到51之间的元素才能使树相等(否则20的右子将是该数字,而不是51 )。

我确实尝试了几种方法:

  1. 天真的方法:构建2棵树并进行比较。
  2. 我使用了一个有趣的变体,我将数组分成2个(一个较小的子数组和一个比数组大的子数组),并递归地将左数组传递给左子数,另一个传递给对。就地和厚脸皮,但仍然是O(N 2 )。
  3. 一位朋友尝试将最长的子序列应用于它并找到它然后进行比较,但这是不正确的。
  4. 我正在试图用LinkedHashMap解决它,但我很难证明它的正确性。
  5. 帮助,非常感谢解决此问题的任何提示。

4 个答案:

答案 0 :(得分:1)

摘要

我认为你可以通过使用range minimum query构建二叉树来改进从O(N ^ 2)到O(NlogN)的天真方法。

快速二叉树构造

假设我们要为数组A构建二叉树。

这个想法是首先构造一个数组B,其中B [i]是A中第i个最大元素的位置。这可以通过在O(NlogN)中排序来完成。

然后,我们可以在阵列B上使用范围最小查询,以允许我们找到给定范围a< = i< = b的B [i]的最小值。换句话说,这让我们找到A中的第一个位置,其中我们在第一个和第二个最大元素之间的范围内有一个值。

RMQ需要时间O(N)进行预处理,然后可以及时回答查询O(1)。

然后我们可以递归地找到每个元素的左右子元素(如果有的话)并检查它们是否匹配。

伪代码

假设两个数组是A和A2,为简单起见,我们假设A,A2已被预处理,使得第i个最大元素等于i。

如果find_children(1,N)为True,则树是相同的:

find_children(low,high)
   if high==low
      return True
   node = A[RMQ(low,high)]
   return node == A2[RMQ2(low,high)]
          and find_children(low,node-1)
          and find_children(node+1,high)

对于树中的每个节点(和空子指针)调用此函数一次,因此需要时间O(N)。

总的来说,这是O(NlogN),因为预处理排序需要O(NlogN)。

说明

假设我们已将元素20和51输入到二叉树中。然后我们将有20个是根,51个是正确的孩子。要找到51的左子元素,我们需要找到数组中第一个元素,其值大于20且小于51.该值由我们应用于范围20 + 1-> 51的范围最小查询给出。 -1

因此,我们可以更快地找到所有节点的左右子节点,而不是以自然的方式将它们插入到二叉树中(在理论上最坏的情况下只会更快 - 对于典型示例,其他方法可能更快)。 / p>

答案 1 :(得分:1)

“构造两棵树并进行比较”不一定是O(N ^ 2)。你可以使用辅助数据结构,让你在O(log N)而不是O(N)中找到新节点的位置,这样即使正在构造BST,BST的构造也是O(N log N)是不平衡的。

对于BST中的每个空位置(即节点中的空闲子槽)pos,存在关联的间隔(a_pos,b_pos)(其中一个值可能是+/-无穷大),例如当且仅当值在区间内时,才会在v创建值pos的新节点。

您可以将间隔存储在平衡间隔树中,以便可以在O(log N)中找到每个新到达值的位置。间隔树的更新也是O(log N),因为您只用两个间隔替换一个间隔。

(实际上,间隔从不重叠,因此辅助结构可以是普通的(平衡的)BST而不是间隔树。)

示例:

采用以下非平衡BST,为数组前缀[1,10,2,9,3,...]构建

  1
 /  \
a  10
   / \
  2   f
 / \
b   9
   / \
  3   e
 / \
c   d

字母a-f表示可以放置新节点的可能位置(零叶)。每个字母都有一个相关的间隔,如下所示:

[-inf,1] -> a
[1,2] -> b
[2,3] -> c
[3,9] -> d
[9,10] -> e
[10, +inf] -> f

v的新节点将添加到由v所属的时间间隔确定的位置的BST中。 Zero将以a结束,5 d结束,依此类推。关键的想法是将这些信息存储在树外。

如果您可以有效地表示上表(包含指向实际树节点的链接),则向树添加新节点将采用O(访问表)+ O(1)。 O(1)表示将节点添加到非平衡BST中,因为您已经知道放置它的位置。添加5不需要与1,10,2,9和3进行比较,而是在表格中查找并直接放在d

放置新节点后,显然还需要更新表。表示表的数据结构可以是间隔树(http://en.wikipedia.org/wiki/Interval_tree)。

答案 2 :(得分:0)

试试这个:

int identical(struct node* a, struct node* b) 
{
    if (a==NULL && b==NULL)
    {
        return(true);
    } 
    else if (a!=NULL && b!=NULL)
    {
        return(a-> data == b-> data && identical(a->left, b->left) && identical(a->right, b->right));
    } 
    else 
        return(false);
}

答案 3 :(得分:0)

我提出了以下代码。虽然分区效率低下,但它工作正常。

    bool isBST (vector<int> vec1, vector<int> vec2) {
    if (vec1.size() == 0 && vec2.size() == 0)
        return true;
    if (vec1.size() != vec2.size())
        return false;
    if (vec1[0] != vec2[0])
        return false;

    vector<int> temp1;
    vector<int> temp2;
    vector<int> temp3;
    vector<int> temp4;
    for (int k = 1; k < vec1.size(); k++) {
       if(vec1[k] < vec1[0])
           temp1.push_back(vec1[k]);
       else
           temp2.push_back(vec1[k]);

       if(vec2[k] < vec2[0])
           temp3.push_back(vec2[k]);
       else
           temp4.push_back(vec2[k]);
    }

    return isBST(temp1, temp3) && isBST(temp2, temp4);

}