剣はオファー 26 (ツリー 2) を指します。ツリーの下部構造

剣はオファー 26 (ツリー 2) を指します。ツリーの下部構造

問題の説明:

2つの二分木AとBを入力し、BがAの部分構造であるかどうかを判定します。(空のツリーはどのツリーの部分構造でもないことを約束します)

B は A の部分構造です。つまり、A は B と同じ構造とノード値を持ちます。

例如:
给定的树 A:

     3
    / \
   4   5
  / \
 1   2
给定的树 B:

   4 
  /
 1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。

输入:A = [1,2,3], B = [3,1]
输出:false
输入:A = [3,4,5,1,2], B = [4,1]
输出:true

問題解決のアイデア:アイデアリンク

反復: 一般的なアイデア:

  1. 最初にツリー A をたどって、ノード B と同じ値を持つノードにたどる場合は、ヘルパー メソッドを入力して、次のノードが同じかどうかを判断します。
  2. ノードがすべて同じである場合は True を返し、同じでない場合は False を返し、ツリー A の走査を続けて次の同一ノードを見つけます。
  3. 走査後に A が True を返さなかった場合、B は A の部分構造ではないことを意味し、False を返します。

bfs メソッド: A のサブツリーに B と同じ部分があるかどうかを判断するために使用されます。

  1. 通常の BFS ステップでは、キューを使用して、ツリー A およびツリー B に対応するノードノード NodeA および NodeB を格納します。
  2. チームに参加するための条件は、ツリー B ノードが存在する限りチームに参加することであるため、A に対応するノードがない場合は False を返します。
  3. A と B の対応するノード値が同じでない場合は、False を返します。
  4. B を走査した後に False が返されない場合は、B が A の部分構造であり、True を返すことを意味します。

追加説明

1. offer() と add() の違い
add() と offer() はどちらも要素をキューに追加します。ただし、満杯のキューに新しい要素を追加する場合、add() メソッドを呼び出すと未チェック例外がスローされ、offer() メソッドを呼び出すと false が返されます。これをもとにプログラム内で効果的な判断ができるようになります!

2. Peak() と element() の違い
eek() と element() はどちらも削除せずにキューの先頭に戻りますが、peek() メソッドはキューが空の場合に null を返し、element() を呼び出します。メソッドでは NoSuchElementException がスローされます。

3.poll ()とremove()の違い

poll() とremove() はどちらもオブジェクトを削除して返しますが、キューが空の場合は、poll() は null を返し、remove() は NoSuchElementException をスローします。

反復コード bfs を使用して実装:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    
    
    public boolean isSubStructure(TreeNode A, TreeNode B) {
    
    
        if(A == null || B == null){
    
    
            return false;
        }
        //构建队列将A中的结点,放入队列中
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(A);
        while(!queue.isEmpty()){
    
    
            //弹出A树中的结点,判断与子树B中的结点值是否相同
            TreeNode node = queue.poll();
            if(node.val == B.val){
    
    
                //当结点相同,判断左子结点和右子结点是否相同
                if(bfs(node,B)){
    
    
                    return true;
                }
            }
            //依次寻找A树中其它结点等于B子树结点,直到叶子结点为止
            if(node.left != null){
    
    
                queue.offer(node.left);
            }
            if(node.right != null){
    
    
                queue.offer(node.right);
            }
        }
        //如果A树为空,则返回false
        return false;
    }

    private boolean bfs(TreeNode nodeA,TreeNode nodeB){
    
    
        //将树的结点值放到队列中
        Queue<TreeNode> queueA = new LinkedList<>();
        Queue<TreeNode> queueB = new LinkedList<>();
        queueA.offer(nodeA);
        queueB.offer(nodeB);

        while(!queueB.isEmpty()){
    
    
            //弹出两个队列的结点值
            nodeA = queueA.poll();
            nodeB = queueB.poll();
            //当A树没有子节点,即为叶子结点或者或者结点值不同时,返回false
            if(nodeA == null || nodeA.val != nodeB.val){
    
    
                return false;
            }
            //判断之后的节点
            if(nodeB.left != null){
    
    
                queueA.offer(nodeA.left);
                queueB.offer(nodeB.left);
            }

            if(nodeB.right != null){
    
    
                queueA.offer(nodeA.right);
                queueB.offer(nodeB.right);
            }
        }
        return true;
    }
}

再帰の一般的な考え方:アイデアへのリンク

ツリー B がツリー A の部分構造である場合、部分構造のルート ノードはツリー A の任意のノードになります。したがって、ツリー BB がツリー A の部分構造であるかどうかを判断するには、次の 2 つの手順を完了する必要があります。

  1. ツリー A 内の各ノード n_A の事前順序トラバーサル (対応する関数 isSubStructure(A, B))
  2. ツリー A の n_A をルートとするサブツリーにツリー B が含まれているかどうかを判断します。(対応する関数 recur(A, B))

画像-20210907155830582

アルゴリズムの流れ

isSub(A, B) 関数:

終了条件

  1. ノード B が空の場合: ツリー B が (リーフ ノード上で) 一致したことを意味するため、true を返します。
  2. ノード A が空の場合: ツリー A の葉ノードを越えたことを意味します。つまり、マッチングが失敗し、false が返されます。
  3. ノード A と B の値が異なる場合: マッチングが失敗し、false が返されることを意味します。

戻り値:

  1. A と B の左の子ノードが等しいかどうかを判断します。つまり、 recur(A.left, B.left);
  2. A と B の右側の子ノードが等しいかどうか、つまり recur(A.right, B.right) かどうかを判断します。

isSubStructure(A, B) 関数:

特殊なケースの処理: ツリー A が空、またはツリー B が空の場合は、直接 false を返します。

戻り値: ツリー B がツリー A の部分構造である場合、次の 3 つの条件のいずれかを満たしている必要があるため、または || を使用して接続します。

  1. ノード A をルート ノードとするサブツリーには、isSub(A, B) に対応するツリー B が含まれます。
  2. ツリー B はツリー A の左側のサブツリーの部分構造であり、isSubStructure(A.left, B) に対応します。
  3. ツリー B はツリー A の右側のサブツリーの部分構造であり、isSubStructure(A.right, B) に対応します。

再帰的コードを使用して実装

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    
    
    public boolean isSubStructure(TreeNode A, TreeNode B) {
    
    
       if(A == null || B == null){
    
    
           return false;
       }
       //先从根节点的判断B是不是Ade子树,再从左右子节点开始判断
       //只要从其中一个中找到B子树都返回True
       return isSub(A,B) || isSubStructure(A.left,B) || isSubStructure(A.right,B);

    }

    boolean isSub(TreeNode A,TreeNode B){
    
    
        //如果B为空时,代表B子树中的节点都访问结束,A中都能找到对应节点,返回true
        if(B == null){
    
    
            return true;
        }
        //如果B不为空A为空,或者两个结点对应值不同,返回false
        if(A == null || A.val != B.val){
    
    
            return false;
        }
        //比较完当前子树根节点,还要判断其左右子节点是否相同
        return isSub(A.left,B.left) && isSub(A.right,B.right);
    }
}

おすすめ

転載: blog.csdn.net/Royalic/article/details/120160980