Árbol de directorio de análisis de problemas de PTA

Árbol de directorios


Al ver este problema, estamos confundidos, ¿qué dijiste? No se preocupe, simulemos con muestras de prueba.

Simulación de muestra

En primer lugar, primero consideramos la forma de almacenar datos. Según las observaciones y nuestra comprensión de las carpetas, para una carpeta, solo habrá 2 relaciones con otros archivos o carpetas, en el mismo directorio que yo, en mi directorio Es decir, solo hay dos relaciones en el mismo nivel y en el nivel inferior. Por lo tanto, naturalmente pensamos en la notación de hermano menor, porque el hermano menor solo puede tener dos tipos de relaciones para un nodo-hijo y hermano, luego usamos el hijo para representar en el directorio subordinado, y el hermano representa en el mismo directorio.

En primer lugar, tenemos una raíz del directorio raíz, lee la primera línea de datos, lo que indica que el directorio raíz tiene un archivo llamado b, que es un hijo de raíz, por lo que, según la notación de hermano menor, b debería ser el nodo de rama izquierdo de la raíz.

Lea la segunda línea de datos, hay un directorio c en el directorio raíz. Por lo tanto, el directorio c es hijo de root y está en el mismo nivel que el archivo b, es decir, una relación de hermano entre sí y se agrega el nodo.

Lea la tercera línea de datos, hay un directorio ab en el directorio raíz y un archivo cd en este directorio. Por lo tanto, el directorio ab es un hijo de root y está en el mismo nivel que el directorio c, es decir, una relación de hermano entre sí, y el directorio ab tiene una rama izquierda cd para unirse al nodo.

Lea la cuarta línea de datos, hay un directorio en el directorio raíz y hay un archivo bc en este directorio. Por lo tanto, el directorio a es un hijo de root, y está en el mismo nivel que el directorio ab, es decir, una relación de hermano entre sí. Al mismo tiempo, el directorio a tiene una rama izquierda bc para unir los nodos.

Lea la quinta línea de datos, hay un archivo d en el directorio ab. Como ab ya existe, d y cd son hermanos entre sí, modifique el hijo de ab a d, y el hermano de d a cd, para unirse al nodo.

Repita la operación anterior para completar la construcción del árbol.


Organice este árbol en forma de árbol binario.

Leímos este árbol de lecciones en el orden del recorrido anterior, y descubrimos que el orden de lectura es exactamente el mismo que el de los datos de salida de la muestra, ignorando la sangría, es decir, mientras el árbol esté construido, este escenario Lo solucionamos. En la aplicación, deberíamos considerar activamente usar la notación de hermano menor para construir un árbol, ya que este método construye un árbol binario, podemos usar la operación básica del árbol binario para operar este árbol.

Definición de estructura de nodo

typedef struct CSNode
{
    string data;    //数据域
    struct CSNode* firstchild;    //指向对应长子结点的指针域
    struct CSNode* rightsib;    //指向对应右兄弟结点的指针域
    int flag_file;    //判断是文件还是目录的 flag
}CSNode, * CSTree;
  • ¿Por qué usar int type aquí como flag en lugar de bool type? Esto se debe a que cambiar a tipo int es equivalente a dar prioridad directamente, y puede buscar directamente en el nodo con la misma prioridad al determinar la posición de inserción.

Algoritmo de construcción de árboles

Algoritmo de corte de cadenas

El algoritmo de corte se puede implementar usando una matriz de caracteres, o se puede implementar usando la clase string. Aquí uso una matriz de caracteres para describirlo. Como los datos que leemos son una cadena, primero debemos separar los distintos directorios. Cabe señalar que, aunque el archivo es información especial, el archivo solo aparecerá al final de la cadena, por lo que solo una estructura de ramificación debe procesarse por separado.

Pseudocódigo


Cabe destacar que la clase de cadena utilizada para determinar el orden lexicográfico y las operaciones de copia pueden implementarse directamente con los operadores, lo cual es más conveniente.

Implementación de código

Resultados de puesta en servicio

Pseudocódigo

El algoritmo de construcción de árboles solo necesita ser modificado por el algoritmo de corte de cadenas, y la declaración de salida puede implementarse llamando a la función de inserción de nodos.

Implementación de código

void createTree(CSTree pre, string str)
{
    int idx = 0;

    getline(cin, str);
    for (int i = 0; i < str.size(); i++)
    {
        if (str[i] == '\\')    //注意用反义字符,不然会报错
        {                              //只要不在串尾,只会是目录
            pre = insertNode(pre, str.substr(idx, i - idx), 1);
            idx = i + 1;    //移动字符串到下一个目录,即 '\' 之后
        }
    }
    if (idx < str.size())    //文件只出现在字符串尾
    {
        pre = insertNode(pre, str.substr(idx, str.size() - idx), 0);
    }
}

Algoritmo de inserción de nodos

El propósito de este algoritmo es insertar un nuevo nodo en la posición correcta en una estructura de árbol, que es el núcleo de la solución de este problema. Aquí debemos enfatizar la importancia del valor de retorno. Si no establece el valor de retorno, sino que se refiere al directorio, debe mover el puntero al directorio actual cuando regrese a la función de llamada, lo cual es más engorroso. Una buena solución es insertar el El contacto se usa como directorio y el valor de retorno se usa para devolver la ubicación de la llamada a la función. El lugar propenso a errores es que si la posición de inserción es el nodo secundario más antiguo del directorio, la operación directamente a través del sucesor del puntero precursor se insertará en la posición incorrecta y causará un enlace roto. Por lo tanto, debe establecer el puntero de posición actual y el puntero precursor para evitar este problema.

Pseudocódigo

Implementación de código

CSTree insertNode(CSTree t, string str, int flag)    //解决问题的核心
{
    CSTree a_node = new CSNode;
    CSTree pre = t, ptr;

    a_node->data = str;    //初始化新结点
    a_node->firstchild = a_node->rightsib = NULL;
    a_node->flag_file = flag;

    if (t->firstchild == NULL)    //所在目录没孩子,直接插入结点
    {
        t->firstchild = a_node;
        return t->firstchild;
    }

    ptr = t->firstchild;    //由于根结点本身插入时,是插在长子位,因此另外设置 pre 当前驱结点,ptr 当 pre 的后继
    while (ptr != NULL && ((ptr->flag_file > a_node->flag_file) || (ptr->flag_file == a_node->flag_file && str > ptr->data)))
    {
        pre = ptr;
        ptr = ptr->rightsib;    //同时移动 ptr 和其前驱,这么做的好处是不需要考虑长子结点和兄弟结点的关系
    }

    //要先判空,不然有段错误
    if (ptr == NULL)    //无处可插入,插在链尾
    {
        a_node->rightsib = pre->rightsib;
        pre->rightsib = a_node;
        return a_node;    //接下来以 a_node 为根目录操作
    }
    else if (ptr->data == a_node->data && ptr->flag_file == a_node->flag_file)    //目录或文件已存在(之前因为这个出了 bug)
    {
        delete a_node;    //把申请的新结点打掉
        return ptr;    //接下来在已有的 ptr 目录下操作
    }
    else    //找到了应该插入的位置
    {
        if (pre->data == t->data)    //插在根目录的长子位
        {
            a_node->rightsib = pre->firstchild;
            pre->firstchild = a_node;
        }
        else    //正常插入
        {
            a_node->rightsib = pre->rightsib;
            pre->rightsib = a_node;
        }
        return a_node;    //接下来以 a_node 为根目录操作
    }
}

Imprimir árbol de directorios

Ya sabemos que el orden de los nodos de salida es el orden de atravesar primero el árbol binario, por lo que solo necesitamos agregar un mecanismo de sangría para lograrlo.

void PreOrderTraverse(CSTree T, int space)
{                                 //因为要输出空格,稍微改装遍历算法
    if (T == NULL)
        return;
    for (int i = 0; i < space; i++)
    {
        cout << " ";
    }
    cout << T->data << endl;    //前序遍历
    PreOrderTraverse(T->firstchild, space + 2);    //下一层多两个空格
    PreOrderTraverse(T->rightsib, space);    //兄弟结点不需要多空格
}

Función principal

Muestra de prueba

Muestra de entrada

15
b
c\
ab\cd
a\bc
ab\d
a\d\a
a\d\z\
b\
c
ab\cd\e
a\bc\f
ab\d\g
a\d\a\h
a\d\z

Salida de muestra

root
  a
    bc
      f
    d
      a
        h
      z
      a
      z
    bc
  ab
    cd
      e
    d
      g
    cd
    d
  b
  c
  b
  c

Supongo que te gusta

Origin www.cnblogs.com/linfangnan/p/12617499.html
Recomendado
Clasificación