學習資料結構——連結串列
一直以來都知道自己在資料結構上是個弱點,大學時期學的東西到現在就只能記得一個概念了,自從期末考完試就都還給老師了。要開始找工作面試了,決定把這些東西都重新溫習一遍。
資料結構中最基礎的應該就是線性表(線性表:零個或多個數據元素的有限序列)了,線性表根據物理結構的不同分為順序儲存結構和鏈式儲存結構,順序結構實現就是陣列了,鏈式結構就是連結串列了。
定義:連結串列是一種物理儲存單元上非連續、非順序的儲存結構,資料元素的邏輯順序是通過連結串列中的指標連結次序實現的。
連結串列分為單項鍊表,雙向連結串列和迴圈連結串列,一般情況下單項鍊表居多。推薦看一下J_Knight_在掘金上的文章(末尾有連結,連結串列要實現節點(Node)和鏈式結構(List)
節點是連結串列的基本單元,單項鍊表節點只有next指標,雙向連結串列包含next和previous兩個指標
ADT 節點(node) Data value:持有的資料 Operation init:初始化 previous:指向上一節點的指標 next:指向下一節點的指標 endADT
連結串列實現了一些主要的操作功能,如插入節點,刪除節點,查詢節點等(增刪改查)
ADT 連結串列(linked list) Data linked list:持有的線性表 Operation init:初始化 count:持有節點總個數 isEmpty:是否為空 first:頭節點 last:尾節點 node:傳入index返回節點 insert:插入node到指定index insertToHead:插入節點到表頭 appendToTail:插入節點到表尾 removeAll:移除所有節點 remove:移除傳入的節點 removeAt:移除傳入index的節點 endADT
具體程式碼就不貼了,J_Knight_在掘金上寫的很清楚了,建議自己敲一遍,還是很有作用的,我想分享一下在iOS面試之道上涉及到連結串列的三道題並記錄一下我當時的想法。
question1:給出一個連結串列和一個值x,要求將連結串列中所有小於x的值放到左邊,大於等於x的值放到右邊,並且保證原連結串列節點的順序不變,例子:List:1->5->3->2->4->2 x = 3 結果為:1->2->3->5->3->4
解題思路:使用兩個連結串列,一個記錄比x小的值,一個記錄比x大的值,使用尾插法來保證節點的順序是不變的,最後將兩個連結串列連線起來。
Dummy節點:它的作用就是作為一個虛擬的頭前結點。我們引入它的原因是我們不知道要返回的新連結串列的頭結點是哪一個,它有可能是原連結串列的第一個節點,可能在原連結串列的中間,也可能在最後,甚至可能不存在(nil)。
實現程式碼:
func LinkedListSort(_ head : LinkedList<Int>, _ x : Int) -> LinkedList<Int>{ let prevDummy = LinkedListNode<Int>(value: 0) , postDummy = LinkedListNode<Int>(value: 0) var prev = prevDummy , post = postDummy var node = head.first while node != nil { if((node?.value)! < x){ prev.next = node prev = node! }else{ post.next = node post = node! } node = node!.next } post.next = nil prev.next = postDummy.next return head }
question2:如何檢測連結串列中是否有環存在(這道題在去蘇寧面試的時候問過的)
解題思路:用兩個指標同時訪問連結串列,其中一個的速度是另一個的兩倍(快指標和慢指標),如果他們變成相等的,那麼連結串列存在環,反之則連結串列不存在環(這個理論我思考了很久,不明白為什麼快指標一定會和慢指標指向同一個節點,後來想通了,因為有環的連結串列是不會over的,兩個指標一定會指向同一個節點)
實現程式碼:
func isExistCircle(_ List : LinkedList<Int>) -> Bool{ let head = List.first var slow = head var fast = head while fast != nil && fast?.next != nil{ slow = slow?.next fast = fast?.next?.next if slow === fast{ return true } } return false }
Question 3:給出連結串列和一個值x,要求將連結串列中倒數第x個節點刪掉(連結串列為一個未知長度的單向連結串列)
解題思路:使用兩個速度相同的指標,快指標領先慢指標x個節點,當快指標到達終點時,慢指標的下一個節點就是我們要刪除的節點。(我當時直接用連結串列裡面的size函式獲得連結串列長度,用remove函式刪除了對應的節點,但是remove函式是基於雙向連結串列實現的,如果是單項鍊表的話是不可以直接remove的)
實現程式碼:
func removeFromEnd(_ List : LinkedList<Int>, _ x : Int ) -> LinkedList<Int>{ let head = List.first var slow = head var fast = head //設定快指標初始位置 for _ in 0 ..< x { fast = fast?.next } while fast != nil && fast?.next != nil{ slow = slow?.next fast = fast?.next } //List.remove(node: (slow?.next)!)//remove函式是基於雙向連結串列實現的,單項鍊表無法使用 slow?.next = slow?.next?.next return List }
連結串列章節就這三道題了,主要還是學到了dummy節點和快行指標這樣的概念,比較有收穫。
連結:
J_Knight_掘金地址iOS面試之道(唐巧,故胤道長)