前言: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; }
後記:
妹紙以爲答案挺滿意的,而後就問,要是是單向呢?
我就答,那就記住每次遍歷到哪裏好了;
再問,要是多層的結構是這樣的呢?
我再答:那樣連起來啊,也是有子鏈表就追加,並且能夠一次追加整一層,不過要記住其中的有子鏈表的位置,那樣好像又有開銷了,誒,記住第一個就好了,反正一層都連起來了。
妹紙會心一笑。