斐波那契堆java实现

一 可合并堆

在这里插入图片描述

1.1 斐波那契堆和二项堆时间复杂度比较

在这里插入图片描述

二 斐波那契堆结构

在这里插入图片描述

在这里插入图片描述

Java 定义堆以及结点结构如下

public class FibHeap<T extends Comparable> {

    private FibNode<T> min; // 指向最小的结点
    private int size;   	//结点总数目


    private final class FibNode<T>{

        T val;
        FibNode<T> right; 	//右兄弟结点;
        FibNode<T> left;  	// 左兄弟结点;
        FibNode<T> parent; 	// 父节点
        FibNode<T> child;   // 指向第一个孩子结点;
        int degree; 		//具有孩子结点的个数;
        boolean marked; 	//该标记表示该结点是否失去过一个孩子结点;

        public FibNode(T val) {
            this.val = val;
        }
    }
}

三、可合并堆操作

3.1 插入结点

伪代码如下:

在这里插入图片描述

在这里插入图片描述

Java 实现:

public void insert(T t){
        FibNode<T> node = new FibNode<T>(t); //创建结点;
        addNode(node);
}
public void addNode(FibNode<T> node){
        if(min == null) {
            min = node;
            min.left = min;
            min.right = min;
        }
        else{
            node.right = min.right;
            min.right.left = node;
            min.right = node;
            node.left = min;
        }
        if(node.val.compareTo(min.val) < 0)
            min = node;
        ++size;
}

3.2 寻找最小结点

直接通过 min指针指向 即可获得:

public T getMin(){
    if(min == null) return null;
    return min.val;
}
3.3 合并操作

伪代码如下:

在这里插入图片描述

Java实现:

/**
 * @param fibHeap 要合并的另一个堆, 加入到当前堆中
 */
public void union(FibHeap<T> fibHeap){
    if(fibHeap == null || fibHeap.min == null) return ;
    if(min == null){
        min = fibHeap.min;
        return;
    }
    FibNode<T> tmp = fibHeap.min.right;
    min.right.left = fibHeap.min;
    fibHeap.min.right = min.right;
    min.right = tmp;
    tmp.left  = min;
    if(min.val.compareTo(fibHeap.min.val) > 0) min = fibHeap.min;
    size += fibHeap.size;
}
3.3 抽取最小结点

抽取结点过程较为复杂总体思路是先将最小结点删除并把孩子结点接入到链表中, 在合并具有相同度的结点。

详情请见《算法导论》

public T extractMin(){

    if(min == null) return null;
    //将每个孩子结点挂在根节点链表上;
    FibNode child = min.child;
    if(child!=null){
        // 兄弟链表也是循环双链表, 直接拼接, 并将各个子节点的parent置为空;
        min.right.left = child.left;
        child.left.right = min.right;
        min.right = child;
        child.left = min;
        FibNode p = min.right;
        while(p.parent!=null){
            p.parent = null;
            p = p.right;
        }
    }

    if(min == min.right){//根链表只有一个min结点, 此时堆没有元素;
        min = null;
    }
    else{
        //删除min结点
        FibNode tmp = min.right;
        min.left.right = min.right;
        min.right.left = min.left;
        min.left= null;
        min.right = null;

        min = tmp;
        consolidate();
    }
    --size;
    return min.val;
}

private void consolidate() {
    // 结点的最大度 maxDegree <= LOGx(size)   x 为 1.618...
    int maxDegree = (int)Math.floor(Math.log(size)/Math.log(X));
    FibNode[] map = new FibNode[maxDegree+1];
    FibNode<T> p = min;
    do {
        while(map[p.degree]!=null){//此时存在与该结点相同degree的结点,合并结点
            if(((T)map[p.degree].val).compareTo(p.val) < 0){
                FibNode tmp = map[p.degree];
                tmp.right = p.right;
                map[p.degree] = p;
                p = tmp;

            }
            FibNode q = map[p.degree];
            map[p.degree] = null;
            link(p,q);
        }
        map[p.degree] = p;
        p = p.right;
    }while(p!=min);

    min = null;
    //将所有map中的结点连接起来;
    for(int i = 0; i <= maxDegree; ++i){
        if(map[i] == null) continue;
        if(min == null){
            min = map[i];
            min.left = min;
            min.right = min;
        }
        else{
            min.right.left = map[i];
            map[i].right = min.right;
            min.right = map[i];
            map[i].left = min;
        }
        if(min.val.compareTo(map[i].val) > 0) 
            min = map[i];
        map[i] = null;
    }
}

/**
 *
 * @param p
 * @param q
 * 将 q 结点连接成 p 的孩子, 需要将q的marked标记清除;
 */
private void link(FibNode<T> p, FibNode q) {
    if(p.child==null){
        q.left = q;
        q.right = q;
    }else{
        p.child.left.right = q;
        q.left = p.child.left;
        p.child.left = q;
        q.right = p;
    }
    q.marked = false;
    p.child = q;
    ++p.degree;
}
3.4 关键字值减少
public void decreaseKey(FibNode<T> node, T t){
    if(node == null || t.compareTo(node.val) >= 0) return ;
    node.val = t;
    FibNode<T> parent = node.parent;
    if(parent == null || node.val .compareTo(parent.val) >= 0){
        // 如果父节点为空或者说没有违反最小堆序;
        return ;
    }
    cut(node); //剪断该结点与父节点联系;
    cascadingCut(parent);
}

private void cascadingCut(FibNode<T> node) {
    FibNode<T> parent = node.parent;
    if(parent == null){
        return ;
    }
    if(parent.marked == false){ //如果父亲结点没有失去过孩子
        parent.marked = true;
        return ;
    }
    //失去过一个孩子,要将该结点剪断;
    cut(node);
    cascadingCut(parent);
}

/**
 *
 * @param node 待剪断的结点
 *  将结点从其父节点链表中移除;
 */
private void cut(FibNode<T> node) {
    FibNode<T> parent = node.parent;
    if(node == node.parent.child){
        //如果是父节点child指针所指
        parent.child = parent.child.right;
        parent.child.left = node.left;
        node.left.right = parent.child;
    }else{
        node.right.left = node.left;
        node.left.right = node.right;
    }
    if(parent.child == node) //如果父亲链表中一开始只有一个元素;
        parent.child = null;
    //将该节点加入到根链表中;
    node.parent = null;
    addNode(node);
    --parent.degree;
}

全部代码:

public class FibHeap<T extends Comparable> {

    private FibNode<T> min; // 指向最小的结点
    private int size;   //结点总数目
    public final double X = (Math.sqrt(5) + 1) / 2;



    private final class FibNode<T>{
        T val;
        FibNode<T> right;   //右兄弟结点;
        FibNode<T> left;    // 左兄弟结点;
        FibNode<T> parent;  // 父节点
        FibNode<T> child;   // 指向第一个孩子结点;
        int degree;         //具有孩子结点的个数;
        boolean marked;     //该标记表示该结点是否失去过一个孩子结点;

        public FibNode(T val) {
            this.val = val;
        }
    }

    public void insert(T t){
        FibNode<T> node = new FibNode<T>(t); //创建结点;
        addNode(node);
    }

    public T getMin(){
        return min.val;
    }

    /**
     * @param fibHeap 要合并的另一个堆, 加入到当前堆中
     */
    public void union(FibHeap<T> fibHeap){
        if(fibHeap == null || fibHeap.min == null) return ;
        if(min == null){
            min = fibHeap.min;
            return;
        }

        //连接两个循环链表;
        fibHeap.min.right.right = min.right;
        min.right.left = fibHeap.min.right;
        min.right = fibHeap.min;
        fibHeap.min.left = min;

        if(min.val.compareTo(fibHeap.min.val) > 0) min = fibHeap.min;
        size += fibHeap.size;
    }

    public T extractMin(){

        if(min == null) return null;
        //将每个孩子结点挂在根节点链表上;
        FibNode child = min.child;
        if(child!=null){
            // 兄弟链表也是循环双链表, 直接拼接, 并将各个子节点的parent置为空;
            min.right.left = child.left;
            child.left.right = min.right;
            min.right = child;
            child.left = min;
            FibNode p = min.right;
            while(p.parent!=null){
                p.parent = null;
                p = p.right;
            }
        }

        if(min == min.right){//根链表只有一个min结点, 此时堆没有元素;
            min = null;
        }
        else{
            //删除min结点
            FibNode tmp = min.right;
            min.left.right = min.right;
            min.right.left = min.left;
            min.left= null;
            min.right = null;

            min = tmp;
            consolidate();
        }
        --size;
        return min.val;
    }

    private void consolidate() {
        // 结点的最大度 maxDegree <= LOGx(size)   x 为 1.618...
        int maxDegree = (int)Math.floor(Math.log(size)/Math.log(X));
        FibNode[] map = new FibNode[maxDegree+1];
        FibNode<T> p = min;
        do {
            while(map[p.degree]!=null){//此时存在与该结点相同degree的结点,合并结点
                if(((T)map[p.degree].val).compareTo(p.val) < 0){
                    FibNode tmp = map[p.degree];
                    tmp.right = p.right;
                    map[p.degree] = p;
                    p = tmp;

                }
                FibNode q = map[p.degree];
                map[p.degree] = null;
                link(p,q);
            }
            map[p.degree] = p;
            p = p.right;
        }while(p!=min);

        min = null;
        //将所有map中的结点连接起来;
        for(int i = 0; i <= maxDegree; ++i){
            if(map[i] == null) continue;
            if(min == null){
                min = map[i];
                min.left = min;
                min.right = min;
            }
            else{
                min.right.left = map[i];
                map[i].right = min.right;
                min.right = map[i];
                map[i].left = min;
            }
            if(min.val.compareTo(map[i].val) > 0)
                min = map[i];
            map[i] = null;
        }
    }

    /**
     *
     * @param p
     * @param q
     * 将 q 结点连接成 p 的孩子, 需要将q的marked标记清除;
     */
    private void link(FibNode<T> p, FibNode q) {
        if(p.child==null){
            q.left = q;
            q.right = q;
        }else{
            p.child.left.right = q;
            q.left = p.child.left;
            p.child.left = q;
            q.right = p;
        }
        q.marked = false;
        p.child = q;
        ++p.degree;
    }

    public void decreaseKey(FibNode<T> node, T t){
        if(node == null || t.compareTo(node.val) >= 0) return ;
        node.val = t;
        FibNode<T> parent = node.parent;
        if(parent == null || node.val .compareTo(parent.val) >= 0){
            // 如果父节点为空或者说没有违反最小堆序;
            return ;
        }
        cut(node); //剪断该结点与父节点联系;
        cascadingCut(parent);
    }

    private void cascadingCut(FibNode<T> node) {
        FibNode<T> parent = node.parent;
        if(parent == null){
            return ;
        }
        if(parent.marked == false){ //如果父亲结点没有失去过孩子
            parent.marked = true;
            return ;
        }
        //失去过一个孩子,要将该结点剪断;
        cut(node);
        cascadingCut(parent);
    }

    /**
     *
     * @param node 待剪断的结点
     *  将结点从其父节点链表中移除;
     */
    private void cut(FibNode<T> node) {
        FibNode<T> parent = node.parent;
        if(node == node.parent.child){
            //如果是父节点child指针所指
            parent.child = parent.child.right;
            parent.child.left = node.left;
            node.left.right = parent.child;
        }else{
            node.right.left = node.left;
            node.left.right = node.right;
        }
        if(parent.child == node) //如果父亲链表中一开始只有一个元素;
            parent.child = null;
        //将该节点加入到根链表中;
        node.parent = null;
        addNode(node);
        --parent.degree;
    }

    public void addNode(FibNode<T> node){
        if(min == null) {
            min = node;
            min.left = min;
            min.right = min;
        }
        else{
            node.right = min.right;
            min.right.left = node;
            min.right = node;
            node.left = min;
        }
        if(node.val.compareTo(min.val) < 0)
            min = node;
        ++size;
    }
}

        addNode(node);
        --parent.degree;
    }

    public void addNode(FibNode<T> node){
        if(min == null) {
            min = node;
            min.left = min;
            min.right = min;
        }
        else{
            node.right = min.right;
            min.right.left = node;
            min.right = node;
            node.left = min;
        }
        if(node.val.compareTo(min.val) < 0)
            min = node;
        ++size;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值