Swift實現LRU緩存淘汰算法

LRU = Least Recently Used,最近最少使用node

使用的數據結構:鏈表,哈希表算法

使用的編程語言:Swift編程

思路描述:

  • 維護一個有序鏈表(我使用的雙向鏈表)
    • 靠近尾部的節點則在時間上越早被訪問
  • 當有新數據時,先從頭開始遍歷鏈表
    • 若是數據已經在緩存中
      • 遍歷後獲得數據所在的結點,從這個位置刪除
      • 最後插入到鏈表頭部
    • 若是數據沒在緩存中,再次分爲兩種狀況
      • 若是緩存的空間沒有滿
        • 直接把這個數據所在的結點插入到鏈表頭部
      • 若是緩存空間已滿
        • 先刪除尾結點
        • 把新數據的結點插入到鏈表頭部 (這個思路不包含哈希表)

一些細節

  1. 這次練習實現LRU緩存算法的目的:熟悉鏈表的代碼實現,具體實現的是雙向鏈表
  2. 使用鏈表的同時,也使用了哈希表:
    • 若是不使用哈希表
      • 因爲每當有新數據時都要遍歷一次鏈表,時間複雜度爲O(n)
    • 若是包含哈希表
      • 每當有新數據時,因爲能夠查詢數據是否存在哈希表裏,時間複雜度降至O(1)
      • 這裏使用了空間換時間的思想
  3. 使用鏈表實現的缺點
    • 1)內存空間消耗更大,由於須要額外的空間存儲指針信息。
    • 2)對鏈表進行頻繁的插入和刪除操做,會致使頻繁的內存申請和釋放,容易形成內存碎片,若是是Java語言,還可能會形成頻繁的GC(自動垃圾回收器)操做。
  4. 使用數組實現的缺點
    • 1)若申請內存空間很大,好比100M,但若內存空間沒有100M的連續空間時,則會申請失敗,儘管內存可用空間超過100M。
    • 2)大小固定,若存儲空間不足,需進行擴容,一旦擴容就要進行數據複製,而這時很是費時的。

具體代碼(基於鏈表和哈希表)

鏈表:swift

// 節點
public class LinkedListNode<K, V> {
    
    var key: K?
    var value: V?
    var next: LinkedListNode?
    weak var previous: LinkedListNode?
    
    public init(key: K? ,value: V?) {
        self.key = key
        self.value = value
    }
}


// 雙向鏈表
class LinkedList<K, V> : NSObject {
    
    public typealias Node = LinkedListNode<K, V>
    
    // 頭節點,私有
    private var head: Node?
    
    // 獲取鏈表第一個元素
    public var first: Node? {
        return head
    }
    
    // 獲取鏈表最後一個元素
    public var last: Node? {
        
        guard var node = head else {
            return nil
        }
        
        while let next = node.next {
            node = next
        }
        return node
    }
    
    
    // 檢查鏈表是否爲空
    public var isEmpty: Bool {
        return head == nil
    }
    
    
    // 獲取鏈表的長度
    public var count: Int {
        
        guard var node = head else {
            // head爲nil,空鏈表
            return 0
        }
        
        // 循環,知道node爲nil爲止
        var count = 1
        while let next = node.next {
            node = next
            count += 1
        }
        return count
    }
    
    
    // 在指定的index獲取node
    public func node(atIndex index: Int) ->Node? {
        
        guard index != 0 else {
            // 是head
            return head
        }
        
        var node = head!.next
        
        guard index < 0 else {
            return nil
        }
        
        for _ in 1..<index {
            node = node?.next
            if node == nil {
                break
            }
        }
        return node!
    }
    
    
    // 在鏈表尾部插入元素
    public func appendToTail(node: Node) {
        
        let newNode = node
        
        if let lastNode = last {
            
            // 尾節點存在
            newNode.previous = lastNode
            lastNode.next = newNode
            
        } else {
            
            // 尾節點不存在
            head = newNode
            
        }
    }
    
    
    // 在鏈表的頭部插入元素
    public func insertToHead(node: Node) {
        
        let newNode = node
        
        if head == nil {
            
            head = newNode
            
        } else {
            
            newNode.next = head
            head?.previous = newNode
            head = newNode
            
        }
        
    }
    
    
    // 在指定位置插入元素
    public func insert(_ node: Node, atIndex index: Int) {
        
        if (index < 0) {
            print("invalid input index")
            return
        }
        
        let newNode = node
        
        if count == 0 {
            
            head = newNode
            
        } else {
            
            if index == 0 {
                
                // 插入到鏈表頭部,特殊處理
                newNode.next = head
                head?.previous = newNode
                head = newNode
                
            } else {
                
                guard index <= count else {
                    print("out of range")
                    return
                }
                
                let prev = self.node(atIndex: index - 1)
                let next = prev?.next
                
                newNode.previous = prev
                newNode.next = prev?.next
                prev?.next = newNode
                next?.previous = newNode
                
            }
            
        }
        
    }
    
    
    // 刪除所有元素
    public func removeAll() {
        head = nil
    }
    
    
    // 刪除最後一個元素
    public func removeLast() -> V? {
        guard !isEmpty else {
            return nil
        }

        return remove(node: last!)
    }
    
    
    // 刪除指定的元素
    public func remove(node: Node) -> V? {
        
        guard head != nil else {
            print("鏈表爲空")
            return nil
        }
        
        
        let prev = node.previous
        let next = node.next
        
        
        if let prev = prev {
            prev.next = next
        } else {
            head = next
        }
        
        
        next?.previous = prev
        
        node.previous = nil
        node.next = nil
        
        return node.value
    }
    
    
    // 刪除指定index的元素
    public func removeAt(_ index: Int) -> V? {
        
        guard head != nil else {
            print("linked list is empty")
            return nil
        }
        
        let node = self.node(atIndex: index)
        guard node != nil else {
            return nil
        }
        return remove(node: node!)
    }
}
複製代碼

LRU緩存算法:數組

class LRUStrategy<K: Hashable, V>: NSObject {

    let capacity: Int
    var length = 0
    
    private let queue: LinkedList<K, V>
    private var hashtable: [K: LinkedListNode<K, V>]
    
    
    /*
      LRU(Least Recently Used) Cache, 
		capacity是肯定緩存的最大值
     */
    init(capacity: Int) {
        self.capacity = capacity
        
        self.queue = LinkedList()
        self.hashtable = [K: LinkedListNode<K, V>](minimumCapacity: self.capacity)
    }
    
    // swift的下標語法,在這是LRUStrategy類哈希化後,get set的語法糖
    subscript (key: K) -> V? {
        get {
            if let node = self.hashtable[key] {
                _ = self.queue.remove(node: node)
                self.queue.insertToHead(node: node)
                return node.value
            } else {
                return nil
            }
        }
        
        set(value) {
            if let node = self.hashtable[key] {
                node.value = value
                
                _ = self.queue.remove(node: node)
                self.queue.insertToHead(node: node)
            } else {
                let node = LinkedListNode(key: key, value: value)
                
                if self.length < capacity {
                    // 隊列尚未滿
                    self.queue.insertToHead(node: node)
                    self.hashtable[key] = node
                    
                    self.length = self.length + 1
                } else {
                    // 隊列滿了
                    self.hashtable.removeValue(forKey: self.queue.last!.key!)
                    _ = self.queue.removeLast()
                    
                    if let node = self.queue.last {
                        node.next = nil
                    }
                    
                    self.queue.insertToHead(node: node)
                    self.hashtable[key] = node
                }
            }
        }
    }
}

複製代碼
相關文章
相關標籤/搜索