【数据结构与算法 | 堆篇】JAVA实现小顶堆

1. 堆的特点

  • 堆的逻辑结构是数组,内存结构是完全二叉树.完全二叉树即只有最后一层才有叶子节点.
  • 堆又分为大顶堆与小顶堆. 大顶堆的特点是 : 父亲节点比孩子节点的都要大. 小顶堆的特点与其相反.
  • Java的优先级队列(PriorityQueue)的底层实现即用到了小顶堆. 所以下文我们就用Java代码来实现小顶堆.
  • 本文没有像实现栈或队列那样使用了泛型,为了方便省去了泛型的步骤.

2. 小顶堆的实现

(1). 主要操作 : 

  • 堆化:传入一个数组,从下到上,最后一个非叶子节点开始,再从上到下不断进行下沉操作.
  • offer()添加元素:从数组尾部添加元素,不断上浮.
  • poll()弹出堆首元素.将堆首元素与最后一个叶子节点互换,size--,再从堆首开始下沉操作即可.
  • sort()堆排序.

(2). 代码实现

//代码实现小顶堆
//逻辑结构是数组
public class MyHeap {
    private int size;
    private int[] heap;

    public MyHeap(int capacity) {
        heap = new int[capacity];
    }
    //堆化
    public void heapify(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            heap[i] = arr[i];
        }
        size = arr.length;
        //第一步 : 找到最后一个非叶子节点
        //第二步 : 从该节点出发, 从后往前, 以此堆化
        //size - 1即最后一个叶子节点, 依据公式, 该则为叶子节点的父亲节点
        for (int parent = (size - 1) / 2; parent >= 0; parent--) {
            int leftChild = parent*2 + 1;
            int rightChild = parent*2 + 2;
            int min = parent;
            //如果存在左孩子, 而且父亲节点比孩子节点要大
            if (leftChild < size && heap[min] > heap[leftChild]) {
                min = leftChild;
            }
            //如果存在右孩子, 而且右孩子的值比左孩子和父亲还要小
            if (rightChild < size && heap[min] > heap[rightChild]) {
                min = rightChild;
            }
            //如果最小值不是父亲, 那么需要下沉
            if (min != parent) {
                down(parent);
            }
        }
    }
    private void swap(int a, int b) {
        int temp;
        temp = heap[a];
        heap[a] = heap[b];
        heap[b] = temp;
    }
    private void down(int parent) {
        int leftChild = parent*2 + 1;
        int rightChild = parent*2 + 2;
        int min = parent;
        if (leftChild < size && heap[min] > heap[leftChild]) {
            min = leftChild;
        }
        if (rightChild < size && heap[min] > heap[rightChild]) {
            min = rightChild;
        }
        if (min != parent) {
            swap(min, parent);
            down(min);
        }
    }
    //向堆中添加元素, 如果该添加的元素比父亲节点(如果有的话)要小,
    //则需要上浮
    public void offer(int a) {
        if (size > heap.length) {
            return;
        }
        heap[size] = a;
        int child = size;
        size++;
        int parent;
        while (child > 0) {
             parent = (child - 1) / 2;
            //如果添加的元素要比父亲要小
            if (heap[parent] > heap[child]) {
                swap(parent, child);
                down(parent);
                child = parent;
            } else {
                //如果添加后父亲节点仍然符合小顶堆, 那么退出循环, 无需再上浮
                break;
            }
        }
    }
    //返回堆顶元素
    public int peek() {
        return heap[0];
    }
    //弹出堆顶元素
    //第一步 : 将最后一个叶子节点与堆顶元素互换, size--
    //第二步 : 从堆顶开始不断下沉
    public int poll() throws Exception {
        if (size == 0) {
            throw new Exception();
        }
        int value = peek();
        swap(0, size - 1);
        size--;
        //不断下沉
        int parent = 0;
        down(parent);
        return value;
    }
    //删除指定索引的节点, 思路与poll类似
    public int poll(int index) throws Exception {
        //检验index索引的合法性
        if (index < 0 || index >= size) {
            throw new Exception();
        }
        int value = heap[index];
        swap(index, size - 1);
        size--;
        int parent = index;
        while (parent >= 0) {
            down(parent);
            //如果不加这个判断, 那么将parent为0的时候, 不断死循环
            if (parent == 0) {
                break;
            }
            parent = (parent - 1) / 2;
        }
        return value;
    }
    //堆排序
    //第一步 : 将最后一个叶子节点与堆顶互换, size--
    //第二步 : 开始下沉
    int l;
    public void sort() {
        l = size;
        while (size > 0) {
            swap(0, size - 1);
            size--;
            down(0);
        }
        size = l;
        int j = size - 1;
        for (int i = 0; i < size/2; i++){
            swap(i, j);
            j--;
        }
    }
    public boolean isEmpty() {
        return size == 0;
    }
}

3. 单元测试

public class MyHeapTest {
    @Test
    public void test1() throws Exception {
        MyHeap heap = new MyHeap(10);
        int[] arr = new int[]{9, 12, 4, 3, 1};
        heap.heapify(arr);
        System.out.println(heap.peek());
        //1
        System.out.println(heap.poll());
        //1
        System.out.println(heap.peek());
        //3
        heap.offer(0);
        System.out.println(heap.peek());
        //0
        System.out.println(heap.poll(0));
    }
    @Test
    public void test2() throws Exception {
        MyHeap heap = new MyHeap(10);
        int[] arr = new int[]{9, 12, 4, 3, 1, 10, 0};
        heap.heapify(arr);
        heap.offer(-1);
        heap.offer(7);
        heap.offer(-10);
        while (!heap.isEmpty()){
            System.out.print(heap.peek() + " ");
            heap.poll();
        }
    }
}
  • 23
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值