Método iterativo para recorrer el árbol binario antes, después y después

Prefacio

El método recursivo es muy simple para resolver los tres métodos transversales, pero el método iterativo no es tan simple.Este artículo proporciona varios métodos y plantillas.

Tipo de nodo

struct TreeNode {
    
    
	 int val;
	 TreeNode* left;
	 TreeNode* right;
	 TreeNode(int x) : val(x), left(NULL), right(NULL) {
    
    }
	 
};

Recorrido de preorden

tema

El recorrido de preorden es un orden secuencial de izquierda y derecha. De acuerdo con las características de primero en entrar, último en salir de la pila, el orden de apilamiento es opuesto al orden de recorrido, de modo que se puede obtener el orden deseado cuando la pila está publicado.

Pensamiento recursivo:先树根,然后左子树,然后右子树。每棵子树递归。

En el algoritmo iterativo, la idea evolucionó hasta convertirse en un nodo A, al que se debe acceder de inmediato.

Porque cada subárbol visita primero su nodo raíz. Para los subárboles izquierdo y derecho de un nodo, la raíz también debe visitarse primero. En los dos subárboles de A, después de atravesar el subárbol izquierdo, vuelva a atravesar el subárbol derecho. Por lo tanto, después de visitar el nodo raíz, antes de atravesar el subárbol izquierdo, inserte el subárbol derecho en la pila.


class Solution {
    
    
public:
    vector<int> preorderTraversal(TreeNode* root) 
    {
    
    
        stack < TreeNode*> st;
		vector< int > result;
		st.push(root);
		while (!st.empty())
		{
    
    
			TreeNode* node = st.top();
			st.pop();
			if (node != NULL)
				result.push_back(node->val);
			else
				continue;
			st.push(node->right);
			st.push(node->left);
	    }
		return result;
    }
};

Recorrido posterior al pedido

tema

Podemos completar el recorrido posterior al pedido de manera similar al recorrido posterior al pedido. El recorrido posterior al pedido es simétrico con el recorrido posterior al pedido.

Idea: 每到一个节点 A,就应该立即访问它。 然后将左子树压入栈,再次遍历右子树。
Debido a que el pedido anticipado es medio e izquierdo, después de completar lo anterior, es medio-derecho-izquierdo, y el resultado es izquierda-derecha y medio en orden inverso (recorrido posterior al pedido)

遍历完整棵树后,结果序列逆序即可。

class Solution {
    
    
public:
    vector<int> postorderTraversal(TreeNode* root) 
    {
    
    
        stack < TreeNode*> st;
		vector< int > result;
		st.push(root);
		while (!st.empty())
		{
    
    
			TreeNode* node = st.top();
			st.pop();
			if (node != NULL)
				result.push_back(node->val);
			else
				continue;			
			st.push(node->left);
			st.push(node->right);
	    }
        reverse(result.begin(), result.end());
		return result;
    }
};

Recorrido en orden

tema

Ideas:每到一个节点 A,因为根的访问在中间,将 A 入栈。然后遍历左子树,接着访问 A,最后遍历右子树。

Después de acceder a A, A se puede sacar de la pila. Porque se han visitado A y su subárbol izquierdo.

class Solution {
    
    
public:
    vector<int> inorderTraversal(TreeNode* root) 
    {
    
    
        stack < TreeNode*> st;
		vector< int > result;
		TreeNode* node = root;
		while (node||st.size())
		{
    
    
			while (node)
			{
    
    
				st.push(node);
				node = node->left;
			}
			if (st.size()) 
			{
    
    
				node = st.top(), st.pop();
				result.push_back(node->val);
				node = node->right;
				//左子树遍历完成,开始遍历右子树
			}
		}
		return result;
    }
};

Solución de plantilla más inteligente

original

Idea: Necesitamos un signo para distinguir cada pila de llamadas recursivas, aquí usamos nullptr para representar el nodo a procesar.

En el proceso de iterar en este momento, en realidad tenemos dos operaciones, una está procesando: poner elementos en la matriz de resultados, y la otra está visitando: atravesar nodos.

Por qué el código de recorrido de la reserva que se acaba de escribir no se puede usar en común con el recorrido de orden medio, porque el orden de recorrido de la orden de anticipación es medio izquierdo y derecho, el elemento al que se accede primero es el nodo del medio y el elemento procesado también es el nodo intermedio, por lo que se puede escribir ahora Código relativamente conciso, porque el orden de los elementos a los que se accede y los elementos a procesar son los mismos, y ambos son nodos intermedios.

Pero el recorrido en orden no lo es, lo que hace que el orden de procesamiento y el orden de acceso sean inconsistentes. Entonces, en el método mejorado, use nullptr para marcar el nodo que se procesará

Recorrido de preorden

class Solution {
    
    
public:
    vector<int> preorderTraversal(TreeNode* root) {
    
    
        vector<int> res;  //保存结果
        stack<TreeNode*> call;  //调用栈
        if(root!=nullptr) call.push(root);  //首先介入root节点
        while(!call.empty()){
    
    
            TreeNode *t = call.top();
            call.pop();  //访问过的节点弹出
            if(t!=nullptr){
    
    
                if(t->right) call.push(t->right);  //右节点先压栈,最后处理
                if(t->left) call.push(t->left);
                call.push(t);  //当前节点重新压栈(留着以后处理),因为先序遍历所以最后压栈
                call.push(nullptr);  //在当前节点之前加入一个空节点表示已经访问过了
            }else{
    
      //空节点表示之前已经访问过了,现在需要处理除了递归之外的内容
                res.push_back(call.top()->val);  //call.top()是nullptr之前压栈的一个节点,也就是上面call.push(t)中的那个t
                call.pop();  //处理完了,第二次弹出节点(彻底从栈中移除)
            }
        }
        return res;
    }
};
来源:力扣(LeetCode)

Recorrido posterior al pedido

class Solution {
    
    
public:
    vector<int> postorderTraversal(TreeNode* root) {
    
    
        vector<int> res;
        stack<TreeNode*> call;
        if(root!=nullptr) call.push(root);
        while(!call.empty()){
    
    
            TreeNode *t = call.top();
            call.pop();
            if(t!=nullptr){
    
    
                call.push(t);  //在右节点之前重新插入该节点,以便在最后处理(访问值)
                call.push(nullptr); //nullptr跟随t插入,标识已经访问过,还没有被处理
                if(t->right) call.push(t->right);
                if(t->left) call.push(t->left);
            }else{
    
    
                res.push_back(call.top()->val);
                call.pop();
            }
        }
        return res;   
    }
};
来源:力扣(LeetCode)

Recorrido en orden

class Solution {
    
    
public:
    vector<int> inorderTraversal(TreeNode* root) {
    
    
        vector<int> res;
        stack<TreeNode*> call;
        if(root!=nullptr) call.push(root);
        while(!call.empty()){
    
    
            TreeNode *t = call.top();
            call.pop();
            if(t!=nullptr){
    
    
                if(t->right) call.push(t->right);
                call.push(t);  //在左节点之前重新插入该节点,以便在左节点之后处理(访问值)
                call.push(nullptr); //nullptr跟随t插入,标识已经访问过,还没有被处理
                if(t->left) call.push(t->left);
            }else{
    
    
                res.push_back(call.top()->val);
                call.pop();
            }
        }
        return res;
    }
};
来源:力扣(LeetCode)

Supongo que te gusta

Origin blog.csdn.net/weixin_45605341/article/details/108028991
Recomendado
Clasificación