前言: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; }
后记:
妹纸以为答案挺满意的,而后就问,要是是单向呢?
我就答,那就记住每次遍历到哪里好了;
再问,要是多层的结构是这样的呢?
我再答:那样连起来啊,也是有子链表就追加,并且能够一次追加整一层,不过要记住其中的有子链表的位置,那样好像又有开销了,诶,记住第一个就好了,反正一层都连起来了。
妹纸会心一笑。