web-dev-qa-db-ja.com

二分木が二分探索木かどうかを調べる

今日、私はインタビューを行いました。そこでは、バイナリツリーを取得し、それがバイナリ検索ツリーでもある場合はtrueを返すプログラムを作成するように依頼されました。

私のアプローチ1:順序走査を実行し、要素をO(n)時間で保存します。今度は要素の配列/リストをスキャンし、iの要素をチェックします番目 インデックスが(i + 1)の要素より大きい番目 インデックス。そのような状態が発生した場合は、falseを返してループを抜けます。 (これにはO(n)時間かかります)。最後にtrueを返します。

しかし、この紳士は私に効率的な解決策を提供することを望んでいました。試しましたが、BSTであるかどうかを確認するには各ノードをチェックする必要があるため、失敗しました。

さらに、彼は再帰について考えるように私を指していた。私のアプローチ2:任意のノードN N-> leftが<NおよびN-> right> Nであり、Nの左ノードの順序後継がNより小さく、順序後継がある場合、BTはBSTですNの右ノードのNはNより大きく、左および右のサブツリーはBSTです。

しかし、これは複雑になり、実行時間は良くないようです。最適なソリューションを知っている場合は助けてください。

33
dharam

これは、次の答えを持つかなりよく知られた問題です。

public boolean isValid(Node root) {
     return isValidBST(root, Integer.MIN_VALUE,
          Integer.MAX_VALUE);
}
private boolean isValidBST(Node node, int l, int h) {
     if(node == null)
         return true;
     return node.value > l 
         && node.value < h
         && isValidBST(node.left, l, node.value)
         && isValidBST(node.right, node.value, h);
}

再帰呼び出しは、サブツリーノードがその祖先の範囲内にあることを確認します。これは重要です。すべてのノードが一度検査されるため、実行時間の複雑さはO(n)になります。

もう1つの解決策は、特にバイナリツリーが入力として提供されていることを既に知っているため、順序のトラバーサルを行い、シーケンスがソートされているかどうかを確認することです。

78
Dhruv Gairola

@Dhruvが提供する答えは良いものです。これに加えて、O(n) time。
このアプローチでは、前のノードを追跡する必要があります。各再帰呼び出しでは、現在のノードデータで以前のノードデータをチェックします。現在のノードデータが前のものよりも少ない場合、falseを返します

int isBST(node* root) {
  static node* prev  = NULL;

  if(root==NULL)
    return 1;

  if(!isBST(root->left))
    return 0;

  if(prev!=NULL && root->data<=prev->data)
    return 0;

  prev = root;
  return isBST(root->right);
}
7
AgentX
boolean b = new Sample().isBinarySearchTree(n1, Integer.MIN_VALUE, Integer.MAX_VALUE);
.......
.......
.......
public boolean isBinarySearchTree(TreeNode node, int min, int max){
  if(node == null){
    return true;
  }

  boolean left  = isBinarySearchTree(node.getLeft(), min, node.getValue());
  boolean right = isBinarySearchTree(node.getRight(), node.getValue(), max);

  return left && right && (node.getValue()<max) && (node.getValue()>=min);      
}

コメントを募集しています。ありがとう。

1
Trying

2番目のアプローチは正しいと思います。ツリーは再帰的に横断できます。各反復で、現在のサブツリーの下限と上限を保存できます。ルートxのサブツリーをチェックし、サブツリーの境界がlとhである場合、必要なのはl <= x <= hであり、左のサブツリーを境界lとxで、右をチェックすることだけです。境界xとhを持つもの。

これはO(n)複雑さを持ちます。これは、ルートから開始し、各ノードがサブツリーのルートとして一度だけチェックされるためです。また、O(h)再帰呼び出しのメモリ。hはツリーの高さです。

0
fdermishin