Revisión de algoritmos: conocimiento básico del árbol binario

Varios recorridos de árboles binarios

Recorrido recursivo

Recorrido de orden medio: el orden de recorrido de orden medio es raíz izquierda a la derecha, y el método de escritura recursiva es relativamente simple. Pega la plantilla directamente

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

Por ejemplo, lettcode94 atraviesa el árbol binario usando el método de recorrido en orden, pero necesita volver a colocar una lista, luego puede agregar esta parte de la lógica en el comentario de código anterior

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);
}

Luego, solo cite los otros dos directamente, escriba la escritura de recorrido de preorden y postorden transversal, solo necesita colocar el código lógico de la operación en diferentes posiciones.

//先序遍历
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);
}

Recorrido no recursivo

La ventaja del recorrido recursivo es que el código es conciso, pero cuando el árbol binario tiene más nodos, es fácil romper StackOverFlow, por lo que generalmente también escribimos la escritura no recursiva del árbol binario. En términos generales, la escritura recursiva puede cambiar a escritura no recursiva., Esto requiere el uso de una pila de estructura de datos. La pila se caracteriza por ser el primero en entrar, el último en salir. Para el recorrido en orden, el nodo izquierdo se recorre primero, por lo que todos los nodos izquierdos se puede agregar a la pila primero, y luego el último nodo izquierdo Sáquelo para obtener el valor, luego saque el nodo derecho y repita

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;
}

Con la base del recorrido transversal de orden medio, puede escribirlo directamente escribiendo el recorrido transversal de primer orden

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;
}

¿Qué pasa con el recorrido posterior al pedido? Al realizar un recorrido de orden posterior, debe determinar si el nodo actual tiene nodos izquierdo y derecho, lo cual es más problemático. Aquí se utiliza un método más flexible, porque el recorrido de primer orden son las raíces izquierda y derecha, y la publicación -order traversal son las raíces izquierda y derecha, y los hijos izquierdo y derecho del árbol binario original El árbol se invierte, y luego el resultado del recorrido de preorden se puede revertir para obtener el resultado del recorrido de postorden, por ejemplo

Árbol original:

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

Recorrido posterior al pedido: 7 6 2 4 5 3 1

Invertir los nodos izquierdo y derecho

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

Recorrido de reserva: 1 3 5 4 2 6 7

Invertir el recorrido de preorden para obtener: 7 6 2 4 5 3 1 es el recorrido de posorden del árbol original, y revertir los subárboles izquierdo y derecho es relativamente simple y se puede hacer. Solo necesita atravesar el subárbol derecho primero durante el recorrido de preorden, y luego simplemente recorre el subárbol izquierdo.

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;
}

Cruce de nivel

El recorrido jerárquico se refiere al orden de arriba a abajo, de izquierda a derecha, luego BFD, generalmente se puede usar el algoritmo de amplitud primero, y la implementación de este algoritmo requiere la ayuda de una cola de estructura de datos. La ventaja de la cola es primero -en, primero en salir.

¿Por qué utilizar colas?

    3
   / \
  9  20
    /  \
   15   7

regreso:

[3,9,20,15,7]

De acuerdo con el ejemplo anterior, podemos saber que necesitamos guardar cada nodo para obtener los posibles nodos del subárbol izquierdo y derecho del nodo. Si la impresión es de izquierda a derecha, usar la cola primero en entrar primero en salir solo cumple con esto rasgo

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;
}

Aquí también hay una breve revisión de algunos métodos comunes de Deque como pila y cola

作为栈

método efecto
empujar (e) Aparecer
música pop() surgir
ojeada() Toma el elemento superior de la pila

作为队列

método efecto
oferta (e) Únete al jefe del equipo
encuesta() Excluir del final de la línea
ojeada() Desde el final de la línea

Ejercicio

La espada se refiere a la oferta32 pregunta 1

El árbol binario se imprime en capas de arriba a abajo, los nodos de la misma capa se imprimen en orden de izquierda a derecha y cada capa se imprime en una línea.

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

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

A diferencia del recorrido de nivel, este problema también necesita imprimir los datos de cada capa como una línea, lo cual debe ser porque los datos de cada línea se almacenan en la cola, lo que requiere operaciones en la cola actual durante cada ciclo. recuerde primero el tamaño de la cola y luego agregue los nodos secundarios

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;
}

La espada se refiere a la oferta 32 pregunta 2

Implemente una función para imprimir el árbol binario en orden en zigzag, es decir, la primera línea se imprime de izquierda a derecha, la segunda capa se imprime de derecha a izquierda y la tercera línea se imprime de izquierda a derecha. Otros Y así sobre.

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

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

Esta es una variante del problema anterior, solo necesita invertir los datos cuando encuentre filas pares

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;
    }

Supongo que te gusta

Origin blog.csdn.net/weixin_44706647/article/details/115190793
Recomendado
Clasificación