前言:
适逢妹纸在复习,顺便见我也在学习,便问了这样一个问题【她是怎么想到这个的】。
有一个很多层的链表,嗯,双向的吧,然后每个节点还有个指针指向了它的子链表,单独的,如果有的话;同一层的子链表,嗯,也是双向链接的吧;然后怎么将它们比较快的变成只有一条主链表呢?关系还是保持不变的哦。对了,可以展开的话,顺便也还原回去吧。。。
为了不在妹纸面前怂,毅然决然拿起纸币就左图右画了起来,经过快二十分钟的左移右移,加上点必要代码,算是给妹纸解释好了,差点就跪了。
过程:
首先是双向的,那就好办多了,之前遇到的很多问题都是单向的,先画个多层链表示意图好了;问妹纸是不是这样的,她说差不多。。。
然后想一下以什么样的顺序存放吧,可以是父子父子这样一直连下去的,也可以是横着过去,一行一行的放的,感觉一层一层来改变的顺序要少一点,那就那样吧。
肯定是要从头开始遍历的,所以时间复杂度最少是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; }
后记:
妹纸觉得答案挺满意的,然后就问,要是是单向呢?
我就答,那就记住每次遍历到哪里好了;
再问,要是多层的结构是这样的呢?
我再答:那样连起来啊,也是有子链表就追加,而且可以一次追加整一层,不过要记住其中的有子链表的位置,那样好像又有开销了,诶,记住第一个就行了,反正一层都连起来了。
妹纸会心一笑。
原文:http://www.cnblogs.com/annsshadow/p/5020861.html