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

前言:app

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

  有一个不少层的链表,嗯,双向的吧,而后每一个节点还有个指针指向了它的子链表,单独的,若是有的话;同一层的子链表,嗯,也是双向连接的吧;而后怎么将它们比较快的变成只有一条主链表呢?关系仍是保持不变的哦。对了,能够展开的话,顺便也还原回去吧。。。学习

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

 

过程:spa

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

  

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

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

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

  

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

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

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

  要不仍是从头开始!

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

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

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

 

代码:

  

/*********************
    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;
}

 

后记:

  妹纸以为答案挺满意的,而后就问,要是是单向呢?

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

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

  

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

  妹纸会心一笑。

相关文章
相关标签/搜索