[来自妹纸的挑战]-展开/还原多层链表

前言:

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

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

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

 

过程:

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

  

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

  肯定是要从头开始遍历的,所以时间复杂度最少是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;
}

 

后记:

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

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

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

  

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

  妹纸会心一笑。

posted @ 2015-12-04 23:58  AnnsShadoW  阅读(1047)  评论(2编辑  收藏  举报