鏈表有關的常見面試題


1.單鏈表逆序

實現1:

遍歷:

   1: /*
   2:  * 遍歷鏈表, 將每個結點的next置為其前驅
   3:  * 遍歷過程中需要額外的指針來記錄結點的前驅和后繼 
   4:  */
   5: LinkList ReverseList(LinkList L)
   6: {
   7:     if (!L || !L->next) {
   8:         return L;
   9:     }
  10:  
  11:     Node *pre, *cur, *next;
  12:  
  13:     pre = next = NULL;
  14:     cur = L->next;
  15:     while (cur) {
  16:         next = cur->next;
  17:         cur->next = pre;
  18:         pre = cur;
  19:         cur = next;
  20:     }
  21:     L->next = pre;
  22:  
  23:     return L;
  24: }

實現2:

遞歸:

 

   1: /* 
   2:  * 采用遞歸,每次遞歸返回子鏈表反轉后的頭指針 
   3:  */
   4: Node * ReverseList(Node *node)
   5: {
   6:     if (!node || !node->next) {
   7:         return node;
   8:     }
   9:  
  10:     Node *next = node->next;
  11:     Node *head = ReverseList(next);
  12:     next->next = node;
  13:     node->next = NULL;
  14:  
  15:     return head;
  16: }

2.合並有序鏈表

實現1:

遍歷, 遍歷兩個鏈表,每次將較小的結點插入到合並后的新鏈表。

   1: Node *Merge(Node *head1, Node *head2)
   2: {
   3:     /* 如果一個鏈表為空, 則直接返回另一個 */
   4:     if (!head1) {
   5:         return head2;
   6:     }
   7:     if (!head2) {
   8:         return head1;
   9:     }
  10:     
  11:     /* head為合並后的頭指針, p1 p2分別用來遍歷兩個鏈表 */
  12:     Node *head, *p1, *p2;
  13:     head = p1 = p2 = NULL;
  14:     if (head1->data < head2->data) {
  15:         head = head1;
  16:         p1 = head1->next;
  17:         p2 = head2;
  18:     } else {
  19:         head = head2;
  20:         p2 = head2->next;
  21:         p1 = head1;
  22:     }
  23:     
  24:     /* cur為合並后鏈表的當前結點, 從p1和p2從選出較小的作為其后繼 */
  25:     Node *cur = head;
  26:     while (!p1 && !p2) {
  27:         if (p1->data <= p2->data) {
  28:             cur->next = p1;
  29:             cur = p1;
  30:             p1 = p1->next;
  31:         } else {
  32:             cur->next= p2;
  33:             cur = p2;
  34:             p2 = p2->next;
  35:         }
  36:     }
  37:     
  38:     /* 遍歷完一個鏈表后,將另一鏈表剩余結點插入到新鏈表中 */
  39:     if (!p1) {
  40:         cur->next= p1;
  41:     } else {
  42:         cur->next= p2;
  43:     }
  44:     
  45:     return head;    
  46: }

實現2:

遞歸,每次遞歸返回合並后新鏈表的頭結點。

   1: Node *MergeRecursive(Node *head1, Node *head2)
   2: {
   3:     if (!head1 || !head2) {
   4:         return head1 ? head1 : head2;
   5:     }
   6:     
   7:     Node *head;
   8:     if (head1->data < head2->data) {
   9:         head = head1;
  10:         head->next = MergeRecursive(head1->next, head2);        
  11:     } else {
  12:         head = head2;
  13:         head->next = MergeRecursive(head1, head2->next);
  14:     } 
  15:     
  16:     return head;
  17: }

3.判斷兩個無環單鏈表是否交叉,如果是則返回交點

實現1:

判斷是否交叉:因為無環單鏈表只可能是Y型交叉,所以遍歷兩個鏈表至最后一個結點,判斷最后一個結點是否相等即可。

   1: bool IsCross(Node *head1, Node *head2)
   2: {
   3:     if (!head1 || !head2) {
   4:         return false;
   5:     }
   6:     
   7:     Node *p1 = head1;
   8:     Node *p2 = head2;    
   9:     while (p1->next) {
  10:         p1 = p1->next;
  11:     }
  12:     while (p2->next) {
  13:         p2 = p2->next;
  14:     }
  15:     
  16:     return (p1 == p2);
  17: }

找出交點:遍歷兩個鏈表,記錄長度分別為L1和L2,先讓長的鏈表向后移動abs(L1-L2),然后在逐個比較結點,第一個相等的結點即為交點。

   1: Node *FindCross(Node *head1, Node *head2)
   2: {
   3:     if (!head1 || !head2) {
   4:         return NULL;
   5:     }
   6:     
   7:     /* 求出兩個鏈表的長度 */
   8:     Node *p1, *p2;
   9:     p1 = head1;
  10:     p2 = head2;
  11:     int len1, len2, len;
  12:     len1 = len2 = 0;    
  13:     while (p1) {
  14:         len1++;
  15:         p1 = p1->next;
  16:     } 
  17:     while (p2) {
  18:         len2++;
  19:         p2 = p2->next;
  20:     }
  21:     
  22:     /* 將長鏈表先移動len個結點 */
  23:     int i;
  24:     len = abs(len1 - len2);
  25:     p1 = head1;
  26:     p2 = head2;
  27:     if (len1 > len2) {
  28:         for (i = 0; i < len; ++i) {
  29:             p1 = p1->next;
  30:         }
  31:     } else {
  32:         for (i = 0; i < len; ++i) {
  33:             p2 = p2->next;
  34:         }
  35:     }
  36:     
  37:     /* 遍歷 找到第一個相等的結點即為交點 */    
  38:     while (!p1) {
  39:         p1 = p1->next;
  40:         p2 = p2->next;
  41:         if (p1 == p2) {
  42:             return p1;
  43:         }
  44:     }
  45:     
  46:     return NULL; 
  47: }

實現2:

將其中一個鏈表首尾相連,這樣就轉換成為判斷另一個鏈表是否有環即求進入環的結點的問題。

4.判斷單鏈表是否有環, 如果有環則返回進入環的第一個結點.

實現1:

設置快慢指針,初始都指向鏈表頭,快指針每次前進兩步,慢指針每次前進一步,如果有環則兩個指針必定相遇。

判讀是否有環:

   1: bool HaveCircle(Node *head)
   2: {
   3:     if (!head) {
   4:         return false;
   5:     }
   6:     
   7:     Node *fast, *slow;
   8:     fast = slow = head;
   9:     
  10:     while (fast && fast->next) {        
  11:         slow = slow->next;
  12:         fast = fast->next->next;
  13:         if (fast == slow) {
  14:             return true;
  15:         }
  16:     }
  17:     
  18:     return false;
  19: }
找出環的位置:
實現2:

將結點的地址做hash,遍歷鏈表,檢測是否有地址相等的結點。

5.找出單鏈表的中間元素

實現:

設置快慢指針,初始都指向鏈表頭,快指針每次前進兩步,慢指針每次前進一步,當快指針到達鏈表末尾時,慢指針所指即為單鏈表的中間元素。

   1: Node *FindMid(Node *head)
   2: {
   3:     if (!head) {
   4:         return NULL;
   5:     }
   6:     
   7:     Node *fast, *slow;
   8:     fast = slow = head;
   9:     while (fast && fast->next) {
  10:         slow = slow->next;
  11:         fast = fast->next->next;
  12:     }
  13:     /* 如果是fast為NULL, 則鏈表長度為奇數, slow為中間結點
  14:     *  如果是fast->next為NULL, 則鏈表長度為偶數, slow 和 slow->next 為中間結點 
  15:     */    
  16:     return slow;
  17: }

6.刪除單鏈表中倒數第k個結點

實現:

設置兩個指針p1和p2,p1指向鏈表第一個結點,p2指向第k+1個結點,兩個指針同時前進,當p2到達鏈表尾時,p1指向倒數第k+1個結點,刪除p1后面的一個結點即可。

   1: Node *DeleteK(Node *head, int k) {
   2:     if (!head) {
   3:         return NULL;
   4:     }
   5:     
   6:     int i;
   7:     Node *p1, *p2;
   8:     p1 = p2 = head;
   9:     for (i = 0; i <= k; ++i) {
  10:         if (p2) {
  11:             p2 = p2->next;
  12:         } else {
  13:             return NULL;
  14:         }
  15:     }
  16:     
  17:     while (p2) {
  18:         p1 = p1->next;
  19:         p2 = p2->next;
  20:     }
  21:     p2 = p1->next;
  22:     p1->next = p2->next;
  23:     
  24:     return p2;    
  25: }

7.從單鏈表中刪除指定的中間結點

實現:

將指定結點的后繼的值賦值給指定結點,然后刪除其后繼即可。

   1: void DeleteMidNode(Node *del)
   2: {
   3:     if (!del) {
   4:         return; 
   5:     }
   6:     
   7:     Node *next = del->next;
   8:     if (next) {
   9:         del->data= next->data;
  10:         del->next= next->next;
  11:         free(next);    
  12:     }
  13: }

8.在單鏈表的指定結點前插入一個結點

實現:

將結點插入到指定結點后,然后交換兩節點的值即可。

   1: int InsertBeforeNode(Node *node, Node *insert)
   2: {
   3:     if (!node || !insert) {
   4:         return -1;
   5:     }
   6:     
   7:     Node temp = *node;
   8:     node->data = insert->data;
   9:     node->next= insert;
  10:     *insert = temp;
  11:     
  12:     return 0;    
  13: }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM