バイナリツリーの各種動作(非再帰再帰トラバーサル、木、ノード番号、等の深さ)

バイナリツリーの確立

最初のノード構造を与えられました:

static class Node {
    public int val;
    public Node left;
    public Node right;

    public Node(int val) {
        this.val = val;
    }
}
复制代码

方法を作成するには、2つのオプション:

  • バイナリツリーは二分木のルートと子ノードの周りに再帰的に添字関係、バイナリレベルの入力ノードであることができます。
  • 配列バイナリツリー(注空のツリーに入り-1)も、入力ストリームの前に使用することができます。

1、添字の関係

// given a arr to build
static Node createTree(int arr[], int i) {
    if (i >= arr.length || arr[i] == -1)
        return null;
    Node root = new Node(arr[i]);
    root.left = createTree(arr, 2 * i + 1);
    root.right = createTree(arr, 2 * i + 2);
    return root;
}
复制代码

実質的に、次のとおりです。

確立するために図2に示すように、プリアンブル入力(CIN)

// cin method	
static Node buildTree(Scanner cin) {
    Node root = null;
    int data = cin.nextInt();
    if (data != -1) {
        root = new Node(data);
        root.left = buildTree(cin);
        root.right = buildTree(cin);
    }
    return root;
}
复制代码

次のようにプロセスは以下のとおりです。

予約限定トラバーサル

図1に示すように、プリアンブル再帰

static void preOrder(Node T) {
    if (T == null)
        return;
    System.out.print(T.val + " ");
    preOrder(T.left);
    preOrder(T.right);
}
复制代码

2、プリアンブル非再帰

プレオーダートラバーサル順序:  ルート- >左サブツリー- >右の部分木は、ルートノードが利用可能であるようにするために、ダイレクトアクセス、アクセスが完了した後、同様に左サブツリーへのアクセスは、アクセス権のサブツリーは、次の通り:

  • 現在のノードがあればp、空のアクセスノードではないp、とノードp、スタック上へと(左サブツリーが空になるまで)、左サブツリーを訪問し続けます。
  • そうでない場合は、スタックから最上位の要素、スタックとアクセス権サブツリーの要素。
  • スタックが空となるまでp、空、サイクルの終わり。

コード:

static void iterativePre(Node root) {
    Stack<Node> s = new Stack<>();
    Node p = root;
    while (!s.empty() || p != null) {
        if (p != null) {//也可以写一个while循环,直到左子树为空
            s.push(p);
            System.out.print(p.val + " ");
            p = p.left;
        } else {
            p = s.pop();
            p = p.right;
        }
    }
}
复制代码

また、空の書かれている左上のサブツリーにアクセスすることができwhileサイクルを:

static void iterativePre2(Node root) {
    Stack<Node> s = new Stack<>();
    Node p = root;
    while (!s.empty() || p != null) {
        while (p != null) { // while循环,直到左子树为空
            s.push(p);
            System.out.print(p.val + " ");
            p = p.left;
        }
        p = s.pop();
        p = p.right;
    }
}
复制代码

書き込みに別の方法があります: 

  • まず、ルートノードスタック、およびこの要素への最初の訪問の各要素をポップし、それが右のサブツリーが存在、スタックだ場合、それは左のサブツリーだ場合、スタックもあります。
  • なぜ、それの右部分木に最初の先行順走査で、理由 - >左 - >右、しかし、あなたは右、次に左ので、最初、逆の順序でスタックすることができます。

この方法は、このわずかな変更であることを、デュアルスタック方式の後続の走査で具現化されます。

static void iterativePre3(Node root) {
    if (root == null)
        return;
    Node p = root;
    Stack<Node> stack = new Stack<>();
    stack.add(p);
    while (!stack.isEmpty()) {
        p = stack.pop();
        System.out.print(p.val + " ");
        if (p.right != null)// 先右再左即可
            stack.push(p.right);
        if (p.left != null)
            stack.push(p.left);
    }
}
复制代码

予約限定!

注文再帰的では1、

static void inOrder(Node T) {
    if (T == null)
        return;
    inOrder(T.left);
    System.out.print(T.val + " ");
    inOrder(T.right);
}
复制代码

受注非再帰的では2、

予約限定:  左のサブツリー- >ルート- >右のサブツリーは、次の通り:

  • 現在のノードが空でない場合!= null、スタックにプッシュされ、(及び種々の先行順走査、印刷に必要とされない)左側に現在のノード。
  • 現在のノードが空であり== null、そして(ここで印刷)、プリントアウト、右にスタックから現在のノード。

スタックまでは空であり、pがnull、サイクルの終わりです。

/**
* 1)、当前节点不空(!=null),压入栈中(和前序遍历不同的是,不需要打印),当前节点向左;
* 2)、当前节点为空(==null),从栈中拿出一个并且打印(在这里打印) ,当前节点向右;
*/
static void iterativeIn(Node root) {
    if (root == null)
        return;
    Stack<Node> s = new Stack<>();
    Node p = root;
    while (!s.empty() || p != null) {
        if (p != null) {
            s.push(p);
            p = p.left;
        } else {
            p = s.pop();
            System.out.print(p.val + " "); //在这里打印
            p = p.right;
        }
    }
}
复制代码

同様に、子供たちはどこにもへのアクセスをすることができ残されていますwhlie

static void iterativeIn2(Node root) {
    if (root == null)
        return;
    Stack<Node> s = new Stack<>();
    Node p = root;
    while (!s.empty() || p != null) {
        while (p != null) { //这里改成while
            s.push(p);
            p = p.left;
        }
        p = s.pop();
        System.out.print(p.val + " "); //在这里打印
        p = p.right;
    }
}
复制代码

後順

1、再帰的なシーケンス

static void postOrder(Node T) {
    if (T == null)
        return;
    postOrder(T.left);
    postOrder(T.right);
    System.out.print(T.val + " ");
}
复制代码

2、非再帰的なシーケンス

1)デュアルスタック方式

これは実際、非再帰プリアンブル(あるiterativePre3)改善の少し。

  • まず、Aプレオーダートラバーサルスタック(iterativePre3)順序は、左と右に
  • この時点で、我々は行うことができます最初に右クリック左折トラバーサルの順序を行うことができますので、  「右から左」とそれに続くトラバーサルがあり  、「左右」私たちは、ちょうど反対の前で、その後、スタックの反転を使用それを保存します

コード:

/**
* 非递归后续1(双栈法解决非递归后续)
* 后续遍历是要实现&emsp;&emsp;&emsp;左->右->中
* 这个方法和前序遍历的第二种方法&emsp;只是多了一个栈而已
* 因为&emsp;前序遍历是  中->左->右&emsp;&emsp;压栈顺序是 右->左
* 这样,我们就很容易实现&emsp;中->右->左遍历&emsp;&emsp;压栈顺序是&emsp;左->右
* 而后续遍历是要实现  左->右->中,
* 我们把上面的&emsp;&emsp;中右左&emsp;压入到另一个栈中&emsp;就实现了&emsp;左右中
*/
static void iterativePos(Node root) {
    Stack<Node> s = new Stack<>(), s2 = new Stack<>();
    Node p;
    s.push(root);
    while (!s.empty()) {
        p = s.pop();
        s2.push(p);
        if (p.left != null) s.push(p.left); //这里是先左再右  (非递归前序是先右再左)
        if (p.right != null) s.push(p.right);
    }
    while (!s2.empty())
        System.out.print(s2.pop().val + " ");
}
复制代码

2)、提供preノード

次のようにプロセスは以下のとおりです。

  • 任意のノードについてp、その第一のスタック。
  • 場合は、アクセスすることができます:①場合はp左の子と右の子が存在しない場合、あなたはそれを直接アクセスすることができます。②またはp左の子か右の子の存在が、子供が左と右の子供たちが訪問してきた、あなたはまた、ノードに直接アクセスすることができます。
  • どちらの場合もそうでない場合は、その後、右の子と左の子はスタックを回しますあなたが先頭の要素を取るたびに、子供がアクセス右の子の前に残っていたことをこれは保証し左の子と右の子の訪問のルートノードの後にアクセスされています。

コード:

/*** 非递归后续2(设置pre结点) */
static void iterativePos2(Node root) {
    Node cur, pre = null;
    Stack<Node> s = new Stack<>();
    s.push(root);
    while (!s.empty()) { 
        cur = s.peek();
        // 两种可以访问的情况
        if ((cur.left == null && cur.right == null) ||
            ((pre != null) && (pre == cur.left || pre == cur.right))) {
            System.out.print(cur.val + " ");
            s.pop();
            pre = cur;
        } else {
            if (cur.right != null) s.push(cur.right);
            if (cur.left != null) s.push(cur.left);
        }
    }
}
复制代码

レベルをトラバース

非常にシンプル。使用にBFSキューは、それぞれの完全な訪問はp周りの子がある場合は、その後、チームまでのチームに空にする。

static void levelOrder(Node root) {
    if (root == null)
        return;
    Queue<Node> queue = new LinkedList<>();
    queue.add(root);
    while (!queue.isEmpty()) {
        Node now = queue.poll();
        System.out.print(now.val + " ");
        if (now.left != null) queue.add(now.left);
        if (now.right != null) queue.add(now.right);
    }
}
复制代码

木を見ると、どのノード値xを持っていません

再帰的な2つの条件が、1が空の代表者を発見していない、彼は、サブツリーに関するそれ以外の場合は、再帰的な検索を直接返された言葉を見つけました。

//查找某个值为x的结点
static Node search(Node T, int x) {
    if (T == null)
        return null;
    if (T.val == x)
        return T;
    else {
        if (search(T.left, x) == null)
            return search(T.right, x);
        else
            return search(T.left, x);
    }
}
复制代码

ツリー内のノードの数を数えます

ツリーのルートノードは、番号(1)+ +右サブツリーの左の子には、再帰的に解決することができるツリーノードポイント番号の数に等しいです。

source-java
//统计结点个数
static int count(Node T) {
    if (T == null)
        return 0;
    else
        return count(T.left) + count(T.right) + 1;
}
复制代码

木の高さ計算

また、ルートの比較的高い高さと相まって再帰的解決策は、ツリーのサブツリーについてです。

source-java
//计算二叉树的深度
static int depth(Node T) {
    if (T == null)
        return 0;
    return Math.max(depth(T.left), depth(T.right)) + 1;
}
复制代码

2つのツリーを分析等しくありません

再帰的な解決策は、ルートノードの値の両方が等しく、2つのツリーに等しく、左右のサブツリーは等しくなければなりません。

source-java
//判断两棵树是不是相等
static boolean is_SameTree(Node T1, Node T2) {
    if (T1 == null && T2 == null)
        return true;
    else {
        return T1 != null && T2 != null && T1.val == T2.val
            && is_SameTree(T1.left, T2.left) && is_SameTree(T1.right, T2.right);
    }
}
复制代码

いいえ世間の注目を歓迎しない:歳の少年の成長経路は、定期的に乾燥品の毎週の選択を提供します!

おすすめ

転載: juejin.im/post/5e06053551882512513545f3