一、鏈表的定義
1.和數組一樣,鏈表也是一種線性表。
2.從內存結構來看,鏈表的內存結構是不連續的內存空間,是將一組零散的內存塊串聯起來,從而進行數據存儲的數據結構。
3.鏈表中的每一個內存塊被稱為節點Node。節點除了存儲數據外,還需記錄鏈上下一個節點的地址,即后繼指針next。
詳細定義:
二、鏈表的特點
1.插入、刪除數據效率高,時間復雜度為O(1)級別(只需更改指針指向即可),隨機訪問效率低,時間復雜度O(n)級別(需要從鏈頭至鏈尾進行遍歷)。
2.和數組相比,內存空間消耗更大,因為每個存儲數據的節點都需要額外的空間存儲后繼指針。
三、常用的鏈表類型
1.單鏈表
1)每個節點只包含一個指針,即后繼指針。
2)單鏈表有兩個特殊的節點,即首節點和尾節點。為什么特殊?用首節點地址表示整條鏈表,尾節點的后繼指針指向空地址null。
3)性能特點:插入和刪除節點的時間復雜度為O(1),查找的時間復雜度為O(n)。
2.循環鏈表
1)除了尾節點的后繼指針指向首節點的地址外均與單鏈表一致。
2)適用於存儲有循環特點的數據,比如約瑟夫問題。
3.雙向鏈表
1)節點除了存儲數據外,還有兩個指針分別指向前一個節點地址(前驅指針prev)和下一個節點地址(后繼指針next)。
2)首節點的前驅指針prev和尾節點的后繼指針均指向空地址。
3)性能特點:
和單鏈表相比,存儲相同的數據,需要消耗更多的存儲空間。
插入、刪除操作比單鏈表效率更高O(1)級別。以刪除操作為例,刪除操作分為2種情況:給定數據值刪除對應節點和給定節點地址刪除節點。對於前一種情況,單鏈表和雙向鏈表都需要從頭到尾進行遍歷從而找到對應節點進行刪除,時間復雜度為O(n)。對於第二種情況,要進行刪除操作必須找到前驅節點,單鏈表需要從頭到尾進行遍歷直到p->next = q,時間復雜度為O(n),而雙向鏈表可以直接找到前驅節點,時間復雜度為O(1)。
對於一個有序鏈表,雙向鏈表的按值查詢效率要比單鏈表高一些。因為我們可以記錄上次查找的位置p,每一次查詢時,根據要查找的值與p的大小關系,決定是往前還是往后查找,所以平均只需要查找一半的數據。
4.雙向循環鏈表
首節點的前驅指針指向尾節點,尾節點的后繼指針指向首節點。
四、數組與鏈表的比較
1.插入、刪除和隨機訪問的時間復雜度
數組:插入、刪除的時間復雜度是O(n),隨機訪問的時間復雜度是O(1)。
鏈表:插入、刪除的時間復雜度是O(1),隨機訪問的時間復雜端是O(n)。
2.數組缺點
1)若申請內存空間很大,比如100M,但若內存空間沒有100M的連續空間時,則會申請失敗,盡管內存可用空間超過100M。
2)大小固定,若存儲空間不足,需進行擴容,一旦擴容就要進行數據復制,而這時非常費時的。
3.鏈表缺點
1)內存空間消耗更大,因為需要額外的空間存儲指針信息。
2)對鏈表進行頻繁的插入和刪除操作,會導致頻繁的內存申請和釋放,容易造成內存碎片,如果是Java語言,還可能會造成頻繁的GC(自動垃圾回收器)操作。
4.如何選擇?
數組簡單易用,在實現上使用連續的內存空間,可以借助CPU的緩沖機制預讀數組中的數據,所以訪問效率更高,而鏈表在內存中並不是連續存儲,所以對CPU緩存不友好,沒辦法預讀。
如果代碼對內存的使用非常苛刻,那數組就更適合。
如何通過單鏈表實現“判斷某個字符串是否為水仙花字符串”?(比如 上海自來水來自海上)
1)前提:字符串以單個字符的形式存儲在單鏈表中。
2)遍歷鏈表,判斷字符個數是否為奇數,若為偶數,則不是。
3)將鏈表中的字符倒序存儲一份在另一個鏈表中。
4)同步遍歷2個鏈表,比較對應的字符是否相等,若相等,則是水仙花字串,否則,不是。
設計思想
時空替換思想:“用空間換時間” 與 “用時間換空間”
當內存空間充足的時候,如果我們更加追求代碼的執行速度,我們就可以選擇空間復雜度相對較高,時間復雜度小相對較低的算法和數據結構,緩存就是空間換時間的例子。如果內存比較緊缺,比如代碼跑在手機或者單片機上,這時,就要反過來用時間換空間的思路。