アルゴリズムのレビュー-二分木の基本的な知識

二分木のさまざまな走査

再帰的走査

ミドルオーダートラバーサル:ミドルオーダートラバーサルの順序は左ルート右であり、再帰的な書き込み方法は比較的単純です。テンプレートを直接貼り付けます

public void inorder(TreeNode root){
    
    
    if(root == null)	return;
    if(root.left != null) inorder(root.left);
    //当前位置即为操作,可以根据题意进行添加
    if(root.right != null)	inorder(root.right);
}

たとえば、lettcode94はインオーダートラバーサルメソッドを使用してバイナリツリーをトラバースしますが、リストを戻す必要がある場合は、上記のコードコメントにロジックのこの部分を追加できます。

public List<Integer> inorderTraversal(TreeNode root) {
    
    
    List<Integer> res = new ArrayList<Integer>();
    inorder(root, res);
    return res;
}
public void inorder(TreeNode root, List<Integer> res){
    
    
    if(root == null)    return;
    if(root.left != null)   inorder(root.left, res);
    res.add(root.val);
    if(root.right != null)  inorder(root.right, res);
}

次に、他の2つを直接引用し、プレオーダートラバーサルとポストオーダートラバーサルの記述を書き出します。操作のロジックコードを異なる位置に配置するだけで済みます。

//先序遍历
public List<Integer> preorderTraversal(TreeNode root) {
    
    
    List<Integer> res = new ArrayList<Integer>();
    inorder(root, res);
    return res;
}
public void preorder(TreeNode root, List<Integer> res){
    
    
    if(root == null)    return;
    res.add(root.val);
    if(root.left != null)   inorder(root.left, res);
    if(root.right != null)  inorder(root.right, res);
}
//后序遍历
public List<Integer> postorderTraversal(TreeNode root) {
    
    
    List<Integer> res = new ArrayList<Integer>();
    inorder(root, res);
    return res;
}
public void postorder(TreeNode root, List<Integer> res){
    
    
    if(root == null)    return;
    if(root.left != null)   inorder(root.left, res);
    if(root.right != null)  inorder(root.right, res);
    res.add(root.val);
}

非再帰的トラバーサル

再帰的トラバーサルの利点は、コードが簡潔であるということですが、バイナリツリーにノードが多い場合、StackOverFlowをバーストしやすいため、通常、バイナリツリーの非再帰的書き込みも記述します。一般的に、再帰的書き込みは次のことができます。非再帰的書き込みに変更されます。これには、データ構造スタックを使用する必要があります。スタックは、first-in-last-outによって特徴付けられます。順序付きトラバーサルの場合、左側のノードが最初にトラバースされるため、すべての左側のノードがトラバースされます。最初にスタックに追加し、次に最後の左側のノードを追加して値を取得し、次に右側のノードを取り出して繰り返します。

public List<Integer> inorder(TreeNode root){
    
    
    List<Integer> res = new ArrayList<>();
    Deque<TreeNode> stack = new LinkedList<>();
    while(root != null && !stack.isEmpty()){
    
    
		while(root != null){
    
    
            stack.push(root);
            root = root.left;
        }
        root = stack.pop();
        res.add(root.val);
        root = root.right;
    }
    return res;
}

中次走査を基盤として、一次走査を記述することで直接書き込むことができます。

public List<Integer> perorder(TreeNode root){
    
    
    List<Integer> res = new ArrayList<>();
    Deque<TreeNode> stack = new LinkedList<>();
    while(root != null || !stack.isEmpty()){
    
    
        while(root != null){
    
    
            res.add(root.val);
            stack.push(root);
            root = root.left;
        }
        root = stack.pop();
        root = root.right;
    }
    return res;
}

注文後のトラバーサルはどうですか?ポストオーダートラバーサルを実行するときは、現在のノードに左右のノードがあるかどうかを判断する必要がありますが、これはより面倒です。1次トラバーサルは左右のルートであり、ポストであるため、ここではより柔軟な方法を使用します。 -順序トラバーサルは、元のバイナリツリーの左右のルートと左右の子です。ツリーが逆になり、事前順序トラバーサルの結果を逆にして、事後トラバーサルの結果を取得できます。例えば

元の木:

      1
    /    \
   2      3
  /  \    /  \
 7   6   4	  5

注文後のトラバーサル:7 6 2 4 5 3 1

左右のノードを反転します

      1
    /    \
   3      2
  /  \   /  \ 
 5    4 6	 7

事前注文トラバーサル:1 3 5 4 2 6 7

事前順序トラバーサルを逆にすると、次のようになります。76 2 4 5 3 1は元のツリーの事後トラバーサルであり、左右のサブツリーを逆にするのは比較的簡単で実行できます。必要なのは右サブツリーのみです。最初にプレオーダートラバーサル中に、次に左側のサブツリーをトラバースします。

public List<Integer> postorder(TreeNode root){
    
    
    List<Integer> res = new ArrayList<>();
    Deque<TreeNode> stack = new LinkedList<>();
    while(root != null || !stack.isEmpty()){
    
    
        while(root != null){
    
    
            res.add(root.val);
            stack.push(root);
            root = root.right;
        }
        root = stack.pop();
        root = root.left;
    }
    Collections.reverse(res);
    return res;
}

レベルトラバーサル

階層的トラバーサルとは、上から下、左から右の順序を指し、次にBFD、幅優先アルゴリズムを一般的に使用できます。このアルゴリズムの実装には、データ構造キューの助けが必要です。キューの利点は最初です。 -イン、ファーストアウト。

なぜキューを使用するのですか?

    3
   / \
  9  20
    /  \
   15   7

戻り値:

[3,9,20,15,7]

上記の例によると、ノードの可能な左と右のサブツリーノードを取得するには、各ノードを保存する必要があることがわかります。印刷が左から右の場合、先入れ先出しでキューを使用すると、これが満たされます。特徴

public int[] levelOrder(TreeNode root) {
    
    
    List<Integer> res = new ArrayList<>();
    Deque<TreeNode> q = new LinkedList<>();
    if(root == null)    return new int[]{
    
    };
    q.add(root);
    while(!q.isEmpty()){
    
    
        TreeNode temp = q.poll();                
        res.add(temp.val);
        if(temp.left != null) q.add(temp.left);
        if(temp.right != null) q.add(temp.right);
    }
    int[] r = new int[res.size()];
    for(int i = 0;i < res.size();i++){
    
    
        r[i] = res.get(i);
    }
    return r;
}

スタックおよびキューとしてのDequeのいくつかの一般的な方法の簡単なレビューもあります

作为栈

方法 効果
push(e) ポップイン
ポップ() 現れる
ピーク() スタックの一番上の要素を取ります

作为队列

方法 効果
オファー(e) チームのトップに参加する
poll() 行末から除外する
ピーク() 行末から

運動

剣はoffer32質問1を指します

二分木は上から下にレイヤーで印刷され、同じレイヤーのノードは左から右に順番に印刷され、各レイヤーは1行に印刷されます。

例如:
给定二叉树: [3,9,20,null,null,15,7],
	3	
   / \
  9  20
    /  \
   15   7
返回其层次遍历结果:

[
  [3],
  [9,20],
  [15,7]
]

レベルトラバーサルとは異なり、この問題では、各レイヤーのデータを行として出力する必要があります。これは、各行のデータがキューに格納されるため、各サイクル中に現在のキューでの操作が必要になるためです。最初にキューのサイズを覚えてから、子ノードを追加します

public List<List<Integer>> levelOrder(TreeNode root) {
    
    
    List<List<Integer>> res = new ArrayList<>();
    Deque<TreeNode> q = new LinkedList<>();

    if(root == null)    return new ArrayList<List<Integer>>();
    q.offer(root);
    while(!q.isEmpty()){
    
    
        List<Integer> tempA = new ArrayList<>();
        //这行代码很重要,需要记住当前层的队列数量
        // for(int i = queue.size(); i > 0; i--)
        int size = q.size();
        for(int i = 0;i < size;i++){
    
    
            TreeNode temp = q.poll();
            if(temp.left != null) q.offer(temp.left);
            if(temp.right != null) q.offer(temp.right);
            tempA.add(temp.val);
        }
        res.add(tempA);
    }
    return res;
}

剣はoffer32質問2を指します

二分木をジグザグに印刷する機能を実装してください。つまり、最初の行は左から右に印刷され、2番目のレイヤーは右から左に印刷され、3番目の行は左から右に印刷されます。オン。

    3
   / \
  9  20
    /  \
   15   7
返回其层次遍历结果:

[
  [3],
  [20,9],
  [15,7]
]

これは上記の問題の変形であり、偶数行に遭遇した場合にのみデータを反転する必要があります

public List<List<Integer>> levelOrder(TreeNode root) {
    
    
        List<List<Integer>> res = new ArrayList<>();
        Deque<TreeNode> q = new LinkedList<>();
        if(root == null) return new ArrayList<>();
        int flag = 1;
        q.offer(root);
        while(!q.isEmpty()){
    
    
            List<Integer> temp = new ArrayList<>();
            for(int i = q.size();i >0 ;i--){
    
    
                TreeNode node = q.poll();
                temp.add(node.val);
                if(node.left != null)   q.offer(node.left);
                if(node.right != null)  q.offer(node.right);
            }
            if(flag % 2 == 0){
    
    
                Collections.reverse(temp);
            }
            res.add(temp);
            flag++;
        } 
        return res;
    }

おすすめ

転載: blog.csdn.net/weixin_44706647/article/details/115190793