鏈表2


title: 鏈表2
date: 2019-02-19 22:17:11
tags: 鏈表
copyright: true
categories: 算法
---java

通過上一章的學習,咱們基本瞭解了鏈表的特性,如今咱們就兩個練習增強對鏈表這種數據結構的理解和應用,發揮你的才智吧!node


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個鏈表,比較對應的字符是否相等,若相等,則是水仙花字串,不然,不是。

相關文章
相關標籤/搜索