1. 程式人生 > >自己一次失敗的編碼= =

自己一次失敗的編碼= =


public class LFUCache {
    private final Node first;
    private final Node last;
    private final HashRep cache;
    private final HashMap<Integer,Node> count;
    LFUCache(int capacity){
        cache = new HashRep(capacity);
        count = new HashMap<>();
        first = new Node(0,0);
        first.count=-1;
        last = new Node(0,0);
        last.count=-1;
        first.next = last;
        last.pre = first;
    }

    /**
     * 首先,get並返回值
     * 先從cache中get,如果為null,return -1
     * 先看該節點在不在count中
     *      如果在,刪除掉該count
     * count++
     * 我們get一下這個++後的count值
     *      如果有,move連結串列,設定count
     */
    public int get(int key) {
        if (cache.capacity<1){
            return -1;
        }
        Node thisNode = cache.get(key);
        if (thisNode==null){
            return -1;
        }
        Node node = count.get(thisNode.count);

        if (node.key==thisNode.key){
            if (thisNode.next.count==thisNode.count){
                count.put(thisNode.count,thisNode.next);
            }else {
                count.remove(thisNode.count);
            }
        }
        thisNode.count++;
        Node nextNode = count.get(thisNode.count);
        if (nextNode!=null){
            thisNode.move(nextNode);
        }else if (node.key!=thisNode.key){
            thisNode.move(node);
        }
        count.put(thisNode.count,thisNode);
        return thisNode.val;
    }

    /**
     * 1.工具類比較智慧,new Node,直接put
     * 2.返回0,只是修改了值,不做處理
     * 3.返回1,新增成功,需要把他 放在 count[0] 前面,並把count[0]改成新節點
     * 4.返回-1.很難受,我們要先刪除最後一個節點
     *      cache 中刪掉 remove方法
     *      節點.del()刪除連結串列中的引用關係
     *      如果在count中,直接刪除,因為它本來就在連結串列尾,不可能存在與他相同count
     *      試圖放在count[0] 前面,如果count[0]為空就放在last前面,更新count[0]
     */
    public void put(int key, int value) {
        if (cache.capacity<1){
            return;
        }
        Node thisNode = new Node(key, value);
        int sign = cache.put(thisNode);
        if (sign == 0){
            return;
        }
        if (sign==-1){
            if (count.get(last.pre.count)==last.pre){
                count.remove(last.pre.count);
            }
            cache.remove(last.pre.key);
            last.pre.del();
        }
        Node nextNode = count.get(0);
        if (nextNode==null){
            nextNode=last;
        }
        thisNode.move(nextNode);
        count.put(0,thisNode);
    }
}

/**
 * 這個類定義容器類,只維護節點的值,以及訪問次數
 * 不能維護任何連結串列順序
 * 實現LFU仍然需要一個對映來維護訪問次數對應連結串列的位置
 * 我們也可使用該工具類來儲存對映  : 待定
 */
class HashRep {
    final int capacity;
    private int size;
    private final int hashKey;
    private final Node[] rep;

    HashRep(int capacity) {
        this.capacity = capacity;
        size = 0;
        int n = capacity - 1;
        int p = 1;
        while (p < 17) {
            n |= n >>> p;
            p <<= 1;
        }
        hashKey = n;
        rep = new Node[hashKey + 1];
    }

    /**
     * hash取index,迴圈取值,沒取到返回-1,取到返回值
     * 不對count做操作
     * 單純一個集合類
     */
     Node get(int key) {
        int index = key & hashKey;
        Node aimNode = rep[index];
        while (aimNode != null && aimNode.key != key) {
            aimNode = aimNode.get;
        }
        return aimNode;
    }

    /**
     * 返回0 存在key更改數值
     * 返回1 新增
     * 返回-1 新增,但容量溢位
     */
     int put(Node node) {
        int key = node.key;
        int index = key & hashKey;
        //三種情況: 1.index位置上為null  2. 往後找,找到了key   3.沒有找到key相等的,取最後一個
        Node aimNode = rep[index];
        if (aimNode==null){
            rep[index] = node;
        }else {
            if (aimNode.key==key){
                aimNode.val = node.val;
                return 0;
            }
            while(aimNode.get!=null){
                //找到了key,更改值
                if (aimNode.key==key){
                    aimNode.val = node.val;
                    return 0;
                }
                aimNode=aimNode.get;
            }
            aimNode.get = node;
        }
        size++;
        return size>capacity?-1:1;
    }

    /**
     * 如果在桶上面,桶下面沒節點 直接刪桶 size--
     * 如果在桶上面,桶下面有節點,把桶下面那個移上來 size--
     * 如果在桶下的節點之間,上面的get->這個的get    size--
     * 如果在桶末尾,上面那個節點指向這個節點的指標幹掉 size--
     */
    protected void remove(int key){
        //一切為效能,不能遍歷兩邊連結串列
        int index = key & hashKey;
        Node aimNode = rep[index];
        if (aimNode==null){
            return;
        }
        if (aimNode.key==key){
            if (aimNode.get==null){
                rep[index]=null;
                size--;
            }else{
                rep[index] = aimNode.get;
                size--;
            }
            return;
        }
        //注意,單向連結串列使用get來對比
        while(aimNode.get!=null&&aimNode.get.key!=key){
            aimNode = aimNode.get;
        }
        //aimNode.get 有兩種情況 一種為null,一種為目標節點
        if (aimNode.get!=null){
            if (aimNode.get.get!=null){
                aimNode.get = aimNode.get.get;
            }else {
                aimNode.get=null;
            }
        }
    }
}
class Node {
    Node pre;
    Node next;
    int key;
    int val;
    Node get;
    int count=0;
    Node(int key,int val){
        this.key =key;
        this.val = val;
    }
    public void move(Node nextNode){
        if(this.next!=null){
            this.next.pre = this.pre;
            this.pre.next = this.next;
        }
        this.pre=nextNode.pre;
        this.next=nextNode;
        this.pre.next = this;
        nextNode.pre = this;
    }
    public void del(){
        this.pre.next = next;
        this.next.pre = pre;
    }
}


一些感想:

根據不同的需求選擇什麼樣的快取演算法,以及創新新的快取演算法是以後學習的一個方向

但目前還是先把眼前的問題解決,在寫較長的邏輯時總會有很多邏輯錯誤,發生錯誤總會加一些條件判斷來避免同類錯誤

但是錯誤多了之後會使程式碼很亂,沒有條理,更可怕的是加條件判斷的時候沒有經過思考,導致條件與邏輯衝突,加的判斷還有新的錯誤等等...以後寫東西一定將邏輯梳理完整再寫,爭取寫出來一次就過..