連結串列面試題小結(待完成)
阿新 • • 發佈:2019-01-10
以下程式碼使用的公共結構:
連結串列節點:
struct ListNode
{
int data;
ListNode *next;
};
輔助函式:
一,從尾到頭列印連結串列//make a list ListNode *makeList(int *arr, int n) { if(arr == NULL || n <= 0) return NULL; ListNode *phead = new ListNode; phead->data = arr[0]; phead->next = NULL; ListNode *ptr = phead; for(int i = 1; i < n; i++) { ListNode *node = new ListNode; node->data = arr[i]; node->next = NULL; ptr->next = node; ptr = node; } return phead; } //print a list void printList(ListNode *phead) { ListNode *node = phead; while(node != NULL) { cout << node->data << "\t"; node = node->next; } cout << endl; }
題目:給定一個單鏈表的頭節點,從尾到頭打印出該連結串列的所有節點。
解答:
- 使用遞迴,在遞迴中,若節點有後續節點,則先處理後續節點,再列印本節點的資料
- 遞迴的本質就是使用棧,所以可以不是用遞迴,而是在遍歷連結串列的時候將連結串列資料壓入棧中,遍歷結束之後出棧就可以得到從尾到頭的所有節點值
程式碼:
/******************************************************* **question 1 ********************************************************/ //solution 1 void printListFromTail_1(ListNode *phead) { if(phead == NULL) return; printListFromTail_1(phead->next); cout << phead->data << endl; } //solution void printListFromTail_2(ListNode *phead) { if(phead == NULL) return; stack<int> myStack; ListNode *node = phead; do { myStack.push(node->data); node = node->next; }while(node != NULL); while(myStack.size() > 0) { cout << myStack.top() << endl; myStack.pop(); } }
測試及其用例:
//test
/*********************************
arr[10] = {....}
arr[0] = {....}
arr[1] = {....}
arr = NULL
**********************************/
int main()
{
int arr[1] = {1};
ListNode *phead = makeList(arr, 1);
printListFromTail_1(phead);
printListFromTail_2(phead);
return 0;
}
二,連結串列中的倒數第K個節點
題目:給出一個單鏈表的頭節點,寫一個函式獲得其倒數第K個節點
解答:
最簡單的做法是:先遍歷連結串列,獲得連結串列的長度n,然後再次遍歷連結串列,從頭節點開始的第n - K + 1即是所求節點。
優化以下的做法是:使用兩個指標,一個先向前遍歷K 個節點,另外一個指著頭節點,然後兩個指標一起向前遍歷連結串列直到連結串列結尾。這時後面一個節點所指的就是倒數第K個節點
程式碼:
/*******************************************************
**question 2
********************************************************/
ListNode *getKthNodeFromTail(ListNode *phead, int k)
{
if(phead == NULL || k <= 0) return NULL;
ListNode *pBegin = phead;
ListNode *pEnd = phead;
for(int i = 0; i < k - 1; i++)
{
if(pBegin->next != NULL)
pBegin = pBegin->next;
else
return NULL;
}
while(pBegin->next != NULL)
{
pBegin = pBegin->next;
pEnd = pEnd->next;
}
return pEnd;
}
測試及其用例:
//test
/*********************************
arr[10] = {....} k = 10
arr[10] = {....} k = 1
arr[10] = {....} k = 5
**********************************/
int main()
{
int arr[1] = {1};
ListNode *phead = makeList(arr, 1);
getKthNodeFromTail(phead, 1);
return 0;
}
三,反轉連結串列
題目:給定一個單鏈表的頭節點指標,請反轉該單鏈表,並返回反轉之後的單鏈表的頭節點指標
解答:使用兩個指標,反轉連結串列,這個比較簡單。
程式碼:
/*******************************************************
**question 3
********************************************************/
ListNode *reverseList(ListNode *phead)
{
if(phead == NULL) return NULL;
if(phead->next == NULL) return phead;
ListNode *pBegin = phead->next;
ListNode *pEnd = phead;
pEnd->next = NULL;
do
{
ListNode *pTemp = pBegin->next;
pBegin->next = pEnd;
pEnd = pBegin;
pBegin = pTemp;
}while(pBegin != NULL);
return pEnd;
}
四,判斷單鏈表是否有環
五,合併兩個排序的連結串列
六,複雜連結串列的複製
七,兩個連結串列的第一個公共節點
題目:給定兩個單鏈表的頭節點,寫一個函式返回兩個單鏈表的第一個公共節點
解答:
由於是兩個單鏈表存在公共節點,那麼說明是Y型結構,第一個公共節點之後的節點都是公共節點,所以解決這個問題的方法就是利用這些公共節點。
- 將兩個連結串列從前到後遍歷,將他們的節點分別壓入兩個棧中,然後出棧比較,直到節點不一樣,說明上一個出棧的節點就是第一個公共子節點。時間複雜度是O(M + N),空間複雜度是O(M + N)
- 分別遍歷兩個節點,得到兩個節點的長度為:M、N。讓長的連結串列先往前走|M - N|步,然後兩個連結串列一起往前遍歷,直到兩個連結串列遇到的節點一樣,即為公共子節點
程式碼: