本文列舉出了7個鏈表常見的面試題,其他面試題請閱讀下一篇博客
面試題1:刪除非尾節點
刪除非尾節點,只告訴一個當前節點的位置,將該位置的節點刪除。
解題思路:
刪除該位置的節點,由於我們不知道它的前一個節點的位置,不能直接將它的前一個節點與它的後一個節點連接起來。
因此,我們需要用另一種方法:
將該節點的後一個節點的元素賦值給該節點,然後刪除該節點的後一個節點。
void EraseNotTail(pLinkNode pos) { assert(pos); pLinkNode del = NULL; pos->data = pos->next->data; //後一個節點的元素的值賦給當前節點的值 del = pos->next; //當前節點的後一個節點爲要刪除的元素 pos->next = pos->next->next; free(del); // 刪除該節點 del = NULL; }
面試題2:逆序單鏈表
解題思路:
將單鏈表逆序的基本思想是將第一個節點的next置成NULL,然後將它的後一個節點指向它,依次循環直到將最後一個節點指向它的前一個節點。
void ReverseList(pList* pHead) { assert(pHead); pLinkNode cur = *pHead; pLinkNode prev = NULL; pLinkNode NewHead = NULL; while (cur) { prev = cur; cur = cur->next; prev->next = NewHead; NewHead = prev; } *pHead = NewHead; }
面試:3:排序單鏈表
解題思路:
排序鏈表可以用冒泡排序的思想,從前往後遍歷鏈表,要是前一個元素比後一個元素大,那麼就交換兩個元素,遍歷一次的結果 是將鏈表中最大的元素放在最後。
void BubbleSort(pList * pHead) { assert(pHead); pLinkNode cur = *pHead; pLinkNode end = NULL; DataType tmp = 0; while (cur != end) { while (cur && cur->next != end) { if (cur->data > cur->next->data) { tmp = cur->data; cur->data = cur->next->data; cur->next->data = tmp; } //將較大元素放在後面,較小元素放到前面 cur = cur->next; } //遍歷一次完成,將最大元素放在鏈表末尾 end = cur; cur = *pHead; } }
面試題4:在當前節點前插入一個數據x
解題思路:
基本思想是將x插入到當前節點之後,然後交換兩個節點的元素。
void InsertFrontNode(pLinkNode pos, DataType x) { assert(pos); pLinkNode NewNode = BuyNode(x); DataType tmp = 0; NewNode->next = pos->next; pos->next = NewNode; //將要插入的元素插入到當前節點的後面 tmp = NewNode->data;//交換兩元素 NewNode->data = pos->data; pos->data = tmp; }
面試題5:合併兩個有序列表
解題思路:
基本思想是首先確定一個合併以後的頭結點,比較兩個鏈表的第一個元素,較小的元素所在的節點爲頭結點。然後比較兩個鏈表剩餘節點的第一個節點,將較小的一個連在頭結點的後面,再次比較剩餘的節點,然後依次將較小的節點連在新鏈表的後面,直到有一個鏈表爲空。將另一個鏈表的剩餘節點全都連在新鏈表上。
有兩種方法可以實現:
一、非遞歸的方法:
pLinkNode Merge(pList l1, pList l2) { pList NewHead = NULL; pLinkNode cur = NULL; //若有一個鏈表爲空,返回不是空鏈表的那個鏈表,若都爲空,返回任意一個 if (l1 == NULL || l2 == NULL) { if (l1) return l1; else return l2; } if (l1->data < l2->data) //若l1的第一個元素比l2的第一個元素小,那麼l1的第一個節點爲頭節點 { NewHead = l1; l1 = l1->next; } else //若l2的第一個元素比l1的第一個元素小,那麼l2的第一個節點爲頭節點 { //若兩個元素相等則任意一個做頭節點都可以,這裏讓l2的元素做頭節點 NewHead = l2; l2 = l2->next; } cur = NewHead; while (l1 && l2) { if (l1->data < l2->data) { cur->next = l1; l1 = l1->next; } else { cur->next = l2; l2 = l2->next; } //將較小的元素連在新鏈表的後面 cur = cur->next; } if (l1) { cur->next = l1; // 若l1不爲空,將l1剩餘的元素連接在新鏈表的後面 } else { cur->next = l2; } //若l2不爲空,將l2剩餘的元素連接在新鏈表的後面 return NewHead; }
二、遞歸的方法:
因爲每次都是將兩個鏈表上的較小的節點連在新鏈表的後面,因此可以用遞歸方法實現
pLinkNode _Merge(pList l1, pList l2) { pList NewHead = NULL; pLinkNode cur = NULL; if (l1 == NULL || l2 == NULL) { if (l1) return l1; else return l2; } if (l1->data < l2->data) { NewHead = l1; NewHead->next = _Merge(l1->next, l2); } else { NewHead = l2; NewHead->next = _Merge(l1, l2->next); } return NewHead; }
面試題6:查找中間節點
解題思路:
用兩個指針同時從頭節點開始向後遍歷,一個快指針一次走兩步,另一個慢指針一次走一步,直快指針走到結尾,則指針所在的位置就是中間節點。
pLinkNode FindMidNode(pList head) { pLinkNode fast = head; pLinkNode slow = head; while (fast && fast->next) { fast = fast->next->next; //快指針一次走兩步 slow = slow->next; //慢指針一次走一步 } return slow; //返回中間節點 }
面試題7: 刪除單鏈表的倒數第k個節點(k > 1 && k < 鏈表的總長度) 時間複雜度O(N)
解題思路:
要求遍歷一遍刪除鏈表中倒數第k各節點,可以用兩個指針,第一個指針先走k-1步,然後第一個指針和第二個指針一起走,當第二個指針到達鏈表末尾時,第一個指針所在的位置就是倒數第k各節點。
然後根據刪除非尾節點的方法將它刪除(刪除非尾節點的方法可參考面試題1)。
void DelKNode(pList *pHead, int k) { assert(k > 1); pLinkNode l1 = *pHead; pLinkNode l2 = *pHead; pLinkNode del = NULL; while (--k && l2->next) //第二個指針先走k-1步 { l2 = l2->next; } if (l2->next == NULL) //k>=鏈表總長度 { return; } while (l2->next) //兩個指針一起走 { l2 = l2->next; l1 = l1->next; } del = l1->next; l1->data = l1->next->data; l1->next = l1->next->next; free(del); //刪除倒數第k各節點 del = NULL; }