二分木 (2): アルゴリズムの質問タイプの概要

各バイナリ ツリーの質問タイプを説明する前に、バイナリ ツリー内のノードを表すクラスを作成します。コードは次のとおりです。

class Node {
    int value;
    Node left;
    Node right;

    public Node() {
    }

    public Node(int value) {
        this.value = value;
    }
}

1. 2 つの二分木ノードノード 1 とノード 2 が与えられた場合、それらの最も低い共通の祖先ノードを見つけます。

アイデア:

方法1: ①ハッシュテーブルHashMapを作成し、バイナリツリー内の全ノードの親ノードを走査する

                ②node1までのすべての祖先ノードを記録するハッシュテーブルHashSetを作成する

                ③ ノード 2 はすべての祖先ノードを順番に走査し、HashSet によって記録されたノード 1 の祖先ノードがノード 2 の走査プロセスで初めて現れます。このノードは 2 つのノードの中で最も低い共通祖先ノードになります。

方法 2: (このメソッドのコード量は非常に少ないですが、理解するのが難しいため、理解を助けるために自分でコードを理解する必要があります) 最下位共通祖先は次の 2 つのケースのみです。1) ノード 1 はノード 1 とノード 2 の最低共通祖先、またはノード 2 はノード 1 とノード 2 です。 2) の最低共通祖先は、ノード 1 とノード 2 が互いの最低共通祖先ではありません。ノード 1 とノード 2 は、上向きに収束する場合にのみ見つかります。

方法 1 のコード実装は次のとおりです。

  //找到两个节点node1、node2的最低公共祖先
    //方法一
    public Node findLowestAncestor1(Node head,Node node1,Node node2){
        HashMap<Node,Node> fatherMap=new HashMap<>();
        fatherMap.put(head,head);
        process3(head,fatherMap);
        HashSet<Node> node1AncestorSet=new HashSet<>();
        Node cur=node1;
        while (cur!=fatherMap.get(cur)){//从下往上回溯,只有头节点head的父节点是自己
            node1AncestorSet.add(cur);
            cur=fatherMap.get(cur);
        }
        node1AncestorSet.add(head);
        Node temp=node2;
        while (temp!=fatherMap.get(temp)){
            if (node1AncestorSet.contains(temp)){
                return temp;
            }
            temp=fatherMap.get(temp);
        }
        return head;//只有当第二个while中没有找到公共祖先,才可能运行这行代码,head一定是node1、node2的公共祖先
    }

    public void process3(Node head,HashMap<Node,Node> fatherMap){//process3这个方法作用是记录所有节点的父节点是哪个(head除外)
        if (head==null){
            return;
        }
        fatherMap.put(head.left,head);
        fatherMap.put(head.right,head);
        process3(head.left,fatherMap);
        process3(head.right,fatherMap);
    }

方法 2 のコード実装は次のとおりです。

 //方法二
    public Node findLowestAncestor2(Node head,Node node1,Node node2){
        Node cur=head;
        if (cur==null||cur==node1||cur==node2){
            return cur;
        }
        Node left=findLowestAncestor2(cur.left,node1,node2);
        Node right=findLowestAncestor2(cur.right, node1, node2);
        if (left!=null&&right!=null){
            return cur;
        }
        return left!=null?left:right;
    }

2. 二分木の最大幅を取得します。

アイデア: 幅優先トラバーサル + ハッシュ テーブルを使用して現在のノードが配置されているレイヤーを記録し、各レイヤーのノード数を数えて、最大のものを取得します。

アルゴリズムのコードは次のとおりです。

//获取二叉树的最大宽度
    public int getMaxWidth(Node head) {
        if (head == null) {
            return 0;
        }
        Queue<Node> queue = new LinkedList<>();//这是多态的用法,这样写也是对的:LinkedList<Node> queue=new LinkedList<>();
        HashMap<Node, Integer> hashMap = new HashMap<>();
        queue.add(head);
        hashMap.put(head, 1);
        int curLevel = 1;//当前在哪一层
        int curNodeLevel;//表示当前节点属于哪一层
        int curLevelNodes = 0;//当前层节点的个数
        int max = Integer.MIN_VALUE;//统计节点数最多那层节点的个数,max=0,-1等等都行
        Node cur;//表示当前节点
        while (!queue.isEmpty()) {
            cur = queue.poll();
            curNodeLevel = hashMap.get(cur);
            if (curNodeLevel == curLevel) {
                curLevelNodes++;
            } else {
                max = Math.max(max, curLevelNodes);
                curLevel++;
                curLevelNodes = 1;
            }
            if (cur.left != null) {
                hashMap.put(cur.left, curNodeLevel + 1);
                queue.add(cur.left);
            }
            if (cur.right != null) {
                hashMap.put(cur.right, curNodeLevel + 1);
                queue.add(cur.right);
            }
        }
        max = Math.max(max, curLevelNodes);//如果不加这一行,那么最后一层的节点个数就没有作比较
        return max;
    }

3. 二分木が検索二分木であるかどうかを判断します。

        二分木探索の特徴は、各部分木において、左ノード(部分木の先頭ノード)がそれより小さく、右ノードがそれより大きいことである。この場合、二分探索木に対して順序どおりの走査を実行すると、小さいものから大きいものへのシーケンスが得られることを見つけるのは難しくありません。

アイデア:順序トラバーサルを使用します。各サブツリーの左右のノードの値が昇順であることを確認するだけです。

コードは次のように実装されます。

//判断二叉树是否是搜索二叉树
    //方法一:中序递归遍历判断(还有一种递归方法就是创建一个集合,按中序遍历把每次遍历的节点依次加入集合中,如果集合中的树是按升序排列的,就是搜索二叉树)
    int preValue = Integer.MIN_VALUE;//这个变量用来表示(左头右顺序中)前一个节点的值,之所以用Integer.MIN_VALUE进行初始化,

    // 是因为中序遍历第一次打印的是整棵树最底部最左侧的那个节点,此节点没有左树和右数,所以不管这个节点的值是多少都一定符合搜索二叉树的特点
    //(这和任意值(打印的第一个节点的值)一定比系统最小值(该节点左树节点的值,其实该节点没有左树)大是一个道理)
    //也可以这样理解:中序遍历过程中,左节点可以看成是头节点的前一个值,头节点可以看成是右节点的前一个值,当前节点和前一个节点的值preValue比较后,
    // 便将当前节点的值赋值给preValue,依次传递下去
    public boolean isSBTRecur(Node head) {
        if (head == null) {
            return true;//这个初始化很重要,这里的head并不是说就只是整棵树的头节点,其实后面递归之后每个节点都有机会是head
        }
        //判断左树是否是搜索二叉树
        boolean isLeftSbt = isSBTRecur(head.left);
        if (!isLeftSbt) {
            return false;
        }

        //整个树是不是搜索二叉树(判断条件),这个判断条件放中间就是因为必须是中序遍历,判断条件才成立,先序和后序判断条件都无意义
        if (head.value <= preValue) {//意思是如果当前节点的值小于前一节点(此处的前一节点不是固定的含义,可以是左节点,也可以是头节点)的值
            return false;
        } else {
            preValue = head.value;
        }

        //判断右树是否是搜索二叉树
        boolean isRightSbt = isSBTRecur(head.right);
        return isRightSbt;
    }

    //方法二、中序非递归遍历判断,这方法比递归好理解,简单,但代码复杂,自己去写

4. バイナリ ツリーが完全なバイナリ ツリーであるかどうかを判断します。

アイデア: 幅優先トラバーサルを使用します。トラバースされる各ノードは、次の 2 つの条件を同時に満たす必要があります。 ① 右があり、左がない、false ② 不完全な左と右の子を持つ最初のノードが見つかった場合、後続のノードはすべてリーフ ノードです(左も右もありません)

コードは次のように実装されます。

 //判断二叉树是否是完全二叉树
    public boolean isCBT(Node head) {
        if (head == null) {
            return true;
        }
        LinkedList<Node> queue = new LinkedList<>();
        boolean leaf = false;//表示是否遇到左右孩子不双全的节点
        Node left;
        Node right;//分别表示左孩子和右孩子
        Node cur;//表示当前节点
        queue.add(head);
        while (!queue.isEmpty()) {
            cur = queue.poll();
            left = cur.left;
            right = cur.right;
            if ((leaf && (left != null || right != null)) || (left == null && right != null)) {
                //leaf表示是否遇到左右孩子不双全的节点,left!=null||right!=null表示当前节点不是叶节点,left==null&&right!=null表示有右无左
                return false;
            }
            if (left != null) {
                queue.add(left);
            }
            if (right != null) {
                queue.add(right);
            }
            if (left == null || right == null) {
                leaf = true;
            }
        }
        return true;
    }

5. 検索二分木が平衡二分木であるかどうかを判断します。

コードは次のように実装されます。

//判断搜索二叉树是否是平衡二叉树(此处用的方法是二叉树问题的递归套路,此处的递归和之前的递归还是有所不同的,是否二叉树的相关问题都可以用这个套路解决)
    //即树型DP(DP是动态规划的意思)的问题都可以用这个套路完成
    public boolean isBanlanced(Node head) {
        return process1(head).isBalanced;
    }

    public class ReturnType1 {
        boolean isBalanced;
        int height;

        public ReturnType1(boolean isBalanced, int height) {
            this.isBalanced = isBalanced;
            this.height = height;
        }
    }

    //注意:左树、右树、整棵树进行的操作内容都一样才能构成递归(比如此处都是返回ReturnType类型的数据)
    public ReturnType1 process1(Node x) {
        if (x == null) {
            return new ReturnType1(true, 0);
        }//这个if语句很重要,相当于就是初始条件
        ReturnType1 leftData = process1(x.left);
        ReturnType1 rightData = process1(x.right);
        int height = Math.max(leftData.height, rightData.height) + 1;
        boolean isBanlanced = leftData.isBalanced && rightData.isBalanced && Math.abs(leftData.height - rightData.height) < 2;
        return new ReturnType1(isBanlanced, height);
    }

6. バイナリ ツリーがいっぱいかどうかを判断します。

アイデア: ツリー型 DP は再帰ルーチンによって解決されます。再帰情報はレベルと n 情報を伝える必要があります。つまり、戻り値の型はこれら 2 つのメンバー変数を含むクラスです。左のツリーは返すことができ、右のツリーは返すことができます、それ自体が戻ることができます。同時に再帰を構成することに満足しています

コードは次のように実装されます。

//判断二叉树是否为满二叉树,这为树型DP问题,用套路解决
    public boolean isFBT(Node head) {
        return process2(head).nodes == 1 << (process2(head).height) - 1;//1左移一位就是2^1,左移两位就是2^2,……;某个数右移一位就是这个数除以2^1,……
    }

    public class ReturnType2 {
        int height;
        int nodes;

        public ReturnType2(int height, int nodes) {
            this.height = height;
            this.nodes = nodes;
        }
    }

    public ReturnType2 process2(Node x) {
        if (x == null) {
            return new ReturnType2(0, 0);
        }
        ReturnType2 leftData = process2(x.left);
        ReturnType2 rightData = process2(x.right);
        int height = Math.max(leftData.height, rightData.height) + 1;
        int nodes = leftData.nodes + rightData.nodes + 1;
        return new ReturnType2(height, nodes);
    }

おすすめ

転載: blog.csdn.net/Mike_honor/article/details/125834598