• 链表


    链表

    链表与数组的对比

    • 存储
      • 数组需要一块连续的内存空间来存储
      • 链表不需要一块连续的内存空间,而是通过“指针”将一组零散的内存块串联起来

    链表结构

    单链表

    • 结点
      • 链表的每个结点,除了存储数据,还需要记录链上下一个结点的地址
    • 后继指针 next
      • 记录下一个结点地址的指针叫做后继指针
    • 头节点
      • 记录链表的基地址,可以通过基地址遍历得到整条链表
    • 尾结点
      • 尾结点的指针不是指向下一个结点,而是指向一个空地址NULL,表示这是链上的最后一个

    链表的操作

    • 支持数据的查找、插入和删除操作
      • 在链表中插入和删除一个数据的时间复杂度是O(1)
      • 链表查找需要O(n)的时间复杂度

    双向链表

    双向链表是一种特殊的单链表。跟单链表的区别是尾结点。
    单链表的尾结点指向空地址,表示为最后的节点。
    循环链表的尾结点指针指向链表的头节点。

    循环链表

    单链表只有一个方向,节点只有一个后继指针指向后面的节点。
    双向链表有两个方向,每个结点除了一个后继指针next指向后面的结点,还有一个前驱指针prev指向前面的节点。

    应用场景

    LRU缓存淘汰算法

    • 链表的一个经典应用场景是LRU缓存淘汰算法
      缓存淘汰算法用来解决在有限大小的缓存内,当缓存用满时,数据的清理策略。常见的缓存淘汰策略有三种:

      • 先进先出策略FIFO(First In, First Out)
      • 最少使用策略LFU(Least Frequently Used)
      • 最近最少使用策略LRU(Least Recently Used)
    • 我们通过力扣上的一道题来看如何应用
      Leetcode LRU缓存机制

    • GO实现

    package main
    
    import "fmt"
    
    //LRU HashLink
    type LRUCache struct {
        hashmap map[int]*LinkNode
        LinkList LinkList
    }
    
    //链表
    type LinkList struct {
        head *LinkNode//指向第一个节点,通过遍历节点next到所有节点
        tail *LinkNode//指向最后一个节点,通过遍历prev到所有节点
        size int
        capacity int
    }
    
    //链表节点
    type LinkNode struct {
        key int//对应map中的key
        value int
        prev *LinkNode
        next *LinkNode
    }
    
    func Constructor(capacity int) LRUCache {
        var LRUCache LRUCache
        LRUCache.InitLink(capacity)
        return LRUCache
    }
    
    func (this *LRUCache) Get(key int) int {
        if(this.hashmap[key]==nil) {
            return -1
        }
        //放到最新
        this.MakeNodeRecenty(key)
        return this.hashmap[key].value
    }
    
    func (this *LRUCache) Put(key int, value int)  {
        if(this.hashmap[key]!=nil) {
            this.RemoveLink(key)
        }
        this.AddLink(key,value)
    }
    
    //初始化链表
    func (l *LRUCache) InitLink(capacity int) {
        l.hashmap = make(map[int]*LinkNode)
    
        head := &LinkNode{}
        tail := &LinkNode{}
    
        l.LinkList.head = head
        l.LinkList.tail = tail
    
        l.LinkList.head.next = tail
        l.LinkList.tail.prev = head
    
        l.LinkList.size = 0
        l.LinkList.capacity = capacity
    }
    
    //添加链表节点
    //添加到链表尾部
    //尾部节点即是最新的节点
    func (l *LRUCache) AddLink(key int, value int) {
        //存储已满,删除旧的一个节点
        if(l.LinkList.size>=l.LinkList.capacity) {
            l.RemoveLastLink()
        }
    
        newNode := &LinkNode{}
        newNode.key = key
        newNode.value = value
        l.LinkList.size++
        l.hashmap[key] = newNode
    }
    
    //将节点放到最新位置
    func (l *LRUCache) MakeNodeRecenty(key int) {
        node := l.hashmap[key]
        value := node.value
        l.RemoveLink(key)
        l.AddLink(key, value)
    }
    
    //删除指定key
    func (l *LRUCache) RemoveLink(key int) {
        node := l.hashmap[key]
    
        node.prev.next = node.next
        node.next.prev = node.prev
    
        l.hashmap[key] = nil
        l.LinkList.size--
    }
    
    //删除最旧的节点
    //最旧的节点即是头部节点
    func (l *LRUCache) RemoveLastLink() {
        node := l.LinkList.head.next
        key := node.key
        node.next.prev = node.prev
        node.prev.next = node.next
        l.hashmap[key] = nil
        l.LinkList.size--
    }
    
    func main() {
        obj := Constructor(1)
        obj.Put(2,1)
        param_1 := obj.Get(2)
        obj.Put(3,2)
        param_2 := obj.Get(2)
        param_3 := obj.Get(3)
    
    
        fmt.Printf("%v", param_1)
        fmt.Printf("%v", param_2)
        fmt.Printf("%v", param_3)
    }
    

    链表反转

    • 同样我们通过力扣上的一道题来看看
      链表反转
    • GO实现
    /**
     * Definition for singly-linked list.
     * type ListNode struct {
     *     Val int
     *     Next *ListNode
     * }
     */
    func reverseList(head *ListNode) *ListNode {
        if(head == nil || head.Next==nil) {
            return head
        }
    
        current := head.Next
        prev := head
        prev.Next = nil
        for current!=nil {
            tmpnode := current.Next
            current.Next = prev
            prev = current
            current = tmpnode
        }
    
        head = prev
    
        return head
    }
    

    参考

    数据结构与算法之美
    大话数据结构

  • 相关阅读:
    java基础面试
    GC 相关 ---垃圾回收机制
    JVM 内存管理--jvm内存的划分
    Java 中创建一个对象的过程?
    双亲委派模型(Parent Delegation Model)?
    4、ReactJs基础知识04--state&&生命周期
    3、ReactJs基础知识03--组件&&props
    2、ReactJs基础知识02--元素渲染
    1、ReactJs基础知识01--入门使用
    再次理解JSONP
  • 原文地址:https://www.cnblogs.com/biby/p/15208430.html
Copyright © 2020-2023  润新知