• DS博客作业03--树


    0.PTA得分截图

    1.本周学习总结(5分)

    1.1 二叉树结构

    1.1.1 二叉树的2种存储结构

    顺序存储:顺序存储方法它是把逻辑上相邻的结点存储在物理位置相邻的存储单元里,结点间的逻辑关系由存储单元的邻接关系来体现,由此得到的存储表示称为顺序存储结构。顺序存储结构是一种最基本的存储表示方法,通常借助于程序设计语言中的数组来实现。

    链式存储结构:链接存储方法它不要求逻辑上相邻的结点在物理位置上亦相邻,结点间的逻辑关系是由附加的指针字段表示的。由此得到的存储表示称为链式存储结构,链式存储结构通常借助于程序设计语言中的指针类型来实现。

    优缺点:

    一、顺序存储
    优点:读取某个指定的节点的时候效率比较高O(0)
    缺点:会浪费空间(在非完全二叉树的时候)
    二、链式存储
    优点:读取某个指定节点的时候效率偏低O(nlogn)
    缺点:相对二叉树比较大的时候浪费空间较少
    二叉树的顺序存储,寻找后代节点和祖先节点都非常方便,但对于普通的二叉树,顺序存储浪费大量的存储空间,同样也不利于节点的插入和删除。因此顺序存储一般用于存储完全二叉树。
    链式存储相对顺序存储节省存储空间,插入删除节点时只需修改指针,但寻找指定节点时很不方便。不过普通的二叉树一般是用链式存储结构。

    1.1.2 二叉树的构造

    利用先序遍历序列和中序遍历序列

    大概思路为利用先序遍历先遍历根结点的特点,记录根结点,再去中序序列中找到根结点位置。记录其在中序序列中的位置,中序序列根结点左边为左子树,右边为右子树。再通过递归完成树的构造

    Tree CreatTree( char Pre[],char Mid[],int n)
    {
        if( n==0 )
            return NULL;
        int index = 0;
        Tree root = (Tree) malloc(sizeof(struct TNode));
        while( index < n)//找到根结点
        {
            if( Mid[index]==Pre[0])
                break;
            index ++;
        }
        root->data = Pre[0];
        root->lchild = CreatTree(Pre+1,Mid,index);
        root->rchild = CreatTree(Pre+1+index,Mid+index+1,n-index-1);
        return root;
    }
    

    后序遍历序列和中序遍历序列构造二叉树。

    大概思路和利用先序遍历序列和中序遍历序列一样,利用后序序列找到根结点,再去中序序列中找到根结点位置。记录其在中序序列中的位置,中序序列根结点左边为左子树,右边为右子树。再通过递归完成树的构造

    void getpre(int* pos, int* mid, int n)  
    {
        int i;
        if (n > 0)
        {
            int root = pos[n - 1];  //根结点为后序遍历的最后一个
            for ( i = 0; i < n; i++)    //在中序遍历中查找根结点
            {
                if (mid[i] == root)
                {
                    break;
                }
            }
            cout << " " << root;
            getpre(pos, mid, i);      
            getpre(pos + i, mid + i + 1, n - i - 1);  
        }
    }
    

    1.1.3 二叉树的遍历

    先序,中序,后序遍历都可以通过递归实现

    先序遍历

    void PreOrder(BTNode * b)//先序遍历递归算法
    {
    if (b!= NULL)
        {
          printf("%c",b-> data) ;//访问根结点
          PreOrder(b-> lchild);//先序遍历左子树
          PreOrder(b-> rchild);//先序遍历右子树
        }
    }
    

    中序遍历

    void InOrder( BTNode * b)//中序遍历递归算法
    {
         if (b!=NULL)
         {
          InOrder(b -> lchild);//中序遍历左子树
          printf("%c ",b-> data)//访问根结点
          InOrder(b -> rchild) ;//中序遍历右子树
         }
    }
    

    后序遍历

    void PostOrder(BTNode * b)//后序遍历递归算法
    {
         if (b!=NULL)
         {
          PostOrder(b > lchild);//后序遍历左子树
          PostOrder(b-> rchild);//访问根结点
          printf("%c ",b-> data);//后序遍历右子树
         }
    }
    

    层次遍历

    层次遍历通过队列结果实现,树中元素出入队列实现遍历

    void PrintTree(BTree BT)//层次遍历二叉树
    {
    	BTree p;//遍历二叉树
    	queue<BTree>qu;
    	qu.push(BT);//根结点进栈
    	while (!qu.empty())//若队列不为空
    	{
    	   p = qu.front();//第一个元素出栈
    	   qu.pop();
    	   cout << p->data;
    	   if (p->lchild != NULL)//若出栈元素有左右子结点,进栈
    		 qu.push(p->lchild);
    	   if (p->rchild != NULL)
    		 qu.push(p->rchild);
    	}
    }
    

    1.1.4 线索二叉树

    对于n个结点的二叉树,在二叉链存储结构中有n+1个空链域,利用这些空链域存放在某种遍历次序下该结点的前驱结点和后继结点的指针,这些指针称为线索,加上线索的二叉树称为线索二叉树。
    这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree)。根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种。

    中序线索二叉树


    先找到最左边的节点,然后判断其右子树是否为线索,如果是线索,那么就遍历后继结点,如果右子树是右孩子,那么就进入到右孩子的最左边的节点,进行同样的判断,直到遍历完了整棵树为止。

    void traverThiTree(binThiTree* head) {
    	binThiTree* p = head->lchild;//头结点的左子树即二叉树的根节点
    	while (p != head) {//判断遍历是否结束
    		while (p->LTag == false) {  //找打二叉树的最左边的点
    			p = p->lchild;
    		}
    		printf("%d",p->date);//此时p指向最左边的节点
    		while (p->RTag == true && p->rchild != head) {
    			p = p->rchild;//指向后继
    			printf("%d",p->date);
    		}
    		p = p->rchild;
    	//右子树是右孩子,先到右子树,进入下一个循环,找到右子树的最左边结点
    	}
    }
    

    1.1.5 二叉树的应用--表达式树

    伪代码

    //创建两个栈一个保存树节点,一个是字符保存栈
    for  0 to i
        if(字符是数字)创建树节点并且入栈
        else
           if(字符栈栈顶优先级小于str[i]){
           则进栈字符栈
           else if(字符栈栈顶优先级大于str[i]){
            出栈并且从节点栈中拿出两个;
            构树并且放回节点栈中
        else
       直接出栈
    

    计算表达式

       if  左右子树不空
          return BT->data-'0'//转为数字
    num1=计算遍历右树
    num2=计算遍历左树
    switch()
    case '+':相加
    case '*':相减
    case '*':相乘
    case '/':相除//考虑num2为0的情况
    

    代码




    1.2 多叉树结构

    1.2.1 多叉树结构

    (1)双亲存储结构

    结构体定义:

    typedef struct 
    {
       ElemType data;    //结点的值
       int parent;     //指向双亲的位置
    }PTree[MaxSize];
    

    缺点:父亲容易找到,孩子不容易找到

    (2)孩子链存储结构

    结构体定义:

    typedef struct node
    {
       ElemType data;    //结点的值
       struct tnode *sons[MaxSons];  //指向孩子结点
    }TSonNode;
    缺点:空指针太多,造成空间浪费,找父亲不容易
    

    (3)孩子兄弟链存储结构

    一棵树无论有多少叉,它最多有一个长子和一个排序恰在其下的兄弟。根据这样的定
    义,则每个结点的结构就都统一到了二叉链表结构上。这样有利于对结点进行操作。
    孩子兄弟链存储结构是为每个结点设计3个域:
    一个数据元素域
    第一个孩子结点指针域
    一个兄弟结点指针域
    结构体定义:

    typedef struct tnode
    {
       ElemType data;    //结点的值
       struct tnode *son;   //指向兄弟
       struct tnode *brother;   //指向孩子结点
    }TSBNode;
    

    1.2.2 多叉树遍历

    二叉树的前序遍历中直接右子节点先入栈然后左子节点入栈,在多叉树中,某个节点存在多个子节点,使用for循环逆序遍历入栈即可。
    如果需要考虑后续遍历,需要修改两个地方:
    1、增加一个栈
    2、对于子树我们使用for循环顺序遍历入栈即可

    class Solution {
        public List<Integer> preorder(Node root) {
            if(root == null) return new ArrayList<>();
            Stack<Node> stack = new Stack<>();
            List<Integer> result = new ArrayList<>();
            stack.push(root);
            while(!stack.isEmpty()){
                Node node = stack.pop();
                result.add(node.val);
                if (node.children != null && !node.children.isEmpty()) {
                    // 应该倒过来将子节点放入,因为在上文中使用的数据结构是栈
                    for (int i = node.children.size() - 1; i >= 0; i--) {
                        stack.push(node.children.get(i));
                    }
                }
              } 
            return result;
        }
    }
    

    1.3 哈夫曼树

    1.3.1 哈夫曼树定义(为数据压缩而生。)

    在许多应用中经常将树中的结点赋予一个有某种意义的数值,称此数值为该结点的权。从根结点到该结点之间的路径长度与该结点上权的乘积称为结点的带权路径长度Weighted Path Length,WPL). 树中所有叶子结点的带权路径长度之和你为该树的带权路径长度
    其中.no表示叶子结点的个数,W;和l;分别表示第i个叶子结点的权值和
    根到它之间的路径长度(即从根结点到该叶子结点的路径上经过的分支数)。
    mo个带权叶子结点构成的所有二又树中,带权路径长度WPL最小的二叉树称为哈夫曼树(Huffman tree)或最优二叉树、因为构造这种树的算法最早是由哈夫曼于1952年提出的,所以用他的名字命名。
    例如,给定4个叶子结点,设其权值分别为1、3、5、7,可以构造出形状不同的4棵二叉树,如图7.29所示。它们的带权路径长度分别如下:
    (a) WPL=1X2+3X2+5X2+7X2=32
    (b) WPL=1X2+3X3+5X3+7X1= 33
    (c) WPL=7X3+5X3+3X2+1X1=43
    (d) WPL=1X3+3X3+5X2+7X1= 29

    由此可见,对于一组具有确定权值的叶子结点可以构造出多个具有不同带权路径长度的二叉树。可以证明,图7. 29(d)所示的二叉树是一棵哈夫曼树,它的带权路径长度最小。

    1.3.2 哈夫曼树的结构体

    结构体定义:
    typedef struct
    {
    char data;//结点值
    int weight;//权重
    int parent;//双亲结点
    int lchild;//左孩子结点
    int rchild;//右孩子结点
    }HTNode, * HTree;

    1.3.3 哈夫曼树构建及哈夫曼编码

    给定的n个权值存在hf中
    从hf中选出权值最小和次小的两个数据作为左右结点,构造成新节点,新节点的权值等于左右节点之和
    将最小和次小的两个数据删除,并将新结点的权值加到hf中
    重复上面操作,直到最后剩余一个节点

    void CreatHFT(HTNode hf[], int n)//建立哈夫曼树
    {
        int min1, min2, lnode, rnode;
        for (int i = 0; i < 2 * n - 1; i++)//初始化
            hf[i].parent = hf[i].lchild = hf[i].rchild = -1;
        for (int i = n; i < 2 * n - 1; i++) {
            min1 = min2 = MAX;//找到最小和次小
            lnode = rnode = -1;//最小和次小对应下标
            for (int j = 0; j <= i - 1; j++) {
                if (hf[j].parent == -1) {
                    if (hf[j].weight < min1) {
                        min2 = min1;
                        min1 = hf[j].weight;
                        lnode = j;
                    }
                    else if (hf[j].weight < min2) {
                        min2 = hf[j].weight;
                        rnode = j;
                    }
                }
                hf[lnode].parent = hf[rnode].parent = i;
                hf[i].weight = hf[lnode].weight + hf[rnode].weight;
                hf[i].lchild = lnode; hf[i].rchild = rnode;
            }
        }
    }
    

    哈夫曼编码

    由于哈夫曼树中没有度为1的结点,则一棵有n个叶子的哈夫曼树共有2×n-1个结点,可以用一个大小为2×n-1 的一维数组存放哈夫曼树的各个结点。 由于每个结点同时还包含其双亲信息和孩子结点的信息,所以构成一个静态三叉链表。

      1 //haffman 树的结构
      2 typedef struct
      3 {
      4     //叶子结点权值
      5     unsigned int weight;
      6     //指向双亲,和孩子结点的指针
      7     unsigned int parent;
      8     unsigned int lChild;
      9     unsigned int rChild;
     10 } Node, *HuffmanTree;
     11 
     12 //动态分配数组,存储哈夫曼编码
     13 typedef char *HuffmanCode;
     14 
     15 //选择两个parent为0,且weight最小的结点s1和s2的方法实现
     16 //n 为叶子结点的总数,s1和 s2两个指针参数指向要选取出来的两个权值最小的结点
     17 void select(HuffmanTree *huffmanTree, int n, int *s1, int *s2)
     18 {
     19     //标记 i
     20     int i = 0;
     21     //记录最小权值
     22     int min;
     23     //遍历全部结点,找出单节点
     24     for(i = 1; i <= n; i++)
     25     {
     26         //如果此结点的父亲没有,那么把结点号赋值给 min,跳出循环
     27         if((*huffmanTree)[i].parent == 0)
     28         {
     29             min = i;
     30             break;
     31         }
     32     }
     33     //继续遍历全部结点,找出权值最小的单节点
     34     for(i = 1; i <= n; i++)
     35     {
     36         //如果此结点的父亲为空,则进入 if
     37         if((*huffmanTree)[i].parent == 0)
     38         {
     39             //如果此结点的权值比 min 结点的权值小,那么更新 min 结点,否则就是最开始的 min
     40             if((*huffmanTree)[i].weight < (*huffmanTree)[min].weight)
     41             {
     42                min = i;
     43             }
     44         }
     45     }
     46     //找到了最小权值的结点,s1指向
     47     *s1 = min;
     48     //遍历全部结点
     49     for(i = 1; i <= n; i++)
     50     {
     51         //找出下一个单节点,且没有被 s1指向,那么i 赋值给 min,跳出循环
     52         if((*huffmanTree)[i].parent == 0 && i != (*s1))
     53         {
     54             min = i;
     55             break;
     56         }
     57     }
     58     //继续遍历全部结点,找到权值最小的那一个
     59     for(i = 1; i <= n; i++)
     60     {
     61         if((*huffmanTree)[i].parent == 0 && i != (*s1))
     62         {
     63             //如果此结点的权值比 min 结点的权值小,那么更新 min 结点,否则就是最开始的 min
     64             if((*huffmanTree)[i].weight < (*huffmanTree)[min].weight)
     65             {
     66                min = i;
     67             }
     68         }
     69     }
     70     //s2指针指向第二个权值最小的叶子结点
     71     *s2 = min;
     72 }
     73 
     74 //创建哈夫曼树并求哈夫曼编码的算法如下,w数组存放已知的n个权值
     75 void createHuffmanTree(HuffmanTree *huffmanTree, int w[], int n)
     76 {
     77     //m 为哈夫曼树总共的结点数,n 为叶子结点数
     78     int m = 2 * n - 1;
     79     //s1 和 s2 为两个当前结点里,要选取的最小权值的结点
     80     int s1;
     81     int s2;
     82     //标记
     83     int i;
     84     // 创建哈夫曼树的结点所需的空间,m+1,代表其中包含一个头结点
     85     *huffmanTree = (HuffmanTree)malloc((m + 1) * sizeof(Node));
     86     //1--n号存放叶子结点,初始化叶子结点,结构数组来初始化每个叶子结点,初始的时候看做一个个单个结点的二叉树
     87     for(i = 1; i <= n; i++)
     88     {
     89        
     90         //其中叶子结点的权值是 w【n】数组来保存
     91         (*huffmanTree)[i].weight = w[i];
     92         //初始化叶子结点(单个结点二叉树)的孩子和双亲,单个结点,也就是没有孩子和双亲,==0
     93         (*huffmanTree)[i].lChild = 0;
     94         (*huffmanTree)[i].parent = 0;
     95         (*huffmanTree)[i].rChild = 0;
     96     }// end of for
     97     //非叶子结点的初始化
     98     for(i = n + 1; i <= m; i++)
     99     {
    100         (*huffmanTree)[i].weight = 0;
    101         (*huffmanTree)[i].lChild = 0;
    102         (*huffmanTree)[i].parent = 0;
    103         (*huffmanTree)[i].rChild = 0;
    104     }
    105     
    106     printf("
     HuffmanTree: 
    ");
    107     //创建非叶子结点,建哈夫曼树
    108     for(i = n + 1; i <= m; i++)
    109     {
    110         //在(*huffmanTree)[1]~(*huffmanTree)[i-1]的范围内选择两个parent为0
    111         //且weight最小的结点,其序号分别赋值给s1、s2
    112         select(huffmanTree, i-1, &s1, &s2);
    113         //选出的两个权值最小的叶子结点,组成一个新的二叉树,根为 i 结点
    114         (*huffmanTree)[s1].parent = i;
    115         (*huffmanTree)[s2].parent = i;
    116         (*huffmanTree)[i].lChild = s1;
    117         (*huffmanTree)[i].rChild = s2;
    118         //新的结点 i 的权值
    119         (*huffmanTree)[i].weight = (*huffmanTree)[s1].weight + (*huffmanTree)[s2].weight;
    120         
    121         printf("%d (%d, %d)
    ", (*huffmanTree)[i].weight, (*huffmanTree)[s1].weight, (*huffmanTree)[s2].weight);
    122     }
    123     
    124     printf("
    ");
    125 }
    126 
    127 //哈夫曼树建立完毕,从 n 个叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码
    128 void creatHuffmanCode(HuffmanTree *huffmanTree, HuffmanCode *huffmanCode, int n)
    129 {
    130     //指示biaoji
    131     int i;
    132     //编码的起始指针
    133     int start;
    134     //指向当前结点的父节点
    135     int p;
    136     //遍历 n 个叶子结点的指示标记 c
    137     unsigned int c;
    138     //分配n个编码的头指针
    139     huffmanCode=(HuffmanCode *)malloc((n+1) * sizeof(char *));
    140     //分配求当前编码的工作空间
    141     char *cd = (char *)malloc(n * sizeof(char));
    142     //从右向左逐位存放编码,首先存放编码结束符
    143     cd[n-1] = '';
    144     //求n个叶子结点对应的哈夫曼编码
    145     for(i = 1; i <= n; i++)
    146     {
    147         //初始化编码起始指针
    148         start = n - 1;
    149         //从叶子到根结点求编码
    150         for(c = i, p = (*huffmanTree)[i].parent; p != 0; c = p, p = (*huffmanTree)[p].parent)
    151         {
    152             if( (*huffmanTree)[p].lChild == c)
    153             {
    154                 //从右到左的顺序编码入数组内
    155                  cd[--start] = '0';  //左分支标0
    156             }
    157             else
    158             {
    159                 cd[--start] = '1';  //右分支标1
    160             }
    161         }// end of for
    162         //为第i个编码分配空间
    163         huffmanCode[i] = (char *)malloc((n - start) * sizeof(char));
    164         
    165         strcpy(huffmanCode[i], &cd[start]);
    166     }
    167     
    168     free(cd);
    169     //打印编码序列
    170     for(i = 1; i <= n; i++)
    171     {
    172          printf("HuffmanCode of %3d is %s
    ", (*huffmanTree)[i].weight, huffmanCode[i]);
    173     }
    174     
    175     printf("
    ");
    176 }
    177 
    178 int main(void)
    179 {
    180     HuffmanTree HT;
    181     HuffmanCode HC;
    182     int *w,i,n,wei,m;
    183     
    184     printf("
    n = " );
    185     
    186     scanf("%d",&n);
    187     
    188     w=(int *)malloc((n+1)*sizeof(int));
    189     
    190     printf("
    input the %d element's weight:
    ",n);
    191     
    192     for(i=1; i<=n; i++)
    193     {
    194         printf("%d: ",i);
    195         fflush(stdin);
    196         scanf("%d",&wei);
    197         w[i]=wei;
    198     }
    199     
    200     createHuffmanTree(&HT, w, n);
    201     creatHuffmanCode(&HT,&HC,n);
    202     
    203     return 0;
    204 }
    

    1.4 并查集

    并查集的定义

    并查集支持查找一个元素所属的集合以及两个元素各自专属的集合的合并等运算。在这种数据类型中,n个不同的元素被分为若干组。每组是一个集合,这种集合叫分离集合,称之为并查集。

    并查集是一种树形结构,又叫“不相交集合”,保持了一组不相交的动态集合,每个集合通过一个代表来识别,代表即集合中的某个成员,通常选择根结点做这个代表。

    并查集:(union-find sets)是一种简单的用途广泛的集合. 并查集是若干个不相交集合,能够实现较快的合并和判断元素所在集合的操作,应用很多,如其求无向图的连通分量个数、最小公共祖先、带限制的作业排序,还有最完美的应用:实现Kruskar算法求最小生成树。

    并查集的结构体、查找、合并操作

    结构体

    typedef struct node {
    	int data;//结点对应的编号
    	int rank;//结点秩,子树的高度
    	int parent;//结点对应的双亲下表
    }UFSTree;
    

    初始化

    void MakeSet(UFSTree t[], int n)//初始化
    {
    	for (int i = 1; i <= n; i++) {
    		t[i].data = i;
    		t[i].rank = 0;
    		t[i].parent = i;
    	}
    }
    

    查找

    int FindSet(UFSTree t[], int x)//找双亲
    {
    	if (x != t[x].parent)return FindSet(t, t[x].parent);
    	else return x;
    }
    

    合并

    void Union(UFSTree t[],int x, int y)//合并x,y
    {
    	x = FindSet(t, x);
    	y = FindSet(t, y);
    	if (t[x].rank > t[y].rank)t[y].parent = x;
    	else {
    		t[x].parent = y;
    		if (t[x].rank == t[y].rank)
    			t[y].rank++;
    	}
    }
    

    1.5.谈谈你对树的认识及学习体会。

    1.树是非线性结构,也需要线性结构进行辅助完成,例如层次遍历需要队列,表达式二叉树需要栈进行辅助
    2.我们借助了树结构,建立了目录,表达式树...通过哈夫曼树,并查集,解决了更复杂的问题。知识学习的越来越多,对于我们吸收并运用的能力也在逐步加深,也需要不断理解知识。
    3.树的代码方面因为用到递归所以减少了不少代码量;在哈夫曼树解决修理牧场问题时,很巧妙的可以用到优先队列来解决,很大解决建树麻烦的问题;
    4.多动手画图,才能真正理解,懂得怎么遍历实现的

    2.PTA实验作业(4分)

    https://gitee.com/fu-zhaoyang/codes/wdoji24q7p3z0mfhlure970

    2.1 二叉树

    输出二叉树每层节点

    2.1.1 解题思路及伪代码

    解题思路
    先先序遍历二叉树,再进行层次遍历,输出每层结点需判断是否为第一层结点
    伪代码

    void LevelOrder(BTree bt)//层次遍历
    	定义变量层数和flag判断是否为第一层结点
    	BTree node, lastNode 分别用于存放遍历中途结点的孩子结点并判断是否找到这一层的最后一个结点
    	node = lastNode = bt;
    	用队列来存放结点
    	二叉树不为空,则出队
        二叉树为空 NULL
    	while (!qtree.empty())//队列不空
    		若找到这一层的最后一个结点
    			层层递增
    			(格式控制输出)
    			取队尾元素
            取队首元素
    		左右孩子入队
    

    2.1.2 总结解题所用的知识点

    1.运用了#include 库函数,借助队列的知识进行存放元素。
    2.运用两个指针记住当前结点位置和每层的最后一个结点位置,以便于当当前结点等于层最后结点时,输出换行并输出下一层结点;
    3.对先序遍历和中序遍历的使用
    4.先序遍历递归建树

    2.2 目录树

    在ZIP归档文件中,保留着所有压缩文件和目录的相对路径和名称。当使用WinZIP等GUI软件打开ZIP归档文件时,可以从这些信息中重建目录的树状结构。请编写程序实现目录的树状结构的重建工作。
    输入格式:
    输入首先给出正整数N(≤10^4),表示ZIP归档文件中的文件和目录的数量。随后N行,每行有如下格式的文件或目录的相对路径和名称(每行不超过260个字符):路径和名称中的字符仅包括英文字母(区分大小写);符号“”仅作为路径分隔符出现;目录以符号“”结束;不存在重复的输入项目;整个输入大小不超过2MB。
    输出格式:
    假设所有的路径都相对于root目录。从root目录开始,在输出时每个目录首先输出自己的名字,然后以字典序输出所有子目录,然后以字典序输出所有文件。注意,在输出时,应根据目录的相对关系使用空格进行缩进,每级目录或文件比上一级多缩进2个空格。

    2.2.1 解题思路及伪代码

    解题思路:

    设计目录树结构体
    进行初始化树,建根结点
    对输入的字符串进行切割,分离文件名和目录名
    对应产生树结点,插入建立目录树。

    伪代码:

    对结构体进行定义;
    初始化树,建立根节点root;
    输入字符串,进行建树;

    void CreatTree(Tree& bt, string str, int i)//建树,
        新建结点temp,ptr;初始化结点;
        切割字符串;新节点name为该段字符;
           if 该段字符串为目录,isfile改为false;
           if (temp为文件)
               InitFile(temp, bt);//插入文件
           else //temp为目录
               InitList(temp, bt);//插入目录
        CreatTree(temp, str, i);
    
    void InitList(Tree& temp, Tree& bt)//插入目录
        定义结构体指针btr来遍历二叉树bt;
            btr = bt->child;//btr先指向bt的孩子;
    
            /*先对第一个兄弟结点进行判断*/
        if (没有第一个孩子|| btr为文件 || 第一个孩子字典序大于该结点)//可插入
            进行插入temp->brother = btr;bt->child = temp;//修改孩子指针
        else if (二者相等)
            直接使temp指向btr;
        else //查找兄弟节点
            while (btr->brother != NULL)
                if (兄弟节点为文件 || 兄弟节点字典序大于该节点)
                    找到可插入位置,break;
                else if (二者相等)
                    直接使temp指向btr->brother;break;
                else
                    btr = btr->brother;//遍历下一兄弟结点;
            if (btr->brother为空 || btr->brother->name != temp->name)
                进行插入操作:temp->brother = btr->brother;btr->brother = temp;
    
    
    void InitFile(Tree& temp, Tree& bt)//对文件temp找一个可插入位置
    
        定义结构体指针btr来遍历二叉树bt;
            btr = bt->child;//btr先指向bt的孩子;
        if (第一个孩子为空 || btr为文件 && 结点字典序大于等于该节点)
            进行插入,修改bt的孩子指针;
        else //判断兄弟结点
            while (btr->brother != NULL)
                if (btr->brother为文件 && 兄弟节点字典序大于该节点)
                    找到可插入位置,break;
                else
                    btr = btr->brother;//遍历下一个兄弟结点
            对temp进行插入操作:temp->brother = btr->brother;btr->brother = temp;
    

    2.2.2 总结解题所用的知识点

    1.结构体中增加isfile判断一个结点是目录还是文件
    2.目录与文件使用两个函数插入,分类讨论
    3.需要考虑是否有第一个孩子,孩子是目录还是文件,二者字典序大小等情况
    4.对孩子兄弟链的使用

    3.阅读代码(0--1分)

    3.1 题目及解题代码

    代码

    //步骤:先序遍历两棵二叉树的同时进行值相加
    struct TreeNode* mergeTrees(struct TreeNode* t1, struct TreeNode* t2) {
        
        if(t1 == NULL && t2 == NULL )
        {
            return NULL;
        }
        else if(t1 == NULL)
        {
            return t2;
        }
        else if(t2 == NULL )
        {
            return t1;
        }
        else
        {
            t1->val += t2->val;
            t1->left = mergeTrees(t1->left,t2->left);
            t1->right = mergeTrees(t1->right,t2->right);
        }
        
        return t1;
    }
    

    3.2 该题的设计思路及伪代码

    思路

    利用递归的思想,将左右子树的值相加后输出

    伪代码

        if 两子树都为空
            返回 NULL
        else if t1 为空
            返回 t2
        else if t2为空
            返回 t1
        else//都不为空
           相加
          递归访问两个树的左右子树
    

    3.3 分析该题目解题优势及难点。

    优势:思路清晰,代码简洁
    难点:对函数递归的利用

  • 相关阅读:
    面试官是如何筛选简历?
    成为一名架构师得学习哪些知识?
    一个对话让你明白架构师是做什么的?
    教你一招用 IDE 编程提升效率的骚操作!
    80个让你笑爆肚皮的程序员段子,不好笑算我输!
    Java初学者最佳的学习方法以及会遇到的坑(内含学习资料)!
    作为程序员的你,一年看几本技术相关的书
    MEF 插件式开发之 DotNetCore 中强大的 DI
    MEF 插件式开发之 DotNetCore 初体验
    读 《CSharp Coding Guidelines》有感
  • 原文地址:https://www.cnblogs.com/f2002/p/14725202.html
Copyright © 2020-2023  润新知