【面試演算法】——連結串列(一)
一、連結串列
1.連結串列問題演算法難度不高,但是考察程式碼實現能力
2.連結串列和陣列都是一種線性結構
- 陣列是一段連續的儲存空間
- 連結串列空間不一定是保證連續的,為臨時分配的
3.連結串列的分類
4.連結串列問題程式碼實現的關鍵點
(1)連結串列調整函式的返回型別,根據要求往往是節點的型別
(2)處理連結串列過程中,先採用畫圖的方式理清邏輯
(3)連結串列問題對於邊界條件討論要求嚴格
5.連結串列插入和刪除的注意事項
(1)特殊處理連結串列為空,或者連結串列長度為1的情況
(2)注意插入操作的調整過程
(3)注意刪除操作的調整過程
注意點:頭尾節點及空節點需要特殊考慮
6.連結串列的翻轉操作
(1)特殊處理連結串列為空,或者連結串列長度為1的情況
大量的連結串列問題可以使用額外的資料結構來簡化調整過程,但連結串列問題最優解往往是不使用額外的資料結構的方法。
2、環形連結串列插值
題型:
給定一個整數num,如何在節點值有序的環形連結串列中插入一個節點為num的節點,並且保證這個環形單鏈表依然有序。
思路:
對於連結串列的演算法題目,我們需要考慮到原連結串列為空的情況和最後返回連結串列頭部節點是否需要改變的情況。我們將num轉換為一個節點型別資料。
首先,如果原連結串列為空,那麼我們將num節點的next指標指向自己,讓num自己成為一個環形連結串列,最後返回num節點;
如果原連結串列不為空,那麼我們設定兩個變數previous和current變數,將兩個變數分別等於原連結串列的頭結點和第二個節點,然後讓previous和current變數同步移動下去,如果出現previous的值小於等於num,current的值大於等於num,那麼就說明num應該插入到當前previous和current之間。
如果出現num比原連結串列中的所有數都大或者所有數都小的情況,num都會被插入到原連結串列的頭節點前面,但是返回的頭結點不同。
程式碼舉例:
import java.util.*; /* public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } }*/ public class InsertValue { public ListNode insert(int[] A, int[] nxt, int val) { // write code here //如果原連結串列為空 if(A.length == 0){ ListNode num = new ListNode(val); num.next = num; return num; } //構造環形連結串列 ListNode head = new ListNode(A[0]); ListNode cur = head; for(int i=0;i<A.length-1;i++){ ListNode next = new ListNode(A[nxt[i]]); cur.next = next; cur = next; } //插入 ListNode preNode = head; ListNode nextNode = preNode.next; //如果num小於head的值 if(val<head.val){ ListNode numNode = new ListNode(val); numNode.next = head; return numNode; } while(nextNode!=null && val>nextNode.val){ preNode = nextNode; nextNode = preNode.next; } ListNode numNode = new ListNode(val); numNode.next = nextNode; preNode.next = numNode; return head; } }
3、訪問單個節點的刪除練習題
題型:
給定一個連結串列中的節點node,但不給定整個連結串列的頭節點,如何在連結串列中刪除node?請實現這個函式,要求時間複雜度為O(1)。
思路:
如果是雙向連結串列,那麼比較簡單,因為我們可以通過previous找到被刪除節點的前節點,但是如果是單向連結串列,那麼我們無法訪問到被刪除節點的前節點,有一種不太完美的做法,就是進行值的拷貝,我們將刪除節點的下一個節點的值賦值給刪除節點,然後將刪除節點的指標指向下下一個節點,但是這種方式對於刪除尾結點存在缺陷,所以我們只能通過把尾結點賦值為null來實現,但這並不是真正的實現。
程式碼舉例:
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Remove {
public ListNode removeNode(ListNode pNode, int delVal) {
// write code here
if (pNode == null) {
return null;
}
if (pNode.val == delVal) {
return pNode.next;
}
ListNode node = pNode;
ListNode temp = pNode;
while (node != null) {
//這裡就是複製後一個節點,然後刪除後一個節點的做法
if (node.val == delVal && node.next != null) {
node.val = node.next.val;
node.next = node.next.next;
break;
}
//然而到尾節點還是不靈光的啦,一定需要前節點
else if (node.val == delVal && node.next == null) {
temp.next = null;
break;
}
temp = node;
node = node.next;
}
return pNode;
}
}