zoukankan      html  css  js  c++  java
  • [来自妹纸的挑战]-展开/还原多层链表

    前言:

      适逢妹纸在复习,顺便见我也在学习,便问了这样一个问题【她是怎么想到这个的】。

      有一个很多层的链表,嗯,双向的吧,然后每个节点还有个指针指向了它的子链表,单独的,如果有的话;同一层的子链表,嗯,也是双向链接的吧;然后怎么将它们比较快的变成只有一条主链表呢?关系还是保持不变的哦。对了,可以展开的话,顺便也还原回去吧。。。

      为了不在妹纸面前怂,毅然决然拿起纸币就左图右画了起来,经过快二十分钟的左移右移,加上点必要代码,算是给妹纸解释好了,差点就跪了。

     

    过程:

      首先是双向的,那就好办多了,之前遇到的很多问题都是单向的,先画个多层链表示意图好了;问妹纸是不是这样的,她说差不多。。。

      

      然后想一下以什么样的顺序存放吧,可以是父子父子这样一直连下去的,也可以是横着过去,一行一行的放的,感觉一层一层来改变的顺序要少一点,那就那样吧。

      肯定是要从头开始遍历的,所以时间复杂度最少是O(n);如果有子链表就把它追加到现有的尾指针那里;那么新的尾指针应该在哪里呢?新加的那一个子节点?不,不是那个,因为我们要把整一层都放在一起,那样的话,就要子链表的最后一个节点作为新的尾指针了,那样一整块就被追加过去了。

      接着next下去继续遍历,同样是有子链表就追加,原第一层的完了就从追加的节点继续,然后一层一层就连起来了。

      

      展开了,是时候考虑一下怎么变回去了?!

      逆向思维的话就是从尾部开始遍历回去,反正是双向的,但是从尾部开始的话,该怎么判定这个节点是不是子节点呢?要确认的话估计只能从头指针开始遍历匹配是谁的子节点了,那样务必要遍历多次,不太科学;

      也可以是用东西把所有的子节点都存起来,那样从尾部遍历就不用每次都遍历了,但是又要多用东西耶,可能还有更好的方法;

      要不还是从头开始!

      遍历链表,如果有子链表就将它从主链表那里切断开来,但是又不能就这样返回了,不然到了原来第一层那里就没后续节点了,那就只能分离第一层的子链表了;对了,那就递归,从子链表那继续切断,如果有它也有子链表的话。

      那么尾指针怎么安放好呢?想想因为它会不断从后面分离,不影响前面的,那就直接将遍历到的最后一个作为尾指针好了,到时候就是到原第一层的末尾了。

      既然想好了,那就试试实现吧。

    代码:

      

    /*********************
        Author:AnnsShadow
        Time = 2015-12-03
    *********************/
    
    #include <stdio.h>
    #include <stdlib.h>
    
    //每个节点的定义
    typedef struct chainNode
    {
        struct chainNode *next;
        struct chainNode *previous;
        struct chainNode *child;
        int value;
    } chainNode;
    
    /**
    为什么tail要用指向指针的指针,因为要修改它啊!
    不然在函数里面改了影响不了原来的哦!
    **/
    
    //在链表末尾追加链接
    void appendChain(chainNode *child, chainNode **tail);
    //展开多层链表,使之成为一层
    void expandChain(chainNode *head, chainNode **tail);
    //将子链表从一层链表中分离
    void seperateChild(chainNode *childHead);
    //将一层链表还原成多层链表
    void unexpandChain(chainNode *head, chainNode **tail);
    
    int main()
    {
        chainNode *head, *tail;
        //构造对应的测试多层链表
        /**
            Some codes;
        **/
        expandChain(head, &tail);
        unexpandChain(head, &tail);
        return 0;
    }
    
    void appendChain(chainNode *child, chainNode **tail)
    {
        //将当前指针赋值为传入的子链表
        chainNode *currentNode = child;
        //原尾指针的下一个赋值为传入的子链表
        (*tail)->next = child;
        //子链表的上一个赋值为原尾指针
        child->previous = *tail;
        //遍历当前子链表直到最后
        for(; currentNode->child; currentNode = currentNode->next);
        //赋值产生新的尾指针
        *tail = currentNode;
    }
    
    void expandChain(chainNode *head, chainNode **tail)
    {
        //将当前指针指向链表头指针
        chainNode *currentNode = head;
        //当前指针不为NULL
        while(currentNode)
        {
            //如果当前指针有子链表
            if(currentNode->child)
            {
                //将其追加到链表的尾部
                appendChain(currentNode->child, tail);
            }
            //指向下一个指针
            currentNode = currentNode->next;
        }
    }
    
    void seperateChild(chainNode *childHead)
    {
        //将当前指针赋值为子链表的头指针
        chainNode *currentNode = childHead;
        //当前指针不为空
        while(currentNode)
        {
            //如果当前指针有子链表
            if(currentNode->child)
            {
                //将之前追加到指针‘下一个’的值取消
                //也就是切断链表
                currentNode->child->previous->next = NULL;
                //子链表从主链表分离
                currentNode->child->previous = NULL;
                //如果当前指针有子链表则继续分离
                seperateChild(currentNode->child);
            }
            //指向下一个指针
            currentNode = currentNode->next;
        }
    }
    
    void unexpandChain(chainNode *head, chainNode **tail)
    {
        //将当前指针赋值为主链表头指针
        chainNode *currentNode = head;
        //开始分离
        seperateChild(head);
        //遍历主链表到末尾
        for(; currentNode->next; currentNode = currentNode->next);
        //重新赋值尾指针
        *tail = currentNode;
    }

    后记:

      妹纸觉得答案挺满意的,然后就问,要是是单向呢?

      我就答,那就记住每次遍历到哪里好了;

      再问,要是多层的结构是这样的呢?

      

      我再答:那样连起来啊,也是有子链表就追加,而且可以一次追加整一层,不过要记住其中的有子链表的位置,那样好像又有开销了,诶,记住第一个就行了,反正一层都连起来了。

      妹纸会心一笑。

  • 相关阅读:
    (一)Oracle学习笔记—— 表和表空间
    svn上检出的项目在myeclipse中报错的解决
    创建表空间报错
    PLSQL中scott账户登录不上,报错ORA-01017: invalid username/password; logon denied
    Oracle安装过程中的几点注意
    Refactoring之——代码的坏味道(二)过大的类 &(三)基本类型偏执
    Refactoring之——代码的坏味道(一)过长方法
    (一)Activiti之——简介、插件安装及BPMN元素
    用尽洪荒之力整理的Mysql数据库32条军规(转)
    技术文章翻译
  • 原文地址:https://www.cnblogs.com/annsshadow/p/5020861.html
Copyright © 2011-2022 走看看