算法导论第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
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值