检查给定的前序遍历是否有效BST

时间:2014-10-03 05:41:10

标签: algorithm

如果它是有效的BST,我正在编写代码来检查preorder遍历。 例如,前序遍历1,2,4,3,5是有效的BST而1,3,4,2不是

我从该预订序列中构建了整个树,然后检查该树是否是有效的BST。这是空间和时间复杂性的O(N)解决方案。

有人有比这更好的方法吗?我有直觉,我们可以在O(1)额外的空间中做到这一点。

9 个答案:

答案 0 :(得分:12)

检查1..n的排列是否是有效BST的预先排序(BST,如果它存在,是唯一的; imreal的答案不是反例,因为第二次重建没有排序)相当于测试是否排列是堆栈可排序的,即避免231模式。我似乎无法在任何这些名称下找到任何线性时间常数空间算法,这将倾向于表明这个问题引起的注意力,没有人知道恒定的额外空间解决方案。

如果您愿意假设可以销毁的读/写输入,那么就会有一个使用恒定额外空间的线性时间算法。没有必要单独重建树然后测试它。这是一些Python的效果。

def isbstpreorder(iterable):
    lowerbound = None
    stack = []
    for x in iterable:
        if lowerbound is not None and x < lowerbound: return False
        while stack and stack[-1] < x: lowerbound = stack.pop()
        stack.append(x)
    return True

要消除堆栈的单独存储,请将其存储在输入列表的前缀中。

答案 1 :(得分:1)

想法是检查输入数组是否可以分成表示左子树和右子树的两个子数组。显然,这必须以递归方式完成。

这里有一些经过测试的Java代码来说明相同的内容:

import java.util.ArrayList;
import java.util.List;


public class PreOrderBSTCheck {

    /**
     * @param args
     */
    public static void main(String[] args) {

        String ipStr = "1 4 2 3";
        String[] ipArr = ipStr.split(" ");

        int[] intArr = new int[ipArr.length];
        List<Integer> listArr = new ArrayList<Integer>();

        for(int i = 0 ; i < ipArr.length ; i++)
        {
            intArr[i] = Integer.parseInt(ipArr[i]);
            listArr.add(intArr[i]);
        }
        //System.out.println("List size: "+listArr.size());
        if(checkTree(listArr))
            System.out.println("Valid");
        else
            System.out.println("Invalid");
    }

    public static boolean checkTree(List<Integer> arr)
    {
        if(arr.size() == 1)
        {
            return true;
        }

        List<Integer> left = new ArrayList<Integer>();
        List<Integer> right = new ArrayList<Integer>();

        Integer root = arr.get(0);
        int idx = 1;

        //adding left subtree nodes
        while(arr.get(idx) < root)
        {
            left.add(arr.get(idx++));

            if(idx >= arr.size())
                break;
        }

        //adding right subtree nodes
        if(! (idx >= arr.size()))
            while(arr.get(idx) > root)
            {
                right.add(arr.get(idx++));

                if(idx >= arr.size())
                    break;
            }

        if(left.size() + right.size() == arr.size() - 1)
        {
            if(left.size()==0)
            {
                return true && checkTree(right);
            }
            else if(right.size() == 0)
            {
                return true && checkTree(left);
            }
            else
            {
                return checkTree(left) && checkTree(right);
            }
        }
        else
        {
            return false;
        }

    }

}

答案 2 :(得分:1)

isBST(vector<int> &A) {
    int a=0,b=1,c=2;

    if(A.size() <= 2)
    return 1;

    while(c < A.size())
    {
        if(A[a] < A[b] && A[b] > A[c] && A[c] < A[a])
            return 0;
        a++; b++; c++;
    }
    return 1;
}


    8
  7   9   output preorder 8 7 9

    7
     8
      9   output preorder 7 8 9
    
    7
      9
     8    output preorder 7 9 8

    9
  7
   8      output preorder 9 7 8

    9
   8
  7       out put preorder 9 8 7

BUT FOR 897无法构建BST

答案 3 :(得分:0)

int lower=-1; 
for(long int i=0;i<size;i++)
        {
            if(lower>-1 && pre[i] < lower)
            {
                lower=-2;
                break;
            }
            while(!isempty(s) && top(s)<pre[i])
            {
                lower =top(s);
                pop(s);
            }
            push(s,pre[i]);
        }
        if(lower==-2)
        {
            cout<<"Invalid"<<endl;
        }
        else
        {
           cout<<"Valid BST"<<endl;
        }

此算法适用于任何类型的排序输入,而不仅仅是1到n。它需要O(1)额外的空间,即只有变量'lower'。如果您尝试使用笔和纸来解决此问题,则必须跟踪变量,您要检查的变量不应该小于此值。 Lower用作标记,您可以检查没有新值低于“lower”。一旦获得更高的价值,您就会不断更新“更低”。

答案 4 :(得分:0)

这可以在O(n)时间和O(n)辅助空间复杂度(对于递归堆栈)中完成。整个树的根节点将在预订序列中的索引0处。在预先排序中,每个节点后跟一组左子树元素,然后是右子树元素集。对于每个节点,右子树将以该节点的下一个更高的数组值为根。因此,基本思想是使用有效的节点值范围递归地将预序列数组划分为左右子树。

public class IsBstPossibleFromPreorder {
    public static void main(String args[]){
        int[] preorder = {40, 30, 35, 20, 80};//{6,3,0,5,4,8,7,9};//{3,2,5,1,7};
        System.out.println(isBstPossible(preorder, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, preorder.length-1));
    }

    /*
    li is root of current subtree
    ri is the index of the last element in the current subtree
    min, max range for the elements of current subtree
     */
    private static boolean isBstPossible(int[] preorder, int min, int max, int li, int ri){
        if (li==ri && preorder[li]>min && preorder[li]<max){ // if single node subtree...
            return true;
        }
        if (preorder[li]<=min || preorder[li]>=max ){ // if node value out of range
            return false;
        }
        int lsi = preorder[li+1]<preorder[li]?li+1:-1; // left subtree root index (-1 if no left subtree exits for node li)
        int rsi = findNextHigherElementIndex(preorder, li, ri); // right subtree root index (-1 if no right subtree exists for node li)

        boolean isLeftValid = (lsi==-1 || isBstPossible(preorder, min, preorder[li], lsi, rsi-1>lsi?rsi-1:lsi));
        boolean isRightValid =  (rsi==-1 || isBstPossible(preorder, preorder[li], max, rsi, ri));
        return isLeftValid && isRightValid;
    }

    private static int findNextHigherElementIndex(int[] preorder, int li, int ri){
        int x = -1; // -1 if no higher element on right side of li
        for (int i = li+1; i <= ri ; i++) {
            if (preorder[i]>preorder[li]){
                x = i;
                break;
            }
        }
        return x;
    }
}

答案 5 :(得分:0)

我们在这里可以考虑的第一件事是,对于preorder遍历数组,如果在数组右侧的任何一点我们找到比当前元素更大的元素,并且如果之后的任何元素是更小的元素,我们应该考虑BST是不可能的从那个非常预先的顺序遍历。

例如:arr [] = {40,30,35,20,80,100}无法建立有效的BST。

说明:

当我们开始构建树时,40成为BST的根, 现在我们继续30进入左子树,现在我们发现下一个更大的30是35,因为我们知道任何更低或更小的元素在30的左子树中应该必须在30和下一个更大之间,即35这里。因此,当我们向前移动时,我们发现20不能适合30的左边,因为它是在下一个更大的元素之后,当然不是35的子,因为它将违反BST属性(即任何正确的子树元素必须具有总是与root相比更大的价值)。因此,无法制定有效的BST。

现在,为了解决这个问题,我们可以使用堆栈来查找数组Code for next greater element中的下一个更大的元素。在这里我们必须确保在具有下一个更大的元素之后,没有比之后更小的元素。

在代码中我们最初以INT_MIN的最低可能值为根, 如果root随时低于当前元素,则返回false。 当前元素大于堆栈顶部的元素时,我们弹出它并将root设置为弹出元素。 然后我们将当前元素推入堆栈以进行进一步比较。

bool check(int *arr, int n)
{
stack<int> s;
int root=INT_MIN;
for(int i=0;i<n;++i)
{
    if(arr[i]<root)
        return false;

    while(!s.empty() && arr[i]>s.top())
    {
        root=s.top();
        s.pop();
    }

    s.push(arr[i]);
}
return true;
}

答案 6 :(得分:0)

public static bool IsBST(List<int> array)
    {       
        var s = new Stack<int>();
        int root = 0;
        
        foreach(var el in array)
        {
            if(el < root)
                return false;
            
            while(s.Count != 0 && el > s.Peek())
            {
                root = s.Peek();
                s.Pop();
            }
            
            s.Push(el);
        }
        
        return true;
    }

答案 7 :(得分:0)

不使用多余的空间或容器,例如stack

将输入数组划分为[root] - [left-subtree] - [right-subtree]形式,然后递归验证每个子树。

public boolean isValidPreorder(int[] A) {
    return util(A, 0, A.length-1);
}
public boolean isValidPreorderUtil(int[] A, int x, int y){
    if (x >= y) return true;
    for (int i = x+1; i <= y; i++){
        if (A[x] < A[i]) {
            for (int k = i+1; k <=y; k++){
                if (A[k] < A[x]) return 0;
            }
            return util(A, x+1, i-1) & util(A, i, y); // both left and right subtree
        }    
    }
    return util(A, x+1, y); // left subtree only
}

    

答案 8 :(得分:-1)

单独从前序遍历唯一地构造二叉树是不可能的,因此无法验证它是否是BST。

例如,4,2,3,6,5可以:

    4
  /   \
 2     6
  \   /
  3   5

这是一个BST,还有:

4
 \
  2
   \
    3
     \
      6
       \
        5

不是。