数据结构-优先队列和堆篇

本文围绕数据结构中的优先队列和堆展开。介绍了优先队列,其出队与优先级相关。还阐述了二叉堆,包括最大堆定义、堆的操作,如添加和取出元素,以及Replace、heapify操作,并对比了不同操作的时间复杂度,最后提及基于堆的优先队列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据结构-优先队列和堆篇

一. 优先队列
  • 普通队列:先进先出,后进后出

  • 优先队列:出队顺序和入队顺序无关;和优先级相关

动态选择优先级最高的任务执行

定义队列:

Interface Queue <-------------PriorityQueue implement Queue

  • void enqueue(E)
  • E dequeueu()
  • E getFront()
  • int getSize()
  • boolean isEmpty()
数据结构入队出队
普通线性结构O(1)O(n)
顺序线性结构O(logn)O(1)
O(n)O(logn)
二. 堆

二叉堆Binary Heap

  • 二叉堆是一棵完全二叉树:把元素顺序排列成树的形状

  • 堆中某个节点的值总是不大于其父节点的值→最大堆(相应的可以定义最小堆)

1. 最大堆定义
public class MaxHeap<E extends Comparable<E>> {

    private Array1<E> data;

    public MaxHeap(int capacity) {
        data = new Array1<E>(capacity);
    }

    public MaxHeap() {
        data = new Array1<E>();
    }

    //返回堆中的元素个数
    public int size(){
        return data.getSize();
    }
    //返回一个布尔值,表示堆中是否为空
    public boolean isEmpty(){
        return data.isEmpty();
    }
//返回完全二叉树的数组表示中,一个索引所表示的元素的父亲节点的索引
    private int parent(int index){
        if(index==0){
            throw new IllegalArgumentException("index-0 doesn't have parent.");
        }
        return (index-1)/2;
    }
//返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引
    private int leftChild(int index){
        return 2 * index +1;
    }
    //返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引
    private int rightChild(int index){
        return 2 * index +2;
    }
}
2. 堆的操作
  • 在堆中添加元素
//向堆中添加元素
public void add(E e) {
    data.addList(e);
    siftUp(data.getSize() - 1);
}
private void siftUp(int k) {
    while (k > 0 && data.get(parent(k)).compareTo(data.get(k)) < 0) {
        data.swap(k, parent(k));
        k = parent(k);
    }
}
  • 在第一章数组 Array1中添加函数
/**
 * 交换元素:在数组最后添加一个元素,然后跟父亲节点比较,大就交换
 */
public void swap(int i, int j) {
    if (i < 0 || i >= size || j < 0 || j >= size) {
        throw new IllegalArgumentException("Index is illegal");
    }
    E t = data[i];
    data[j] = data[i];
    data[j] = t;
}
  • 在堆中取出元素
  1. 最后一个元素跟这个元素换,最后一个元素删除
  2. 每次下沉跟他的两个孩子比较,选择大的跟这个元素调换
  3. add和extracMax时间复杂度都是O(logn)
//看堆中的最大元素
public E findMax() {
    if (data.getSize() == 0) {
        throw new IllegalArgumentException("Can not findMax when heap is empty");
    }
    return data.get(0);
}

//从堆中取出最大元素
public E extractMax() {
    E ret = findMax();
    data.swap(0, data.getSize() - 1);
    data.removeLast();
    siftDown(0);
    return ret;
}
private void siftDown(int k) {
    while (leftChild(k) < data.getSize()) {
        //左孩子节点值
        int j = leftChild(k);
        //如果右孩子不是叶子结点并且右孩子大于左孩子
        if (j + 1 < data.getSize() &&
                data.get(j + 1).compareTo(data.get(j)) > 0) {
            j = rightChild(k);
        }
        //data[j]是leftChild和rightChild中的最大值
        if (data.get(k).compareTo(data.get(j)) >= 0) {
            break;
        }
        data.swap(k, j);
        k = j;
    }
}
  • 测试类
public static void main(String[] args) {
    int n = 1000000;

    MaxHeap<Integer> maxHeap = new MaxHeap<Integer>();
    Random random = new Random();
    for (int i = 0; i < n; i++) {
        maxHeap.add(random.nextInt(Integer.MAX_VALUE));
    }
    int[] arr = new int[n];
    for(int i=0;i<n;i++){
        arr[i] = maxHeap.extractMax();
    }
    for(int i=1;i<n;i++){
        if(arr[i-1]<arr[i])
            throw new IllegalArgumentException("Error");
    }
    System.out.println("Test MaxHeap completed");
}
3. Replace:取出最大元素后,放入一个新元素
  • 实现:可以先extracMax,再add,两次O(logn)的操作
  • 实现:可以直接将堆顶元素替换以后Sift Down,一次O(logn)操作
//取出堆中的最大元素,并且替换成元素e
public E replace(E e){
    E ret = findMax();
    data.set(0,e);
    siftDown(0);
    return ret;
}
4. heapify: 将任意数组整理成堆的形状

找到最后一个非叶子结点,向前遍历,对每个节点进行下浮操作

最后一个非叶子节点:parent(arr.length-1)


Heapify的算法复杂度:

  • 将n个元素诸逐个插入到一个空堆中,算法复杂度是O(nlogn)

  • heapify的过程,算法复杂度为O(n)

Heapify实现

  1. 在Array中添加传入数组的构造函数
/**
 * 传入数组
 */
public Array1(E[] arr){
    data = (E[])new Object[arr.length];
    for(int i=0;i<arr.length;i++){
        data[i] = arr[i];
    }
    size = arr.length;
}
  1. 找到最后一个非叶子结点,向前遍历,对每个节点进行下沉操作
public MaxHeap(E[] arr){
    data = new Array1<E>(arr);
    for(int i=parent(arr.length-1);i>=0;i--){
        siftDown(i);
    }
}
  1. 复杂度测试
public class MaxHeapMain {
    private static double testMaxHeap(Integer[] testData, boolean isHeapify) {
        long startTime = System.nanoTime();

        MaxHeap<Integer> maxHeap;
        if (isHeapify) {
            maxHeap = new MaxHeap<Integer>(testData);
        } else {
            maxHeap = new MaxHeap<Integer>();
            for (int num : testData) {
                maxHeap.add(num);
            }
        }
        int[] arr = new int[testData.length];
        for (int i = 0; i < testData.length; i++) {
            arr[i] = maxHeap.extractMax();
        }
        for (int i = 1; i < testData.length; i++) {
            if (arr[i - 1] < arr[i]) {
                throw new IllegalArgumentException("Error");
            }
        }
        long endTime = System.nanoTime();

        System.out.println("Test MaxHeap completed");

        return (endTime - startTime) / 1000000000.0;
    }

    public static void main(String[] args) {
        int n = 1000000;

        Random random = new Random();
        Integer[] testData = new Integer[n];
        for (int i = 0; i < n; i++) {
            testData[i] = random.nextInt(Integer.MAX_VALUE);
        }
        double time1 = testMaxHeap(testData, false);
        System.out.println("Without heapify:" + time1 + "s");
        double time2 = testMaxHeap(testData, true);
        System.out.println("With heapify:" + time2 + "s");
    }
}

结果:

Test MaxHeap completed

Without heapify:0.249022s

Test MaxHeap completed

With heapify:0.1057637s

5. 基于堆的优先队列
public interface Queue<E> {
    int getSize();
    boolean isEmpty();
    void enqueue(E e); //入队
    E dequeue();    //出队
    E getFront();   //获取队首元素
}
//基于堆的优先队列
public class PriorityQueue<E extends Comparable<E>> implements Queue<E> {

    private MaxHeap<E> maxHeap;
    public PriorityQueue(){
        maxHeap = new MaxHeap<E>();
    }

    @Override
    public int getSize(){
        return maxHeap.size();
    }
    @Override
    public boolean isEmpty(){
        return maxHeap.isEmpty();
    }
    @Override
    public E getFront(){
        return maxHeap.findMax();
    }
    @Override
    public void enqueue(E e){
        maxHeap.add(e);
    }
    @Override
    public E dequeue(){
        return maxHeap.extractMax();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值