算法導論第19章-斐波那契堆的學習筆記(以及LeetCode1383的斐波那契堆的做法)

首先,斐波那契堆有什麼優點?插入更快,給元素賦值更快(o(1));
斐波那契堆是可合併堆,那就滿足下列幾個方法
MAKE-HEAP(): o1
INSERT(H,x): o1
MINIMUN(H): o1
EXTRACT-MIN(H): o(lgn)
UNION(H1,H2): o1
除此之外還有
DECREASE-KEY(H,x,k): o1
DELETE(H,x): o(lgn)
我們依次說:
MAKE-HEAP():這個很簡單,就是建個節點,然後沒了
INSERT(H,x):這個也很簡單,就是插在H的鏈表裏
MINIMUN(H):就是返回H.min
EXTRACT-MIN(H):把min移除,這之後就是把堆重新建好
UNION(H1,H2):合併堆就是把list連起來
DECREASE-KEY(H,x,k):把某個值減小,再判斷是不是小於父節點需要剪除,然後放到主鏈表上
DELETE(H,x):設置DECREASE-KEY(H,x,-1)+EXTRACT-MIN(H)

斐波那契堆的特性,正因爲這些特性保證了複雜度小:
1)重新建堆的時候,鏈表上的節點的度(degree,即子節點個數)不能有相同的,這樣就保證總結點不會很多
2)當一個節點有兩個子節點被剪除後,它也要被剪除,這樣就保證每棵樹都基本平衡,不會出現變成鏈
第四節講的理論,看得頭大。

實戰,用斐波那契堆的MAKE-HEAP、INSERT、EXTRACT-MIN、EXTRACT-MIN實現LeetCode1383題(效果比我想象的好,速度超過90%的用戶,關聯:LeetCode1383. 最大的團隊表現值(PriorityQueue的使用)

long NN = 1000000;

public int maxPerformance(int n, int[] speed, int[] efficiency, int k) {
    if (k == 0) {
        return 0;
    }
    long ans = 0;
    if (k == 1) {
        for (int i = 0; i < n; i++) {
            ans = Math.max(ans, ((long) speed[i]) * efficiency[i]);
        }
        return (int) (ans % 1000000007);
    }
    Long[] effSpee = new Long[n];
    for (int i = 0; i < n; i++) {
        effSpee[i] = efficiency[i] * NN + speed[i];
    }
    Arrays.sort(effSpee, new Comparator<Long>() {
        @Override
        public int compare(Long o1, Long o2) {
            return o2.compareTo(o1);
        }
    });
    int nspeed = (int) (effSpee[0] % NN);
    Node min = new Node(nspeed);
    min.left = min;
    min.right = min;
    //建堆
    H h = new H();
    h.min = min;
    long allNum = nspeed;
    ans = (effSpee[0] / NN) * allNum;
    for (int i = 1; i < n; i++) {
        nspeed = (int) (effSpee[i] % NN);
        long v = (effSpee[i] / NN);
        if (i >= k) {
            if (nspeed <= h.min.val) {
                continue;
            } else {
                allNum -= h.min.val;
                //移除最小
                h.rewmovMin();
            }
        }
        allNum += nspeed;
        //插入
        h.add(new Node(nspeed));
        ans = Math.max(ans, v * allNum);
    }
    return (int) (ans % 1000000007);

}

Node[] nodes = new Node[20];

class H {
    Node min;

    public void rewmovMin() {
        Node node = null;
        //移除min,把min的child加入到鏈表中
        if (min.left == min) {
            node = min.child;
        } else {
            node = min.left;
            node.right = min.right;
            node.right.left = node;
            if (min.degree > 0) {
                node.right.left = min.child.left;
                min.child.left.right = node.right;
                node.right = min.child;
                min.child.left = node;
            }
        }
        //上面的操作,就是把可能是最小的值都放在最上層的鏈表裏
        Node nNext = node.right;
        min = node;
        //循環遍歷鏈表
        while (nNext != node) {
            if (nNext.val < min.val) {
                min = nNext;
            }
            Node thisNode = nNext;
            nNext = thisNode.right;
            build(thisNode);
        }
        build(node);
        //以上操作,保證鏈表沒有相同degree的樹結構
        for (int i = 0; i < nodes.length; i++) {
            //清空
            nodes[i] = null;
        }
    }

    private void build(Node thisNode) {
        //同等degree合併
        while (nodes[thisNode.degree] != null) {
            Node addNode = nodes[thisNode.degree];
            nodes[thisNode.degree] = null;
            if (addNode.val <= thisNode.val) {
                //小的做父節點
                remove(thisNode);
                if (addNode.child == null) {
                    thisNode.left = thisNode;
                    thisNode.right = thisNode;
                    addNode.child = thisNode;
                } else {
                    addRight(addNode.child, thisNode);
                }
                thisNode = addNode;
            } else {
                remove(addNode);
                if (thisNode.child == null) {
                    addNode.left = addNode;
                    addNode.right = addNode;
                    thisNode.child = addNode;
                } else {
                    addRight(thisNode.child, addNode);
                }
            }
            thisNode.degree++;
        }
        nodes[thisNode.degree] = thisNode;
    }

    //把node加到N的左邊
    private void addRight(Node N, Node node) {
        node.right = N.right;
        N.right.left = node;
        node.left = N;
        N.right = node;
    }

    //把node從鏈中刪除
    private void remove(Node node) {
        node.left.right = node.right;
        node.right.left = node.left;
    }

    //添加
    public void add(Node node) {
        addRight(min, node);
        if (node.val < min.val) {
            min = node;
        }
    }
}

class Node {
    int degree = 0;
    int val;
    Node child;
    Node left;
    Node right;

    public Node(int nspeed) {
        val = nspeed;
    }
    //四個操作用不到parent
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章