演算法面試(一) 連結串列

天澄發表於2019-02-23

系列文章導圖:

演算法面試(一) 連結串列

檢視所有系列文章: 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:

演算法面試(一) 連結串列

題目要求判斷一個連結串列是否有環,通常有幾種思路解法:

  1. 暴力解法, 一直遍歷迴圈看下一個節點是否為null, 如果是環的話則會一直死迴圈,可以判斷一個時間,比如1s或0.5s,則結束。這種解法只是一種思路,實際面試中肯定不是一種好的解法。

  2. Set判重,思路跟思路一一樣,一直遍歷迴圈,區別就是每一次遍歷,在當前節點留下記號,用set儲存起來,如果下一個節點在set裡則證明有環,否則無環。時間複雜度是O(N),但是空間複雜度由於用了set,則是O(N)。

  3. 龜兔賽跑,則有一個快指標和一個慢指標,快指標每次走2步,慢指標每次走1步,如果有環的情況下,則2者肯定會相遇,即判斷有環。這種思路方式如果沒有事先去刷過這個題,是比較難想到的。

程式碼實現:

  1. 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
複製程式碼
  1. 龜兔賽跑:
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上還有幾題跟上面講的類似,可以先去做一下,下一篇再一起交流解法和程式碼例項:

  1. 142題 地址: leetcode.com/problems/li…
  2. 25題 地址: leetcode.com/problems/re…

如果是國內地址的話,則把路由換成https://leetcode-cn.com/

更多精彩文章請關注公眾號 天澄的技術筆記 (ChengTian_Tech)

演算法面試(一) 連結串列

相關文章