Swift 數據結構學習:線性表(鏈表)

鏈表是實現線性表的鏈式存儲結構的一種數據結構,鏈表根據結定義不一樣也有幾個種類,分別爲:node

  • 靜態鏈表:用於沒有指針類型的語言中。
  • 單鏈表:鏈表的結點中只有指向直接後繼結點的指針域。
  • 循環鏈表:表中最後一個結點的指針指向頭結點,整個鏈表造成一個環。
  • 雙向鏈表:每一個數據結點中都有兩個指針,分別指向直接後繼和直接前驅。

如下的實例代碼,都是以單鏈表爲例的。數組

這篇文章的主要內容bash

  • 鏈表的一些概念
  • 鏈表的基本結構
  • 鏈表的一些操做

概念

  • 鏈表數據結構

    用一組任意的存儲單元存儲數據元素,這組存儲單元能夠是連續的,也能夠是不連續的。數據元素間的邏輯順序是經過結點中的指針域來實現。app

  • 結點函數

    存儲數據元素信息的域稱爲數據域,把存儲後繼位置或前驅結點的域稱爲指針域。這兩部分就是鏈表中的一個節點。post

基本結構

結點的基本結構:性能

class LinkedListNode {
    var value: Int
    var next: LinkedListNode?
    
    public init(_ value: Int) {
        self.value = value
        self.next = nil
    }
}
複製代碼

鏈表的接本結構:ui

class LinkedList {
    var head: LinkedListNode?    // 頭結點
    var tail: LinkedListNode?    // 尾結點
}
複製代碼

經常使用操做

先在已經定義好了鏈表及結點的基本結構了,下面咱們能夠對鏈表作一些常見的操做。spa

增長

尾插法
func appendToTail(_ value: Int) {
    let newNode = LinkedListNode(value)
    if let last = tail {
        last.next = newNode
        tail = last.next
    } else {
        head = newNode
        tail = head
    }
}
複製代碼
頭插法
func appendToHead(_ value: Int) {
    let newNode = LinkedListNode(value)
    if let first = head {
        newNode.next = first
        head = newNode
    } else {
        head = newNode
        tail = head
    }
}
複製代碼
中間插入
// 給鏈表增長了一個長度屬性
var length: Int {
    get {
        var num = 0
        var temp: LinkedListNode? = head
        while let node = temp {
            num += 1
            temp = node.next
        }
        
        return num
    }
}


func appned(_ value: Int, _ index: Int) {
    if length == 0 {
        // 鏈表自己爲空,直接添加
        appendToHead(value)
    } else if index == 0 {
    	 // 插入到頭結點位置
        let newNode = LinkedListNode(value)
        newNode.next = head
        head = newNode
    } else if index < length && index > 0 {
        // 找到須要插入位置的前一個結點
        let newNode = LinkedListNode(value)
        var temp = 1
        var tempNode: LinkedListNode? = head
        while let node = tempNode {
            if temp == index {
                newNode.next = node.next
                node.next = newNode
                break
            }
            tempNode = tempNode?.next
            temp += 1
        }
    }
}
複製代碼

建立

利用尾插法直接將數組元素插入到鏈表末端

init(array: [Int]) {
    array.forEach { appendToTail($0) }
}
複製代碼

刪除

func remove(at index: Int) {
    // 鏈表爲空
    if length == 0 {
        return
    } else if index == 0 {
        head = head?.next
    } else if index < length && index > 0 {
        var tempNode: LinkedListNode?
        var j = 0
        var tempList: LinkedListNode? = head
        while tempList?.next != nil && j < index-1 {
            tempList = tempList?.next
            j += 1
        }
        
        tempNode = tempList?.next
        tempList?.next = tempNode?.next
    }
}
複製代碼

查找

// 查找某個結點
func node(at index: Int) -> LinkedListNode? {
    if length == 0 {
        return nil
    } else if index >= 0 && index < length {
        var tempNode = head
        var j = 0
        while tempNode?.next != nil && j < index {
            tempNode = tempNode?.next
            j += 1
        }
        
        return tempNode
    }
    return nil
}

// 根據值查找
func nodes(_ value: Int) -> [LinkedListNode] {
    var result: [LinkedListNode] = []
    
    var tempNode = head
    
    while let node = tempNode {
        if node.value == value {
            result.append(node)
        }
        tempNode = node.next
    }
    return result
}
複製代碼

反轉

func reverse(_ head: LinkedListNode?) -> LinkedListNode? {
    if head == nil || head?.next == nil {
        return head
    }
    
    // 遞歸找到最後一個結點
    let newHead = reverse(head?.next)
    
    // 將最後一個結點前一個結點反轉
    head?.next?.next = head
    
    // 斷開最後一個結點
    head?.next = nil
    
    self.head = newHead
    return newHead
}
複製代碼

過程說明:

反轉鏈表步驟

爲鏈表擴展一些操做函數

// 打印鏈表
func printSelf() {
    var tempNode = head
    while let node = tempNode {
        print(node.value)
        tempNode = tempNode?.next
    }
}


func map(transform: (Int) -> Int) -> LinkedList {
    let result = LinkedList()
    
    var tempNode = head
    while let node = tempNode {
        result.appendToTail(transform(node.value))
        tempNode = node.next
    }
    
    return result
}
    
func filter(predicate: (Int) -> Bool) -> LinkedList {
    let result = LinkedList()
    
    var tempNode = head
    while let node = tempNode {
        if predicate(node.value) {
            result.appendToTail(node.value)
        }
        tempNode = node.next
    }
    return result
}
複製代碼

總結

結合上篇線性表的順序存儲結構與單鏈表結構進行對比:

  • 存儲方式

    • 順序存儲用一段連續的存儲單元依次存儲線性表的數據元素。
    • 單鏈表採用鏈式存儲結構,用一組任意的存儲單元存放線性表的元素。
  • 時間性能

    • 查找
      • 順序存儲結構 O(1)
      • 單鏈表結構 O(n)
    • 插入和刪除
      • 順序存儲結構平均須要移動表長一半的元素 O(n)
      • 單鏈表結構在找出某位置的指針後,插入和刪除時間爲O(1)
  • 空間性能

    • 順序存儲結構須要預分配存儲空間,分配大了浪費空間,分配小了易形成溢出。
    • 單鏈表結構不須要預分配存儲空間,只要有就能夠分配,元素個數不受限制。
相關文章
相關標籤/搜索