題目:輸入乙個鍊錶的頭結點,從尾到頭反過來列印出每個結點的值。
鍊錶結點定義如下:
struct listnode
;
看到這道題後,很多人的第一反應是從頭到尾輸出將會比較簡單,於是我們很自然的想到把鍊錶中鏈結結點的指標反轉過來,改變鍊錶的方向,然後就可以從頭到尾輸出了。但該方法會改變原來鍊錶的結構。是否允許在列印鍊錶的時候修改鍊錶的結構?這個取決於面試官的需求,因此在面試的時候我們要詢問清楚面試官的要求。
通常列印是乙個唯讀操作,我們不希望列印時修改內容。假設面試官也要求這個題目不能改變鍊錶的結構。
接下來我們想到解決這個問題肯定要遍歷鍊錶。遍歷的順序是從頭到尾的順序,可輸出的順序卻是從尾到頭,也就是說第乙個遍歷到的結點最後乙個輸出,而最後乙個遍歷到的結點第乙個輸出。這就是典型的「後進先出」,我們可以用棧實現這種順序。每經過乙個結點的時候,把該結點放到乙個棧中,當遍歷完整個鍊錶後,再從棧頂開始逐個輸出結點的值,此時輸出的結點的順序已經反轉過來了。這種思路的實現**如下:
void printlistreversingly_iteratively(listnode* phead)
while(!nodes.empty())
}
既然想到了用棧來實現這個函式,而遞迴在本質上就是乙個棧結構,於是很自然的又想到了用遞迴來實現。要實現反過來輸出鍊錶,我們每訪問到乙個結點的時候,先遞迴輸出它後面的結點,再輸出該結點自身,這樣鍊錶的輸出結果就反過來了。
基於這樣的思路,不難寫出如下**:
void printlistreversingly_recursively(listnode* phead)
printf(「%d\t」, phead->m_nvalue);
}}
上面的基於遞迴的**看起來很簡潔,但有個問題:當鍊表非常長的時候,就會導致函式呼叫的層級很深,從而有可能導致函式呼叫棧溢位。顯示用棧基於迴圈實現的**的魯棒性要好一些。
附上自己的**:
(1) 頭結點中的資料域不存放資料的
#include
#include
struct linknode
;typedef struct linknode list;
typedef list
*linklist;
void insert(linklist &head)
else
}}// 由於 這種情況下 頭結點 不存放資料;因此,列印時 不該列印頭結點 的值
// 因此,先將 head 指向 下乙個結點 ,再呼叫 遞迴
void reverseprint(linklist head)
}void print(linklist head)
}void myfree(linklist &head)
}int main()
(2)頭結點中的資料域存放資料的
#include
#include
struct linknode
;typedef struct linknode list;
typedef list
*linklist;
void insert(linklist &head)
else
}}void reverseprint(linklist head)
printf("%d\n", head->
data);
}}void print(linklist head)
}void myfree(linklist &head)
}int main()
面試題5 從尾到頭列印鍊錶
方法一 使用棧 html view plain copy include stack include stdio.h typedef struct listnode listnode listnode createlistnode int value void connectlistnode lis...
面試題5 從尾到頭列印鍊錶
題目 輸入乙個鍊錶的頭結點,從尾到頭反過來列印出每個節點的值。鍊錶定義結構如下 struct listnode 通常遍歷的順序是從頭到尾的順序,可輸出的順序確是從尾到頭。也就是說第乙個遍歷到的節點最後乙個輸出,而最後乙個遍歷到的節點第乙個輸出。這就是典型的 後進先出 我們可以用棧實現這種順序。每經過...
面試題5 從尾到頭列印鍊錶
鍊錶的建立,插入結點,刪除結點等操作都只需要20行左右的 來實現。鍊錶是一種動態資料結構,因為在建立鍊錶的時候,無須知道鍊錶的長度。當插入乙個結點的時候,只需要為新結點分配記憶體,然後調整指標的指向來確保新結點被鏈結到鍊錶當中。記憶體分配不是在建立鍊錶的時候一次完成的,而是每新增乙個結點分配一次記憶...