Problema de secuencia de árbol de búsqueda binaria (atrapar todo a la vez)

contenido

1. Verificar la secuencia de preorden del árbol de búsqueda binaria

2. Recorrido de pedido anticipado para restaurar el árbol de búsqueda binaria

3. Recorrido posterior al pedido del árbol de búsqueda binaria


1. Verificar la secuencia de preorden del árbol de búsqueda binaria

255. Verificación del árbol de búsqueda binaria de secuencia transversal de preorden - LeetCode (leetcode-cn.com)

Tema Descripción:

Ideas para resolver problemas:

  • La naturaleza de BST es que el subárbol izquierdo es más pequeño que la raíz y el subárbol derecho es más grande que la raíz, por lo tanto, verifique esta propiedad.
  • Primero encuentre el valor con la posición de inicio más grande de la matriz, es decir, encuentre el valor más grande que el nodo raíz.Si la secuencia dada es la secuencia de preorden del árbol de búsqueda binaria, entonces las secuencias subsiguientes son todos los subárboles correctos de la raíz, y se debe cumplir la siguiente secuencia: el árbol tiene nodos más grandes que la raíz. Si no se cumple, definitivamente no es un árbol de búsqueda binaria. Si se cumple, dividiremos toda la secuencia en dos partes, la parte del subárbol recursivo y la parte del subárbol derecho. Si se cumplen estas dos partes , entonces esta secuencia es la secuencia de preorden del árbol de búsqueda binaria.

Código correspondiente:

class Solution
{
public:
    bool verifyPreorder(vector<int> &preorder)
    {
        return checkBSTPreorder(preorder, 0, preorder.size() - 1);
    }
    bool checkBSTPreorder(vector<int> &preorder, int begin, int end)
    {
        if (begin >= end)
        {
            return true;
        }
        int index = begin;
        while (index <= end && preorder[index] <= preorder[begin]) //找到第一个小于preorder[left]
        //的数
        {
            index++;
        }
        //检查后面是否有小于根节点值如果有说明不是二叉搜索树的前序序列
        int tmp = index;
        while (tmp <= end)
        {
            if (preorder[tmp] <= preorder[begin])
            {
                return false;
            }
            tmp++;
        }
        //检查左子树和右子树是不是
        return checkBSTPreorder(preorder, begin + 1, index - 1) &&
               checkBSTPreorder(preorder, index, end);
    }
};

Pero, lamentablemente, la complejidad del tiempo es demasiado alta como para que se agote el tiempo. Aquí hay otro método

Método 2: pila monótona

El árbol de búsqueda binaria es izquierda <raíz <derecha, y el recorrido de preorden es raíz->izquierda->derecha. Con base en tales propiedades y métodos transversales, sabemos que cuanto más a la izquierda, más pequeño es. En este De esta manera, se puede construir un árbol monótonamente decreciente La pila para realizar un seguimiento de los elementos atravesados.

¿Por qué usar una pila monotónica? Porque en el proceso de atravesar el subárbol izquierdo, el valor se vuelve cada vez más pequeño. Una vez que el valor es mayor que el valor del elemento superior de la pila, significa que el subárbol derecho comenzará a ingresar (si no, debe continuar ingresando el subárbol izquierdo. Si no lo entiende, consulte la definición del árbol de búsqueda binaria), pero ¿desde qué nodo comienza este subárbol derecho?

La pila monotónica nos ayuda a registrar estos nodos. Siempre que el elemento superior de la pila sea más pequeño que el nodo actual, significa que todavía es un subárbol izquierdo y debe eliminarse, porque necesitamos encontrar el nodo principal conectado directamente al nodo secundario derecho, es decir, para encontrar este subárbol Siempre que el elemento superior de la pila sea aún más pequeño que el nodo actual, aparecerá hasta que el elemento superior de la pila sea mayor que el nodo, o la pila esta vacio. El elemento anterior en la parte superior de la pila es la raíz del nodo del subárbol.

Luego, la matriz continúa recorriendo hacia atrás, y cada nodo del subárbol derecho debe ser más grande que la raíz del subárbol para satisfacer, de lo contrario, no es un árbol de búsqueda binaria.

Código correspondiente:

class Solution {
public:
    bool verifyPreorder(vector<int>& preorder) {
         int Min=INT_MIN;//最小值
         stack<int>stk;
          for(int i=0;i<preorder.size();i++)
          {
              // 右子树元素必须要大于递减栈被top访问的元素,否则就不是二叉搜索树
              if(preorder[i]<Min)//不满足搜索二叉树的性质
              {
                  
                  return false;
              }
              while(!stk.empty()&&preorder[i]>stk.top())
              {
              // 数组元素大于单调栈的元素了,表示往右子树走了,记录下上个根节点
         // 找到这个右子树对应的根节点,之前左子树全部弹出,不在记录,因为不可能在往根节点的左子树走了
                  Min=stk.top();//更新最小值
                  stk.pop();
              }
              stk.push(preorder[i]);//入栈;
          }
          return true;//说明是二叉搜索树的前序遍历
    }
};

2. Recorrido de pedido anticipado para restaurar el árbol de búsqueda binaria

1008. Preordenar Traversal Construyendo Árbol de Búsqueda Binaria - LeetCode (leetcode-cn.com)

Tema Descripción:

 Ideas para resolver problemas:

El título nos da el recorrido previo al pedido del árbol de búsqueda binaria, por lo que no hay duda de que el primer elemento es el nodo raíz de todo el árbol. La búsqueda binaria tiene las siguientes propiedades:

1. Todos los nodos del subárbol izquierdo tienen valores menores que el nodo raíz.

2. Todos los nodos del subárbol derecho tienen valores mayores que el nodo raíz.

Entonces podemos atravesar esta matriz para encontrar el primer elemento más grande que el nodo raíz, luego de este elemento en adelante es la parte derecha del subárbol. De esta forma, podemos dividir el arreglo en [subárbol izquierdo] raíz [subárbol derecho] El subárbol izquierdo y el subárbol derecho se pueden construir recursivamente. Tome [8, 5, 1, 7, 10, 12] como ejemplo, 8 es el nodo raíz, [5, 1, 7] menor que 8 es el valor en su subárbol izquierdo y mayor que él [10, 12 ] ] es el valor en su subárbol derecho. Entonces puede referirse al método de búsqueda binaria para dividir la matriz en dos partes, es así:

Luego dividimos [5, 1, 7] a la izquierda de acuerdo con el método anterior. 5 es el nodo raíz, 1 menor que 5 es el nodo secundario izquierdo y 7 mayor que 5 es el nodo secundario derecho. De manera similar, 10 en [10, 12] a la derecha es el nodo raíz, y 12 más grande que 10 es el nodo secundario derecho, por lo que seguimos dividiendo hasta que no podamos dividir, por lo que el resultado es el siguiente:

Código correspondiente:


class Solution {
public:
    TreeNode* bstFromPreorder(vector<int>& preorder) {
         return buildTree(preorder,0,preorder.size()-1);
    }
    TreeNode*buildTree(vector<int>&preorder,int left,int right)
    {
        if(left>right)
         return nullptr;//区间不存在说明没有数了
         int index=left;//从开始位置查找第一个比preorder[left]大的数
         //将其划分为[left+1,index-1]左子树[left]根[inde,right]//右子树
         while(index<=right&&preorder[index]<=preorder[left])
        {
            index++;
        }
        //[left+1,index-1]左子树[left]根[inde,right]
        TreeNode*root=new TreeNode(preorder[left]);//根节点
        root->left=buildTree(preorder,left+1,index-1);//构建左子树
        root->right=buildTree(preorder,index,right);//构建右子树
        return root;
    }
};

¿Se puede optimizar este problema? Encontramos que cada vez que tenemos que encontrar el elemento de la derecha que es más grande que él y más cercano a él, la complejidad del tiempo es O(N^2). ¿Y no es eso exactamente lo que hacen las pilas monótonas? Entonces podemos usar la pila monotónica para preprocesar y optimizar esta operación transversal por adelantado. Deje que el grado de replicación en el tiempo se reduzca a O (N). Código correspondiente

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* bstFromPreorder(vector<int>& preorder) {
        int N=preorder.size();
        vector<int>nearBig(N,-1);//用于存储一个元素右边比他大并且离他最近位置的小标
        //全部先设置为-1是为了提前解决结算阶段,栈中元素右边没有比他大的了
         stack<int>stk;
         for(int i=0;i<N;i++)
         {
             while(!stk.empty()&&preorder[stk.top()]<preorder[i])
             {
                  nearBig[stk.top()]=i;
                  stk.pop();
                  
             }
             stk.push(i);
         }
      
         return buildTree(preorder,0,preorder.size()-1,nearBig);
    }
    TreeNode*buildTree(vector<int>&preorder,int left,int right,vector<int>&nearBig)
    {
        if(left>right)
         return nullptr;//区间不存在说明没有数了
        
         //将其划分为[left+1,index-1]左子树[left]根[inde,right]//右子树
        /* while(index<=right&&preorder[index]<=preorder[left])
        {
            index++;
        }*/
        int index=nearBig[left]==-1?right+1:nearBig[left];

        //[left+1,index-1]左子树[left]根[inde,right]
        TreeNode*root=new TreeNode(preorder[left]);//根节点
        root->left=buildTree(preorder,left+1,index-1,nearBig);//构建左子树
        root->right=buildTree(preorder,index,right,nearBig);//构建右子树
        return root;
    }
};

3. Recorrido posterior al pedido del árbol de búsqueda binaria

Sword se refiere a la Oferta 33. Secuencia transversal posterior al pedido del árbol de búsqueda binario - LeetCode (leetcode-cn.com)

Tema Descripción:

 Ideas para resolver problemas:

La idea de resolver este problema es básicamente la misma que la del problema anterior. Primero encuentre el nodo raíz, luego encuentre el primer número más pequeño que el nodo raíz de derecha a izquierda y luego divídalo en un subárbol izquierdo y un subárbol derecho. Al juzgar el subárbol izquierdo ¿Hay un número mayor que el nodo raíz? Si hay una descripción de que no es un recorrido posterior al orden del árbol de búsqueda binaria, si ambos son más pequeños que el nodo raíz, recurra al subárbol izquierdo y el subárbol derecho. El subárbol izquierdo y el subárbol derecho se satisfacen al mismo tiempo. Tome [3, 5, 4, 10, 12, 9] como ejemplo a continuación:

Sabemos que el último número en el recorrido posterior debe ser el nodo raíz, por lo que el último número 9 en la matriz es el nodo raíz.Encontramos el primer número 10 mayor que 9 de adelante hacia atrás, luego [10, 12] después 10 (excepto 9) Todos son los nodos secundarios derechos de 9, y [3, 5, 4] delante de 10 son todos los nodos secundarios izquierdos de 9. Este último debe evaluarse. Si hay menos de 9, significa que no es un árbol de búsqueda binario, y devuelve falso directamente. . Luego, los subárboles izquierdo y derecho se juzgan recursivamente.

Veamos otro, su recorrido posterior es [3, 5, 13, 10, 12, 9].

Dividámoslo según el arreglo, el primero mayor que 9 es seguido por el hijo derecho de 9 [13, 10, 12]. Luego divida la matriz, 12 es el nodo raíz, el primero mayor que 12 es seguido por el nodo secundario derecho [13, 10] de 12, pero vemos que 10 es menor que 12, no puede ser 12 secundario derecho, entonces podemos estar seguros de que este árbol no es un árbol de búsqueda binaria. Después de comprender el principio anterior, veamos el código.

class Solution {
public:
    bool verifyPostorder(vector<int>& postorder) {
         return checkBSTPost(postorder,0,postorder.size()-1);
    }
    bool checkBSTPost(vector<int>&nums,int left,int right)
    {
        if(left>=right)
         return true;
         int index=right;//找到第一个比根节点小的树那么前面的及它自己都是左子树部分
         while(index>=left&&nums[index]>=nums[right])
         {
             index--;
         }
        int tmp=left;//检查前面的左子树部分是否有大于根节点
        while(tmp<index)
        {
            if(nums[tmp]>nums[right])
            {
                return false;
            }
            tmp++;
        }
          //递归检查左子树和右子树
        return checkBSTPost(nums,left,index)&&checkBSTPost(nums,index+1,right-1);

    }
};

Solución 2: pila monotónica

Todos sabemos que el recorrido posterior al orden es raíz izquierda y derecha si lo invertimos para ver si se convierte en raíz derecha izquierda. En este punto podemos usar una pila monótona (incrementando a la derecha).

Observe la estructura del árbol de búsqueda binario, verifique el recorrido posterior al pedido en orden inverso y el recorrido posterior al pedido en orden inverso: [nodo raíz | subárbol derecho | subárbol izquierdo]
Del nodo raíz al subárbol derecho al subárbol derecho del subárbol derecho... es una secuencia creciente, como 6, 8, 9.
Cuando ya no se incrementa, significa que aparece el nodo del subárbol izquierdo de un subárbol, como 7.
Ponemos los nodos continuamente crecientes en una pila, y cuando ya no hay Al aumentar el número de nodos, los nodos de la pila se extraen uno por uno, hasta que no haya ningún nodo más grande que él en la pila.
Luego, el último nodo extraído de la pila es su nodo principal, y el valor del nodo principal se actualiza continuamente. Cada vez que se juzga si el nodo actual es mayor que el valor del nodo principal, si es mayor de lo que es definitivamente mal

Código correspondiente:

class Solution {
public:
    bool verifyPostorder(vector<int>& postorder) {
          int Max=INT_MAX;
          stack<int>stk;
          for(int i=postorder.size()-1;i>=0;i--)
          {
              if(postorder[i]>Max)
              {
                  return false;
              }
              while(!stk.empty()&&postorder[i]<stk.top())
              {
                  Max=stk.top();
                  stk.pop();
              }
              stk.push(postorder[i]);
          }
          return true;
    }
};

Supongo que te gusta

Origin blog.csdn.net/qq_56999918/article/details/123979032
Recomendado
Clasificación