系列文章導圖:
檢視所有系列文章: xiaozhuanlan.com/leetcode_tc
Linked List
1. 知識點
1.1 概念
連結串列想必大家都不陌生,連結串列是一種遞迴的資料結構,它或者為空(null),或者是指向一個結點(node)的引用,該節點還有一個元素和一個指向另一條連結串列的引用。
連結串列有單向連結串列(Single Linked List)和雙向連結串列(Doubly Linked List)1.2 連結串列常見操作
1.2.1 插入
有個New Node節點需要插入到連結串列中的時候,找到插入的位置,將新節點的Next指標指向要插入位置元素的前面,然後把前面一個節點的Next指標指向新節點。1.2.2 刪除
刪除操作,把要刪除節點的前一個節點的Next指標指向刪除節點的後一個節點,即跨過需要刪除的節點,然後再將刪除節點從記憶體中釋放。1.3 雙向連結串列
簡單介紹一個雙向連結串列,與上面單連結串列不同的是,雙向連結串列既有前置節點,也有後置節點。
1.4 時間複雜度
操作 | 時間複雜度 | 說明 |
---|---|---|
prepend | O(1) | 頭部增加元素 |
append | O(1) | 尾部增加元素 |
lookup | O(N) | 查詢 |
insert | O(1) | 插入 |
delete | O(1) | 刪除 |
上表中總結,只有查詢的時間複雜度是O(N),因為查詢需要從頭節點依次往下找。其餘操作都是O(1),上面也簡單介紹插入和刪除,只需要動2次Next指標,即O(1)的時間複雜度。
2. 面試題
演算法程式碼實現使用python實現,如果不瞭解python,則使用自己喜歡的語言實現即可, 完整程式碼地址github.com/CrystalSkyZ…,完整程式碼裡可以自己進行用例除錯。
2.1 206. Reverse Linked List
leetcode link 206 leetcode第206題,反轉一個單連結串列。 題目要求: Reverse a singly linked list.
Example: Input: 1->2->3->4->5->NULL Output: 5->4->3->2->1->NULL
題目要求是反轉一個單連結串列,思路的相對簡單,即將每一個指標的Next指向它的前驅節點即可。這類連結串列題目在面試過程之中主要考察的是程式碼實現能力,思維相對簡單,所以在面試之前一定要有所準備,這樣才能緊張的面試過程中快速精煉的寫出程式碼。
python實現:
class Solution:
def reverseList(self, head)
"""
:type head: ListNode
:rtype: ListNode
"""
cur, prev = head, None
while cur:
cur.next, prev, cur = prev, cur, cur.next
return prev
複製程式碼
要實現每一個指標指向前置節點,則需要2個節點,一個是當前節點(cur),一個是前置節點(prev),然後一直迴圈cur節點,再就是對前置節點和當前節點進行賦值操作。
2.2 24. Swap Nodes in Pairs
leetcode link 24 leetcode第24題,反轉相鄰節點。 題目要求: Given a linked list, swap every two adjacent nodes and return its head.
Example: Given 1->2->3->4, you should return the list as 2->1->4->3.
題目是要求反轉相鄰節點,思路是迴圈整個連結串列,將相鄰節點進行反轉。思路簡單,但是怎麼讓程式碼顯得很精煉了。
python實現:
class Solution:
def swapPairs(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
prev, prev.next = self, head
while prev.next and prev.next.next:
a = prev.next
b = a.next
prev.next, b.next, a.next = b, a, b.next
prev = a
return self.next
複製程式碼
首先需要有三個節點,2個相鄰節點和後一個節點。第一行程式碼如果不熟悉python的可能看不懂,其實是將類Solution物件自己賦值prev, prev.next則表示next是類Solution的一個屬性,prev.next才是表示head節點。
需要提醒的是連結串列長度是偶數的話,是剛好全部2個相鄰節點反轉,如果是奇數則最後一個節點保持不變。
python多變數賦值有時候確實是晦澀難懂,它是一次性賦值,即先算好等號右邊的所有值,然後一次性賦給左邊。我們可以先簡單列舉一下 1->2->3->4,如果需要反轉1和2,則它的下一個狀態是什麼。先算好右邊的值: b=2 a=1 b.next=3, 賦值給左邊,則: prev.next=2 b.next=1 a.next=3 賦值之後: b=2 a=1 b.next=1 a.next=3。變成了 2->1->3->4, 可以簡單理解為賦值之後,prev.next 就是 b, b.next=1=a=1, a.next=3 串起來就是 2->1->3->4
下面給出1->2->3->4->5具體一次while迴圈的列印,方便大家更加理解,詳細請看完整程式碼地址https://github.com/CrystalSkyZ/leetcode_tc:
print(a.val, a.next.val, b.val, b.next.val, pre.next.val, pre.next.next.val)
# (1, 2, 2, 3, 1, 2)
# 進行賦值:
pre.next, b.next, a.next = b, a, b.next
print(a.val, a.next.val, b.val, b.next.val, pre.next.val, pre.next.next.val)
# (1, 3, 2, 1, 2, 1)
print(linear.printLiner(self.next)) # 遍歷整個連結串列
# [2, 1, 3, 4, 5]
pre = a
print(pre.val, a.val, self.next.val, self.next.next.val)
# (1, 1, 2, 1)
複製程式碼
2.3 141. Linked List Cycle
leetcode link 141 題目要求: Given a linked list, determine if it has a cycle in it.
Example:
題目要求判斷一個連結串列是否有環,通常有幾種思路解法:
-
暴力解法, 一直遍歷迴圈看下一個節點是否為null, 如果是環的話則會一直死迴圈,可以判斷一個時間,比如1s或0.5s,則結束。這種解法只是一種思路,實際面試中肯定不是一種好的解法。
-
Set判重,思路跟思路一一樣,一直遍歷迴圈,區別就是每一次遍歷,在當前節點留下記號,用set儲存起來,如果下一個節點在set裡則證明有環,否則無環。時間複雜度是O(N),但是空間複雜度由於用了set,則是O(N)。
-
龜兔賽跑,則有一個快指標和一個慢指標,快指標每次走2步,慢指標每次走1步,如果有環的情況下,則2者肯定會相遇,即判斷有環。這種思路方式如果沒有事先去刷過這個題,是比較難想到的。
程式碼實現:
- Set判重:
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
exist = set()
while head:
if head in exist:
return True
exist.add(head)
head = head.next
return False
複製程式碼
- 龜兔賽跑:
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
fast = slow = head
while slow and fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow is fast:
return True
return False
複製程式碼
3. 思考題
leetcode上還有幾題跟上面講的類似,可以先去做一下,下一篇再一起交流解法和程式碼例項:
如果是國內地址的話,則把路由換成https://leetcode-cn.com/
更多精彩文章請關注公眾號 天澄的技術筆記 (ChengTian_Tech)