链表2

链表2

经过上一章的学习,我们基本了解了链表的特性,现在我们就两个练习加强对链表这种数据结构的理解和应用,发挥你的才智吧!


Q1 如何分别用链表和数组实现LRU缓冲淘汰策略?

  • 什么是缓存? 缓存是一种提高数据读取性能的技术,在硬件设计、软件开发中都有着非广泛的应用,比如常见的CPU缓存、数据库缓存、浏览器缓存等等。
  • 为什么使用缓存 即缓存的特点 缓存的大小是有限的,当缓存被用满时,哪些数据应该被清理出去,哪些数据应该被保留?就需要用到缓存淘汰策略。
  • 什么是缓存淘汰策略? 指的是当缓存被用满时清理数据的优先顺序。
  • 有哪些缓存淘汰策略? 常见的3种包括先进先出策略FIFO(First In,First Out)、最少使用策略LFU(Least Frequently Used)、最近最少使用策略LRU(Least Recently Used)。

链表实现LRU缓存淘汰策略

当访问的数据没有存储在缓存的链表中时,直接将数据插入链表表头,时间复杂度为O(1);当访问的数据存在于存储的链表中时,将该数据对应的节点,插入到链表表头,时间复杂度为O(n)。如果缓存被占满,则从链表尾部的数据开始清理,时间复杂度为O(1)。
代码实现:

    public class LRUBaseLinkedList<T> {
    
        /**
         * 默认链表容量
         */
        private final static Integer DEFAULT_CAPACITY = 10;
    
        /**
         * 头结点
         */
        private SNode<T> headNode;
    
        /**
         * 链表长度
         */
        private Integer length;
    
        /**
         * 链表容量
         */
        private Integer capacity;
    
        public LRUBaseLinkedList() {
            this.headNode = new SNode<>();
            this.capacity = DEFAULT_CAPACITY;
            this.length = 0;
        }
    
        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);
                intsertElemAtBegin(data);
            } else {
                if (length >= this.capacity) {
                    //删除尾结点
                    deleteElemAtEnd();
                }
                intsertElemAtBegin(data);
            }
        }
    
        /**
         * 删除preNode结点下一个元素
         *
         * @param preNode
         */
        private void deleteElemOptim(SNode preNode) {
            SNode temp = preNode.getNext();
            preNode.setNext(temp.getNext());
            temp = null;
            length--;
        }
    
        /**
         * 链表头部插入节点
         *
         * @param data
         */
        private void intsertElemAtBegin(T data) {
            SNode next = headNode.getNext();
            headNode.setNext(new SNode(data, next));
            length++;
        }
    
        /**
         * 获取查找到元素的前一个结点
         *
         * @param data
         * @return
         */
        private SNode findPreNode(T data) {
            SNode node = headNode;
            while (node.getNext() != null) {
                if (data.equals(node.getNext().getElement())) {
                    return node;
                }
                node = node.getNext();
            }
            return null;
        }
    
        /**
         * 删除尾结点
         */
        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();
        }
    
        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();
            }
        }
    }

数组实现LRU缓存淘汰策略

  • 方式一:首位置保存最新访问数据,末尾位置优先清理 当访问的数据未存在于缓存的数组中时,直接将数据插入数组第一个元素位置,此时数组所有元素需要向后移动1个位置,时间复杂度为O(n);当访问的数据存在于缓存的数组中时,查找到数据并将其插入数组的第一个位置,此时亦需移动数组元素,时间复杂度为O(n)。缓存用满时,则清理掉末尾的数据,时间复杂度为O(1)。

  • 方式二:首位置优先清理,末尾位置保存最新访问数据 当访问的数据未存在于缓存的数组中时,直接将数据添加进数组作为当前最有一个元素时间复杂度为O(1);当访问的数据存在于缓存的数组中时,查找到数据并将其插入当前数组最后一个元素的位置,此时亦需移动数组元素,时间复杂度为O(n)。缓存用满时,则清理掉数组首位置的元素,且剩余数组元素需整体前移一位,时间复杂度为O(n)。(优化:清理的时候可以考虑一次性清理一定数量,从而降低清理次数,提高性能。)

  • ps 如果有小伙伴对缓存算法感兴趣,可以更深入地了解一下==> 友情链接

Q2 如何通过单链表实现“判断某个字符串是否为回文字符串”?

  1. 前提:字符串以单个字符的形式存储在单链表中。

  2. 遍历链表,判断字符个数是否为奇数,若为偶数,则不是。

  3. 将链表中的字符倒序存储一份在另一个链表中。

  4. 同步遍历2个链表,比较对应的字符是否相等,若相等,则是水仙花字串,否则,不是。

转载于:https://www.cnblogs.com/jiaweixie/p/10403786.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值