Heap

文章目录

  • 1. 优先队列(自己实现的堆)用于Prim
  • 2. 斐波那契堆
    • 2.1 二项树与二项堆(二项队列)
    • 2.2 斐波那契堆
  • 3. theap(平衡二叉查找树+最小堆)
  • 4. FHQ_treap

1. 优先队列(自己实现的堆)用于Prim

package heap;

import java.util.Scanner;

public class MST {

    static int[] dis;
    static int[] graph_heap; //映射数组 graph-->heap
    private static final int MAX = 999999;

    static class Heap{
        int[] h;
        int size; //size == (heap的元素个数)

        Heap(int size) {
            this.size = size;
            h = new int[size+1]; //你的数组都是从1开始的
        }

        void siftDown(int i){
            int t;
            boolean flag = true; //标记是否需要继续往下移

            while (i * 2 <= size && flag) { //如果有儿子(至少有一个左儿子)
                if (dis[h[i]] > dis[h[2 * i]]) {
                    t = 2 * i;
                }
                else
                    t = i;
                //如果i还有右儿子
                if ((i * 2 + 1) <= size) {
                    if (dis[h[t]] > dis[h[2 * i + 1]]) {
                        t = 2*i+1;
                    }
                }

                if (t != i) {
                    swap(t, i);
                    i = t; //更新i为它的儿子的编号, 因为接下来要继续调整
                }
                else if (t == i )// 说明父亲比孩子小, 满足最小堆, 不需要再继续向下调整
                    flag = false;
            }

        }
        void siftUp(int i){
            boolean flag = true;
            if (i == 1) return; //i==1, 表明是堆顶

            while (i != 1 && flag) {
                //System.out.println(i); //debug
                if (dis[h[i]] < dis[h[i / 2]]) {
                    swap(i, i/2);
                    i = i/2; //更新i为他的父亲编号, 以便下一次进行调整操作
                }
                else //儿子比父亲大, 满足最小堆, 不需要再向上调整了
                    flag = false;
            }
        }

        //交换堆数组俩个元素的值
        void swap(int a, int b){
            int tmp = h[a];
            h[a] = h[b];
            h[b] = tmp;

            //同步更新映射数组graph_heap
            tmp = graph_heap[h[a]];
            graph_heap[h[a]] = graph_heap[h[b]];
            graph_heap[h[b]] = tmp;

        }
        int pop(){
            int ans = h[1]; //ans表示在graph中的编号
            h[1] = h[size]; //把堆中最后一个元素赋值给第一个元素
            graph_heap[h[size]] = 1; //更新映射数组, 把堆中编号最大的node对应的graph, 映射数组
            size--;

            siftDown(1);
            return ans;
        }
        void createHeap(){
            for (int i = size / 2; i >= 1; i--) {
                siftDown(i);
            }
        }

    }

    static class Node{
        int idx;
        int weight;
        Node next;

        Node(int idx, int weight) {
            this.idx = idx;
            this.weight = weight;
        }
    }

    /*
    邻接表存储图
     */
    static class Graph{
         int num_ver;
         int num_edge;
         Node[] links;

        Graph(int num_ver, int num_edge) {
            this.num_ver = num_ver;
            this.num_edge = num_edge;
            links = new Node[num_ver+1]; //你的数组都是从1开始的
            for (int i = 1; i <= num_ver; i++) { //i从1开始, i<=num_ver
                links[i] = new Node(i,0);
            }
        }

        void createGraph(int u, int v, int w) { //u---->v
            Node n = new Node(v,w);
            Node tmp = links[u].next; //链表, 用一个临时变量存数据, 防止丢失
            links[u].next = n;
            n.next = tmp; //头插法

            Node m = new Node(u,w);
            Node tmp_ = links[v].next; //链表, 用一个临时变量存数据, 防止丢失
            links[v].next = m;
            m.next = tmp_; //头插法
        }

        Node getLink(int i) {
            return links[i];
        }
    }

    public static void main(String[] args) {
        int a,b; //a:node个数, b:边数
        Scanner in = new Scanner(System.in);
        a= in.nextInt();
        b= in.nextInt();

        Graph graph = new Graph(a,b);
        int n,m,w;
        while (b != 0) {
            b--;
            n = in.nextInt();
            m = in.nextInt();
            w = in.nextInt();
            graph.createGraph(n,m,w);
        }

        //初始化dis数组, 刚开始生成树只有node(1)
        dis = new int[a+1];
        dis[1] = 0;
        for (int i = 2; i <= a; i++) { //i=2从node(2)开始
            dis[i] = MAX;
        }
        Node tmp_ = graph.links[1].next;
        while (tmp_ != null) {
            dis[tmp_.idx] = tmp_.weight; //刚开始从node(1)开始生成最小生成树
            tmp_ = tmp_.next;
        }

        //建立堆
        graph_heap = new int[a+1];
        Heap heap = new Heap(a);
        for (int i = 1; i <= a; i++) {
            heap.h[i] = i;
            graph_heap[i] = i;
        }
        heap.createHeap();
        heap.pop(); //把node(1)弹出,

        int cnt = 1;
        boolean[] book = new boolean[a+1];
        book[1] = true;
        //for (int i=1; i< book.length; i++) book[i] = false; //boolean数组初始值都是false

        int sum = 0;
        while (cnt < a) { //a:num_node
            int g_idx = heap.pop(); //第一次循环,
            System.out.println("here is: "+g_idx); //Debug
            book[g_idx] = true;
            cnt++;
            sum += dis[g_idx];

            Node tmp = graph.links[g_idx].next;
            while (tmp != null) {
                if (!(book[tmp.idx]) && dis[tmp.idx] > tmp.weight) { //idx: node在graph中的索引或者说编号
                    dis[tmp.idx] = tmp.weight;
                    //dis[tmp.idx] 变小, 向上调整
                    //System.out.println("here is childs:"+tmp.idx); //for testing
                    //System.out.println(graph_heap[tmp.idx]); //for testing
                    //System.out.println("here is dis["+tmp.idx+"]: " + dis[tmp.idx]);
                    heap.siftUp(graph_heap[tmp.idx]); //把graph里面Node的编号, 转化为heap的node的编号
                }
                tmp = tmp.next;
            }
        }

        System.out.println(sum);
    } //close main()
}

input: 无向图,69表示这个图6个node, 9个edge 举个例子: 测试用例2 4 11, 表示编号为2与编号为4的边权重为11
6 9
2 4 11
3 5 13
4 6 3
5 6 4
2 3 6
4 5 7
1 2 1
3 4 9
1 3 2

output:
19

2. 斐波那契堆

2.1 二项树与二项堆(二项队列)

Heap_第1张图片
Heap_第2张图片

2.2 斐波那契堆

参考博客,写的一级棒
图解参考博客

3. theap(平衡二叉查找树+最小堆)

4. FHQ_treap

你可能感兴趣的:(Data,Structure)