5. 链表

1. 链表(上):如何实现LRU缓存淘汰算法?

1.1. 常见的缓存策略

  • 先进先出策略 FIFO(First In,First Out)
  • 最少使用策略 LFU(Least Frequently Used)
  • 最近最少使用策略 LRU(Least Recently Used)

1.2. 常用的链表结构

  • 单链表

  • 循环链表

    • 约瑟夫问题
  • 双向链表

1.3. 链表和数组性能对比

  • 数组:插入删除时间复杂度O(n), 随机访问时间复杂度O(1)
  • 链表:插入删除时间复杂度O(1), 随机访问时间复杂度O(n)

2. 链表(下):如何轻松写出正确的链表代码?

2.1. 写好链表的几种技巧

  • 理解指针和引用的含义
  • 警惕指针丢失和内存泄漏
  • 利用哨兵简化实现难度
  • 重点留意边界条件处理
  • 举例画图,辅助思考
  • 多写多练,没有捷径

2.2. 五个常见的链表操作

  • 单链表反转
  • 链表中环的检测
  • 两个有序链表合并
  • 删除链表倒数第n个节点
  • 求链表的中间节点

3. 案例

  • java
import java.util.HashMap;
import java.util.Map;

/**
 * 基于数组实现LRU缓存
 * 1. 空间复杂度为O(n)
 * 2. 时间复杂度为O(n)
 * 3. 不支持null的缓存
 */
public class LRUBasedArray<T> {
    // 把1左移了3位  1 x 2^3
    private static final int DEFAULT_CAPACITY = (1 << 3);
    // 数组大小
    private int size;
    // 数组内容
    private T[] value;
    // 缓存集合
    private Map<T, Integer> holder;

    public LRUBasedArray() {
        this(DEFAULT_CAPACITY);
    }

    public LRUBasedArray(int capacity) {
        this.value = (T[]) new Object[capacity];
        this.size = 0;
        this.holder = new HashMap<T, Integer>(capacity);
    }

    /**
     * 模拟访问某个值
     * @param object
     */
    public void offer(T object) {
        if(object == null) {
            throw new IllegalArgumentException("该缓存容器不支持null!");
        }
        Integer index = holder.get(object);
        if(index == null) {
            if (isFull()) {
                removeAndCache(object);
            }else {
                cache(object, size);
            }
        } else {
            update(index);
        }
    }

    /**
     * 缓存满的情况,踢出最后一个元素后,再缓存到数组的头部
     * @param object
     */
    public void removeAndCache(T object) {
        T key = value[--size];
        holder.remove(key);
        cache(object, size);
    }

    /**
     * 若缓存中有指定的值,则更新位置
     * @param end
     */
    public void update(int end) {
        T target = value[end];
        rightShift(end);
        value[0] = target;
        holder.put(target, 0);
    }

    /**
     * 缓存数据到头部,但要先右移
     * @param object
     * @param end
     */
    public void cache(T object, int end) {
        rightShift(end);
        value[0] = object;
        holder.put(object, 0);
        size ++;
    }

    /**
     * end左边的数据统一右移一位
     * @param end
     */
    private void rightShift(int end) {
        for (int i = end -1; i >= 0; i--) {
            value[i + 1] = value[i];
            holder.put(value[i], i + 1);
        }
    }

    public boolean isContain(T object) {
        return holder.containsKey(object);
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public boolean isFull() {
        return size == value.length;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for(int i = 0;i < size; i ++) {
            sb.append(value[i]).append(" ");
        }
        return sb.toString();
    }

    public static void main(String[] args) {
//        testWithException();
//        testDefaultConstructor();
        testSpecifiedConstructor(4);
    }

    private static void testWithException() {
        LRUBasedArray<Integer>lru = new LRUBasedArray<>();
        lru.offer(null);
    }

    public static void testDefaultConstructor() {
        System.out.println("======无参测试========");
        LRUBasedArray<Integer> lru = new LRUBasedArray<Integer>();
        lru.offer(1);
        lru.offer(2);
        lru.offer(3);
        lru.offer(4);
        lru.offer(2);
        System.out.println(lru);
        lru.offer(6);
        lru.offer(7);
        lru.offer(8);
        lru.offer(9);
        System.out.println(lru);
    }

    public static void testSpecifiedConstructor(int capacity) {
        System.out.println("======有参测试========");
        LRUBasedArray<Integer> lru = new LRUBasedArray<Integer>(capacity);
        lru.offer(1);
        System.out.println(lru);
        lru.offer(2);
        System.out.println(lru);
        lru.offer(3);
        System.out.println(lru);
        lru.offer(4);
        System.out.println(lru);
        lru.offer(2);
        System.out.println(lru);
        lru.offer(4);
        System.out.println(lru);
        lru.offer(7);
        System.out.println(lru);
        lru.offer(1);
        System.out.println(lru);
        lru.offer(2);
        System.out.println(lru);
    }


}

import java.util.Scanner;

/**
 * 基于单链表的LRU算法
 * @param <T>
 */
public class LRUBaseLinkedList<T> {
    // 默认链表容量
    private static final Integer DEFAULT_CAPACITY = (1 << 3);
    // 头结点
    private SNode<T> headNode;
    // 链表长度
    private Integer length;
    // 链表容量
    private Integer capacity;

    public LRUBaseLinkedList() {
        this(DEFAULT_CAPACITY);
    }

    public LRUBaseLinkedList(Integer capacity) {
        this.headNode = new SNode<>();
        this.capacity = capacity;
        this.length = 0;
    }

    public void add(T data) {
        SNode preNode = findPreNode(data);
        // 链表中存在, 删除原数据, 再插入到链表的头部
        if (preNode != null) {
            deleteElemOptim(preNode);
            insertElemAtBegin(data);
        }else {
            if (length >= this.capacity) {
                // 删除尾结点
                deleteElemAtEnd();
            }
            insertElemAtBegin(data);
        }
    }

    /**
     * 删除尾结点
     */
    private void deleteElemAtEnd() {
        SNode ptr = headNode;
        // 空链表直接返回
        if (ptr.getNext() == null) return;
        // 倒数第二个结点
        while(ptr.getNext().getNext() != null) {
            ptr = ptr.getNext();
        }

        SNode tmp = ptr.getNext();
        ptr.setNext(null);
        tmp = null;
        length --;
    }

    private void printAll() {
       SNode node = headNode.getNext();
       while(node != null) {
           System.out.print(node.getElement() + " ");
           node = node.getNext();
       }
       System.out.println();
    }

    /**
     * 删除preNode结点下一个元素
     * @param preNode
     */
    private void deleteElemOptim(SNode preNode) {
        SNode temp = preNode.getNext();
        preNode.setNext(temp.getNext());
        temp = null;
        length --;
    }

    /**
     * 链表头部插入结点
     * @param data
     */
    private void insertElemAtBegin(T data) {
        SNode next = headNode.getNext();
        headNode.setNext(new SNode(data, next));
        length ++;
    }

    /**
     * 获取查找到元素的前一个结点
     * @param data
     * @return
     */
    public SNode findPreNode(T data) {
        SNode node = headNode;
        while(node.getNext() != null) {
            if (data.equals(node.getNext().getElement())) {
                return node;
            }
            node = node.getNext();
        }
        return null;
    }

    public class SNode<T> {
        private T element;
        private SNode next;

        public SNode(T element) {
            this.element = element;
        }

        public SNode(T element, SNode next) {
            this.element = element;
            this.next = next;
        }

        public SNode() {
            this.next = null;
        }

        public T getElement() {
            return element;
        }

        public void setElement(T element) {
            this.element = element;
        }

        public SNode getNext() {
            return next;
        }

        public void setNext(SNode next) {
            this.next = next;
        }
    }

    public static void main(String[] args) {
        LRUBaseLinkedList list = new LRUBaseLinkedList();
        Scanner sc = new Scanner(System.in);
        while(true) {
            list.add(sc.nextInt());
            list.printAll();
        }
    }
}

/**
 * 1. 单链表的插入、删除、查找操作
 * 2. 链表中存储的都是int类型数据
 * 3. 判断回文
 */
public class SinglyLinkedList {

    private Node head = null;

    public Node findByValue(int value) {
        Node p = head;
        while(p != null && p.data != value) {
            p = p.next;
        }
        return p;
    }

    public Node findByIndex(int index) {
        Node p = head;
        int pos = 0;
        while(p != null && pos != index) {
            p = p.next;
            ++pos;
        }
        return p;
    }

    /**
     * 无头结点
     * 表头部插入
     * 这种操作将与输入的顺序相反, 逆序
     */
    public void insertToHead(int value) {
        Node newNode = new Node(value, null);
        insertToHead(newNode);
    }

    public void insertToHead(Node newNode) {
        if (head == null) {
            head = newNode;
        } else {
            newNode.next = head;
            head = newNode;
        }
    }

    public void insertTail(int value) {
        Node newNode = new Node(value, null);
        // 空链表, 可以插入新节点作为head, 也可以不操作
        if (head == null) {
            head = newNode;
        } else {
            Node q = head;
            while(q.next != null) {
                q = q.next;
            }
            q.next = newNode;

        }
    }

    public void insertAfter(Node p, int value) {
        Node newNode = new Node(value, null);
        insertAfter(p, newNode);
    }

    public void insertAfter(Node p, Node newNode) {
        if (p == null) return;
        newNode.next = p.next;
        p.next = newNode;
    }

    public void insertBefore(Node p, Node newNode) {
        if (p == null) return;
        if (head == p) {
            insertToHead(newNode);
            return;
        }
        Node q = head;
        while(q != null && q.next != p) {
            q = q.next;
        }

        if (q == null) return;

        newNode.next = p;
        q.next = newNode;
    }

    public void insertBefore(Node p, int value) {
        Node newNode = new Node(value, null);
        insertBefore(p,newNode);
    }

    public void deleteByNode(Node p) {
        if(p == null || head == null) return;
        if (p == head) {
            head = head.next;
            return;
        }

        Node q = head;
        while(q != null && q.next != p) {
            q = q.next;
        }
        if (q == null) return;
        q.next = q.next.next;
    }

    /**
     * 删除第一个值相等的结点
     * @param value
     */
    public void deleteByValue(int value) {
        if (head == null) return;
        Node p = head;
        Node q = null;
        while(p != null && p.data != value) {
            q = p;
            p = p.next;
        }
        if (p == null) return;
        if (q == null) {
            head = head.next;
        } else {
            q.next = q.next.next;
        }
    }

    public void printAll() {
        Node p = head;
        while(p != null) {
            System.out.print(p.data + " ");
            p = p.next;
        }
        System.out.println();
    }

    public static Node createNode(int value) {
        return new Node(value, null);
    }

    // 判断true或者false
    public boolean IFResult(Node left, Node right) {
        Node l = left;
        Node r = right;
        System.out.println("left_:"+l.data);
        System.out.println("right_:"+r.data);
        while (l != null && r != null) {
            if (l.data == r.data) {
                l = l.next;
                r = r.next;
                continue;
            }else {
                break;
            }
        }
        if (l == null && r == null) {
            return true;
        } else {
            return false;
        }
    }

    public boolean palindrome() {
        if (head == null) {
            return false;
        } else {
            System.out.println("开始执行找到中间节点");
            Node p = head;
            Node q = head;
            // 只有一个结点
            if (p.next == null) {
                return true;
            }
            while(q.next != null && q.next.next != null) {
                p = p.next;
                q = q.next.next;
            }

            System.out.println("中间节点" + p.data);
            System.out.println("开始执行奇数节点的回文判断");

            Node leftLink = null;
            Node rightLink = null;
            if(q.next == null) {
                // p 一定为整个链表的中点,且节点数目为奇数
                rightLink = p.next;
                leftLink = inverseLinkList(p).next;
                System.out.println("左边第一个节点"+leftLink.data);
                System.out.println("右边第一个节点"+rightLink.data);
            } else {
                //p q 均为中点
                rightLink = p.next;
                leftLink = inverseLinkList(p);
            }
            return IFResult(leftLink, rightLink);
        }
    }

    // 无头结点的链表翻转
    public Node inverseLinkList(Node p) {
        Node pre = null;
        Node r = head;
        System.out.println("z---" + r.data);
        Node next = null;
        while (r != p) {
            next = r.next;
            r.next = pre;
            pre = r;
            r = next;
        }
        r.next = pre;
        return r;
    }
    // 带头结点的链表翻转
    public Node inverseLinkList_head(Node p) {
        // head 为新建的一个头结点
        Node head = new Node(9999, null);
        // p 为原来整个链表的头结点,现在head指向 整个链表
        head.next = p;
        // 带头结点的链表翻转等价于从第二个元素开始重新头插法建立链表
        Node cur = p.next;
        p.next = null;
        Node next = null;

        while (cur != null) {
            next = cur.next;
            cur.next = head.next;
            head.next = cur;
            System.out.println("first " + head.data);
            cur = next;
        }
        // 返回左半部分的重点之前的那个结点,从此处开始同步像两边比较
        return head;

    }


    public static class Node {
        private int data;
        private Node next;

        public Node() {
        }

        public Node(int data, Node next) {
            this.data = data;
            this.next = next;
        }

        public int getData() {
            return data;
        }
    }

    public boolean isLoop(Node head) {
        if(head == null) return false;
        Node slow = head;
        Node fast = head;
        
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        SinglyLinkedList link = new SinglyLinkedList();
        int data[] = {1,2,5,3,1};
        for (int i =0; i < data.length; i++) {
            link.insertTail(data[i]);
        }
        link.printAll();

        if (link.palindrome()){
            System.out.println("回文");
        }else{
            System.out.println("不是回文");
        }
    }
}

  • Scala
import scala.util.control.Breaks._

class Node(var data: Int, var next: Option[Node])

class SinglyLinkedListScala(var headOpt: Option[Node]) {

  def this() = this(None)

  def findByValue(value: Int): Option[Node] = {
    headOpt.flatMap(head => {
      var node = head
      while (!node.data.equals(value) && node.next.isDefined) {
        node = node.next.get
      }

      if (node.data.equals(value)) {
        return Some(node)
      } else {
        return None
      }
    })
  }
  
  def insertToHead(value: Int): Unit = {
    val newNode = new Node(value, None)
    insertToHead(newNode)
  }
  
  def insertToHead(newNode: Node): Unit = {
    headOpt match {
      case None =>
        // it's an empty linked list, make new node as head
        headOpt = Some(newNode)
      case Some(head) =>
        newNode.next = Some(head)
        headOpt = Some(newNode)
    }
  }

  def insertTail(value: Int): Unit = {
    val newNode = new Node(value, None)
    insertTail(newNode)
  }

  def insertTail(newNode: Node): Unit = {
    headOpt match {
      case None =>
        // it's an empty linked list, make new node as head
        headOpt = Some(newNode)
      case Some(head) =>
        // need to start fo find from head to current tail
        var node = head;
        while(node.next.isDefined) {
          node = node.next.get
        }
        // now node is the tail as node.next is None, add new tail
        node.next = Some(newNode)
    }
  }

  def deleteByNode(node: Node): Unit = {
    headOpt.map(head => {
      if (head.equals(node)) {
        headOpt = node.next
      } else {
        var p: Node = head;
        while(p.next.isDefined && !p.next.get.equals(node)) {
          p = p.next.get
        }

        if(p.next.isEmpty) {
          throw new IllegalArgumentException("could not find given node.")
        }
        p.next = node.next


      }
    })
  }

  def mkString(): String = {
    headOpt.map(head => {
      var node = head
      val result = new StringBuilder

      while(node.next.isDefined) {
        result.append(node.data)
        node = node.next.get
      }

      result.append(node.data)
      result.mkString
    }).getOrElse("")
  }

  // 回文判断
  def isPalindrome(): Boolean = {
    headOpt match {
      case None => false
      case Some(head) =>
        var p: Node = head
        var q: Node = head

        if (p.next.isEmpty) {
          return true
        }

        while(q.next.isDefined && q.next.get.next.isDefined) {
          p = p.next.get
          q = q.next.get.next.get
        }

        var leftLink: Option[Node] = None
        var rightLink: Option[Node] = None
        q.next match {
          case None =>
            rightLink = p.next
            leftLink = inverseLink(p).next
          case Some(_) =>
            rightLink = p.next
            leftLink = Some(inverseLink(p))
        }
        compareLinkedNodes(leftLink, rightLink)

    }
  }

  def compareLinkedNodes(leftLink: Option[Node], rightLink:Option[Node]): Boolean = {
    var left = leftLink
    var right = rightLink
    breakable {
      while(left.isDefined && right.isDefined) {
        if(!left.get.data.equals(right.get.data)) {
          break
        }
        left = left.get.next
        right = right.get.next
      }
    }
    left.isEmpty && right.isEmpty
  }

  // 翻转链表
  def inverseLink(node: Node): Node = {
    if(headOpt.isEmpty) {
      throw new IllegalArgumentException("list is empty.")
    }

    var pre: Option[Node] = None
    var next: Option[Node] = None
    var current: Option[Node] = headOpt

    while (current.isDefined && !current.get.equals(node)) {
      next = current.get.next
      current.get.next = pre
      pre = current
      current = next
    }

    current.get.next = pre
    current.get
  }
}

object SinglyLinkedListScala {
  def main(args: Array[String]): Unit = {
    val lst = new SinglyLinkedListScala()
    lst.insertTail(1)
    lst.insertTail(2)
    lst.insertTail(3)
    lst.insertTail(1)
    val result = lst.mkString()
    print(result)

    print(lst.isPalindrome())
  }
}

  • python
# 1.单链表的插入、删除、查找操作;
# 2.链表中存储的数据类型是Int
#
# Author: fan


class Node:
    """链表结构的Node结点"""

    def __init__(self, data, next_node=None):
        """Node结点的初始化方法
        参数:
            data:存储的数据
            next_node:下一个Node结点的引用地址
        """
        self.__data = data
        self.__next = next_node

    @property
    def data(self):
        """Node结点存储数据的获取
        返回:
            当前Node结点存储的数据
        """
        return self.__data

    @data.setter
    def data(self, data):
        """Node结点存储数据的设置方法
        参数:
            data:新的存储数据
        """
        self.__data = data

    @property
    def next_node(self):
        """获取Node结点的next指针值
        返回:
            next指针数据
        """
        return self.__next

    @next_node.setter
    def next_node(self, next_node):
        """Node结点next指针的修改方法
        参数:
            next:新的下一个Node结点的引用
        """
        self.__next = next_node


class SinglyLinkedList1:
    """单向链表"""

    @property
    def head(self):
        return self.__head

    @head.setter
    def head(self, head):
        self.__head = head

    def __init__(self):
        """单向链表初始化方法"""
        self.__head = None
        self.__len = 0

    def find_by_value(self, value):
        """按照数据值在单向列表中查找
        参数:
            value:查找的数据
        返回:
            Node
        """
        cur = self.__head
        while cur is not None and cur.data != value:
            cur = cur.next_node
        return cur

    def find_by_index(self, index):
        """按照索引值在链表中查找
        参数:
            index:索引值
        返回:
            Node
        """
        cur = self.__head
        pos = 0
        while cur is not None and pos != index:
            cur = cur.next_node
            pos += 1
        return cur

    def insert_to_head(self, value):
        """在链表的头部插入一个存储value数值的Node结点
        参数:
            value:将要存储的数据
        """
        cur = Node(value)
        cur.next_node = self.__head
        self.__head = cur
        self.__len += 1

    def insert_after(self, node, value):
        """在链表的某个指定Node结点之后插入一个存储value数据的Node结点
        参数:
            node:指定的一个node结点
            value:将要存储在新Node结点中的数据
        """
        if node is None:
            print("current node is invalid.")
            return

        new_node = Node(value)
        new_node.next_node = node.next_node
        node.next_node = new_node
        self.__len += 1

    def insert_before(self, node, value):
        """在链表的某个指定Node结点之前插入一个存储value数据的Node结点
        参数:
            node:指定的一个node结点
            value:将要存储在新Node结点中的数据
        """
        if node is None or self.__head is None:
            print("node is None.")
            return

        if node == self.__head:
            self.insert_to_head(value)
            return

        new_node = Node(value)
        pre = self.__head
        not_found = False

        while pre.next_node != node:
            if pre.next_node is None:
                not_found = True
                break
            else:
                pre = pre.next_node

        if not not_found:
            pre.next_node = new_node
            new_node.next_node = node
            self.__len += 1

    def delete_by_node(self, node):
        """在链表中删除指定的Node结点
        参数:
            node:指定的Node结点
        """
        if self.__head is None:
            return

        if node == self.__head:
            self.__head = node.next_node
            self.__len -= 1
            return

        pre = self.__head
        not_found = False  # 如果在整个链表当中都没有找到相应的结点,则标记为True

        while pre.next_node != node:
            if pre.next_node is None:
                not_found = True
                break
            else:
                pre = pre.next_node

        if not not_found:
            pre.next_node = node.next_node
            self.__len -= 1

    def delete_by_value(self, value):
        """在链表中删除指定存储数据的Node结点
        参数:
            value:指定的存储数据
        """
        if self.__head is None:
            return

        if self.__head.data == value:
            self.__head = self.__head.next_node
            self.__len -= 1

        pre = self.__head
        node = self.__head.next_node
        not_found = False

        while node.data != value:
            if node.next_node is None:
                not_found = True
                break
            else:
                pre = node
                node = node.next_node

        if not not_found:
            pre.next_node = node.next_node
            self.__len -= 1

    def delete_last_n_node(self, n):
        """删除链表中倒数第N个结点(0 < n <= self.__len)
        思路:
            设置快、慢两个指针、快指针,快指针先行,慢指针不动;当快指针跨了N步以后,快、慢指针同时往链表尾部移动,
            当快指针到达链表尾部的时候,慢指针所指向的就是链表倒数第N个结点
        参数:
            n:需要删除的倒数第N个结点
        """
        fast = self.__head
        slow = self.__head
        step = 1
        tmp = None

        if n <= 0 or n > self.__len:
            print("超出链表范围")
            return

        while step < n:
            fast = fast.next_node
            step += 1

        while fast.next_node is not None:
            tmp = slow
            fast = fast.next_node
            slow = slow.next_node
        else:  # python 特有的语法,当while条件不满足时会执行一次else
            if not tmp:
                self.__head = self.__head.next_node  # 倒数最后一个结点(第一个结点)
                self.__len -= 1
                return

        tmp.next_node = slow.next_node

    def find_mid_node(self):
        """查找链表中的中间结点(奇数返回中间,偶数返回左半边元素)
        思想:
            设置快、慢两种指针,快指针每次跨两步,慢指针每次跨一步,则当快指针到达链表尾部的时候,慢指针指向链表的
            中间结点
        返回:
            一个新的Node结点
        """
        if self.__head is None:
            print("空链表")
            return

        fast = self.__head
        slow = self.__head

        while fast.next_node is not None and fast.next_node.next_node is not None:
            slow = slow.next_node
            fast = fast.next_node.next_node

        return slow

    def reversed_self(self):
        """翻转链表自身"""
        if self.__head is None or self.__head.next_node is None:
            return

        pre = self.__head
        cur = self.__head.next_node

        while cur is not None:
            pre, cur = self.__reversed_with_two_node(pre, cur)

        self.__head.next_node = None
        self.__head = pre

    @staticmethod
    def __reversed_with_two_node(pre: Node, cur: Node):
        """翻转两个相邻结点
        参数:
            pre:前一个结点
            cur:当前结点
        返回:
            (pre,cur):下一个相邻结点的元组
        """
        tmp = cur.next_node
        cur.next_node = pre
        pre = cur
        cur = tmp

        return pre, cur

    def has_ring(self):
        """检查链表当中是否有环
        思想:
            设置快、慢两种指针,快指针每次跨两步,慢指针每次跨一步,如果快指针没有与慢指针相遇而是顺利到达链表尾部
            说明没有环: 否则,存在环
        返回:
            True:有环
            False:没有环
        """
        fast = self.__head
        slow = self.__head

        while fast is not None and fast.next_node is not None:
            fast = fast.next_node
            slow = slow.next_node
            if fast == slow:
                return True

        return False

    def create_node(self, value):
        """创建一个存储value值的Node结点
        参数:
            value:将要存储在Node结点中的数据
        返回:
            Node
        """
        return Node(value)

    def print_all(self):
        """打印当前链表所有结点数据"""
        cur = self.__head

        if cur is None:
            print("当前链表还没有数据")
            return

        while cur.next_node is not None:
            print(str(cur.data) + " --> ", end="")
            cur = cur.next_node

        print(str(cur.data))


if __name__ == '__main__':
    lst = SinglyLinkedList1()

    for i in range(10):
        lst.insert_to_head(i)

    lst.print_all()
    # 查找中间结点
    node = lst.find_mid_node()
    print(node.data)
    # 删除倒数第2个结点
    lst.delete_last_n_node(2)
    lst.print_all()
    # 插入一个无效结点
    lst.insert_after(None, 10)
    # 翻转链表
    lst.reversed_self()
    lst.print_all()

    node = lst.create_node(100)
    print(node.data)



class DBListNode:
    def __init__(self, x, y):
        self.key = x
        self.val = y
        self.next = None
        self.prev = None


class LRUCache:
    """
    运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。
    它应该支持以下操作: 获取数据 get 和 写入数据 put 。
    获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
    写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。
        当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间

    哈希表+双向链表
    哈希表: 查询 O(1)
    双向链表: 有序, 增删操作 O(1)

    Author: fan
    """
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.hkeys = {}
        # self.top 和 self.tail 作为哨兵结点, 避免越界
        self.top = DBListNode(None, -1)
        self.tail = DBListNode(None, -1)
        self.top.next = self.tail
        self.tail.prev = self.top

    def get(self, key: int) -> int:
        if key in self.hkeys.keys():
            # 更新结点顺序
            cur = self.hkeys[key]
            # 跳出原位置
            cur.next.prev = cur.prev
            cur.prev.next = cur.next
            # 最近用过的置于链表的首部
            top_node = self.top.next
            self.top.next = cur
            cur.prev = self.top
            cur.next = top_node
            top_node.prev = cur

            return self.hkeys[key].val
        return -1

    def put(self, key: int, val: int):
        if key in self.hkeys.keys():
            cur = self.hkeys[key]
            cur.val = val
            # 跳出原来的位置
            cur.prev.next = cur.next
            cur.next.prev = cur.prev
            # 最近用过的置于链表首部
            top_node = self.top.next
            self.top.next = cur
            cur.prev = self.top
            cur.next = top_node
            top_node.prev = cur
        else:
            # 增加新节点到首部
            cur = DBListNode(key, val)
            self.hkeys[key] = cur
            # 最近用过的置于链表首部
            top_node = self.top.next
            self.top.next = cur
            cur.prev = self.top
            cur.next = top_node
            top_node.prev = cur

            if len(self.hkeys.keys()) > self.capacity:
                self.hkeys.pop(self.tail.prev.key)
                # 去掉原来的尾结点
                self.tail.prev.prev.next = self.tail
                self.tail.prev = self.tail.prev.prev



    def __repr__(self):
        values = []
        p = self.top.next
        while p.next:
            values.append(str(p.val))
            p = p.next
        return '->'.join(values)


if __name__ == '__main__':
    cache = LRUCache(2)
    cache.put(1, 1)
    cache.put(2, 2)
    print(cache)
    cache.get(1)
    cache.put(3,3)
    print(cache)
    cache.get(2)
    print(cache)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值