無標題文章

Swift算法俱樂部:Swift 鏈表數據結構@(Swift)在本教程中,我們將會在Swift 3中實現鏈表。##Getting Started鏈表是一個序列化的數據項,每一個項目都作為節點(node)被引用。有兩個主要類型的鏈表:單向鏈表,每個節點有一個指向到下一個節點的引用。
Alt text
Alt text
雙向鏈表,每個節點各有一個向前和向后的節點的引用。
Alt text
Alt text
我們還需要去跟蹤鏈表的開頭和結尾,通常管這兩個指針叫做head和tail。
Alt text
Alt text
##Swift 3中實現鏈表這部分,我們將會在Swift 3中實現鏈表。記?。烘湵硎怯晒濣c組成的。所以需要先創建基本節點類。創建一個全新的Swift Playground并且添加一個Class:swiftpublic class Node {}###值節點需要關聯一個值,在Node類的中括號中添加下面的代碼:swiftvar value: String init(value: String) { self.value = value}聲明了一個屬性,它的名字叫做value,是String類型。在我們的app中這可能是你想存儲的類型。還聲明了一個初始化方法,需要初始化所有非可選的屬性。###Next另外,還需要一個值,每個節點都需要一個指針指向到鏈表中的下一個節點。在類中添加另外一個屬性:swiftvar next: Node?這里聲明了一個next屬性,它是Node類型。注意next屬性是可選,因為在鏈表中的最后一個節點不會指向任何的節點。###Previous我們將會實現一個雙向鏈表,所以我們還需要一個指向之前節點的指針。在類中添加另外一個屬性:swiftweak var previous: Node?>注意:為了避免所有權的循環引用,聲明的previous指針是弱引用。如果你有一個節點A,它的下一個節點是B,這樣A指向B,B也指向A。在某些的情況下,這樣會導致節點即使在被刪除以后還會存在的情況。這是我們所不希望的,所以需要讓其中的一個指針為弱引用來避免出現這樣的問題。###鏈表現在已經創建的節點還需要確定它的起點和終點。添加新的LinkedList類到playground的底部:swiftpublic class LinkedList { fileprivate var head: Node? private var tail: Node? public var isEmpty: Bool { return head == nil } public var first: Node? { return head } public var last: Node? { return tail }}這個類將會跟蹤鏈表的開頭和結尾,同樣也會提供幾個有用的方法。###Append處理添加一個新的節點到鏈表,我們將會在LinkedList類中聲明一個append(value:)方法。swiftpublic func append(value: String) { // 1 let newNode = Node(value: value) // 2 if let tailNode = tail { newNode.previous = tailNode tailNode.next = newNode } // 3 else { head = newNode } // 4 tail = newNode}回顧這部分的代碼:創建一個包含值的節點。記住,創建Node類的目的是讓每一個項目都可以指向到之前或之后的節點。如果tailNode不為空,就意味著鏈表中有些東西,配置這個新的項目的previous指向到鏈表的tailNode,并且配置tailNode的next指向到新的項目。最后,設置鏈表的tail為最新創建的項目對象。###打印鏈表讓我們試著輸出鏈表。在LinkedList類的外面:swiftlet dogBreeds = LinkedList()dogBreeds.append(value: "Labrador")dogBreeds.append(value: "Bulldog")dogBreeds.append(value: "Beagle")dogBreeds.append(value: "Husky")在定義鏈表的后面,嘗試打印:swiftprint(dogBreeds)你可以通過組合鍵:Command-Shift-Y啟動調式控制臺,可以看到下面的輸出:LinkedList這種輸出沒有任何的價值,要想顯示更多的有用的文字信息,可以讓LinkedList類采用CustomStringConvertable協議。在LinkedList類中實現下面的代碼:swift// 1extension LinkedList: CustomStringConvertible { // 2 public var description: String { // 3 var text = "[" var node = head // 4 while node != nil { text += "\(node!.value)" node = node!.next if node != nil { text += ", " } } // 5 return text + "]" }}上面代碼的意思是:1. 聲明了LinkedList類的擴展,并且采用CustomStringConvertible協議。這個協議希望你實現一個計算屬性description,該屬性是String類型。2. 聲明的description屬性是計算屬性,它是一個只讀的并且返回字符串的屬性。3. 聲明的text變量,它會生成整個的字符串?,F在它包含一個左中括號,然后是鏈表的開始。4. 通過循環鏈表,將鏈表中項目的值依次添加到text中。5. 添加一個右中括號到text變量?,F在,當你調用LinkedList類的print方法,將會看到一個非常美妙的鏈表描述:"[Labrador, Bulldog, Beagle, Husky]"###訪問節點盡管通過鏈表的next和previous方法可以很有效的去按順序獲取每個節點,但有的時候通過索引的方式對于我們來說可能會更加的簡單。我們會在LinkedList類中聲明一個nodeAt(index: )方法,它將返回一個指定索引的節點。 修改LinkedList中的代碼:swiftpublic func nodeAt(index: Int) -> Node? { // 1 if index >= 0 { var node = head var i = index // 2 while node != nil { if i == 0 { return node } i -= 1 node = node!.next } } // 3 return nil}上面的代碼是這樣的:1. 添加對指定索引值的檢測,看它是否為非負數。這樣可以防止負數出現的無限循環。2. 循環節點,直到到達了那個指定索引值的節點,就會返回這個node。3. 如果索引值小于0,或者是大于鏈表的項目數,則會返回nil。###移除所有節點移除所有節點,我們只需要讓head和tail為nil即可。swiftpublic func removeAll() { head = nil tail = nil}###移除單個節點移除單個節點,我們需要考慮下面的三種情況:1. 移除第一個節點,需要修改head和previous指針:
Alt text
Alt text
2. 移除中間的節點,需要修改previous和next:
Alt text
Alt text
3. 移除最后節點,需要修改next和tail指針:
Alt text
Alt text
修改LinkedList類:swiftpublic func remove(node: Node) -> String { let prev = node.previous let next = node.next if let prev = prev { prev.next = next // 1 } else { head = next // 2 } next?.previous = prev // 3 if next == nil { tail = prev // 4 } // 5 node.previous = nil node.next = nil // 6 return node.value}1. 如果移除的節點不是第一節點則修改next指針。2. 如果移除的節點是第一節點則修改head。3. 修改刪除節點的下一個節點的previous。4. 如果刪除的是最后一個節點,修改tail。5. 給刪除節點的previous和next賦值nil。6. 返回移除節點的值。##Generics之前,我們已經實現了可以存儲字符串的基本鏈表類。已經提供了添加、移除和訪問節點的功能。這部分,我們將會使用范式抽象出所有的類型鏈表。修改之前的Node類:swift// 1public class Node<T> { // 2 var value: T var next: Node<T>? weak var previous: Node<T>? // 3 init(value: T) { self.value = value }}1. 改變Node類的聲明為范式類型T。2. 我們的目的是允許Node類存儲任何的類型,所以構建value的類型也為T,而不是之前的String。3. 修改初始化方法。修改LinkedList類使用范式。swift// 1. Change the declaration of the Node class to take a generic type Tpublic class LinkedList<T> { // 2. Change the head and tail variables to be constrained to type T fileprivate var head: Node<T>? private var tail: Node<T>? public var isEmpty: Bool { return head == nil } // 3. Change the return type to be a node constrained to type T public var first: Node<T>? { return head } // 4. Change the return type to be a node constrained to type T public var last: Node<T>? { return tail } // 5. Update the append function to take in a value of type T public func append(value: T) { let newNode = Node(value: value) if let tailNode = tail { newNode.previous = tailNode tailNode.next = newNode } else { head = newNode } tail = newNode } // 6. Update the nodeAt function to return a node constrained to type T public func nodeAt(index: Int) -> Node<T>? { if index >= 0 { var node = head var i = index while node != nil { if i == 0 { return node } i -= 1 node = node!.next } } return nil } public func removeAll() { head = nil tail = nil } // 7. Update the parameter of the remove function to take a node of type T. Update the return value to type T. public func remove(node: Node<T>) -> T { let prev = node.previous let next = node.next if let prev = prev { prev.next = next } else { head = next } next?.previous = prev if next == nil { tail = prev } node.previous = nil node.next = nil return node.value }}所有代碼已經完成,可以實地測試了:swiftlet dogBreeds = LinkedList<String>()dogBreeds.append(value: "Labrador")dogBreeds.append(value: "Bulldog")dogBreeds.append(value: "Beagle")dogBreeds.append(value: "Husky") let numbers = LinkedList<Int>()numbers.append(value: 5)numbers.append(value: 10)numbers.append(value: 15)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內容