最大堆(MaxHeap)

性质

  • 二叉堆是一颗完全二叉树,而完全二叉树是把元素排列成树的形状。
  • 堆中某个节点的值总不大于其父节点的值最大堆(相应的可以定于最小堆)

在这里插入图片描述

// 返回完全二叉树的数组表示中,一个索引所表示的元素的父亲节点的索引
constexpr int parent(const int index) const {
    if (index == 0) {
        throw new NoParent();
    }
    return (index - 1) / 2;
}
// 返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引
constexpr int leftChild(const int index) const {
    return (index * 2) + 1;
}
// 返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引
constexpr int rightChild(const int index) const {
    return (index * 2) + 2;
}

可以先阅读底层动态数组Array

添加

在这里插入图片描述
首先我们堆中的数据使用数组排列的,所以添加一个元素就是在层序遍历的最右端,也就是最下面一层的最后添加一个元素。但是以数组来看就是在索引为10的地方添加一个元素。

void add(const T &e) {
	data->addLast(e);	//在数组的末尾添加元素
	shiftUp(data->getSize() - 1);	//上浮添加元素的索引
}
  • 时间复杂度O(logn)

在这里插入图片描述

但是添加的元素不符最大堆的性质,索引我需要一些调整,而这个调整就是一个上浮的过程。

void shiftUp(int index) {
	//如果传入索引小于等于0并且父元素大于等于子元素则停止循环
   while (index > 0 && data->get(index) > data->get(parent(index))) {
        data->swap(index, parent(index));	//位置交换
        index = parent(index);	//把父节点的索引给子节的
    }
}

取出最大元素

在这里插入图片描述
最大堆的最大元素就是其根节点元素,取出的操作只能取出这个元素,对于数组来说,根结点就是索引为0的元素。
在这里插入图片描述
我们把堆中最后一个元素顶到堆顶去,然后再把最后一个元素删除。然而这样就又不符合最大堆的性质。

在这里插入图片描述
这样的话,其不大于它的子节点,此时又要进行调整,这个调整的过程叫做下沉。在这个过程中每次需要下沉的时候都要和它的两个孩子进行比较,选择其中较大的进行交换位置。
在这里插入图片描述

  • 时间复杂度O(logn)
//返回最大的元素
 T findMax() const {
    if (data->isEmpty()) {
        throw Empty();
    }
    return data->get(0);
}
//取出最大的元素
T extractMax() {
    T ret = findMax();
    data->swap(0, data->getSize() - 1);
    data->removeLast();
    shiftDown(0);
    return ret;
}
//下沉
void shiftDown(int k) {
    while (leftChild(k) < data->getSize()) {
        int j = leftChild(k);	
        //j保存的是左右孩子中较大的元素索引
        if (j + 1 < data->getSize() && data->get(j + 1) > data->get(j)) {
            j = rightChild(k);
        }
        //如果子节点小于等于父节点了,就结束
        if (data->get(k) > data->get(j)) {
            break;
        }
        data->swap(k, j);
        k = j;
    }
}

取出堆中最大的元素,并替换成元素e

  • 时间复杂度O(logn)
T replace(T e) {
    T ret = findMax();
    data->set(0, e);
    shiftDown(0);
    return ret;
}

Heapify

将n个元素逐个插入到一个空堆中,算法复杂度是O(nlogn),Heapify的过程,算法复杂度是O(n)。

MaxHeap(T arr[], const int n) {
	data = new Array<T>(arr, n);
	for (int i = parent(n - 1); i >= 0; --i) {
	    shiftDown(i);
	}
}

对比使用与不适用Heapify代码

#include <iostream>
#include "MaxHeap.h"
#include <cassert>

template<typename T>
double testHeap(T testData[], int n, bool isHeapify) {
    clock_t startTime = clock();
    MaxHeap<T> *maxHeap;
    if (isHeapify) {
        maxHeap = new MaxHeap<T>(testData, n);
    } else {
        maxHeap = new MaxHeap<T>();
        for (int i = 0; i < n; ++i) {
            maxHeap->add(testData[i]);
        }
    }

    T *arr = new T[n];
    for (int j = 0; j < n; ++j) {
        arr[j] = maxHeap->extractMax();
    }

    for (int k = 1; k < n; ++k) {
        assert(arr[k - 1] >= arr[k]);
    }
    std::cout << "Test MaxHeap completed." << std::endl;
    clock_t endTime = clock();
    return double(endTime - startTime) / CLOCKS_PER_SEC;
}

int main() {
    int n = 5000000;
    int *testData = new int[n];
    for (int i = 0; i < n; ++i) {
        testData[i] = rand() % INT32_MAX;
    }
    double time1 = testHeap(testData, n, false);
    std::cout << "Without heapify :" << time1 << " s " << std::endl;
    double time2 = testHeap(testData, n, true);
    std::cout << "With heapify :" << time2 << " s " << std::endl;
    return 0;
}

代码清单

//
// Created by cheng on 2021/7/10.
//

#ifndef MAXHEAP_MAXHEAP_H
#define MAXHEAP_MAXHEAP_H

#include "Array.h"

template<typename T>
class MaxHeap {
public:

    class NoParent {
    };

    class Empty {
    };

    MaxHeap() {
        data = new Array<T>();
    }

    ~MaxHeap() {
        delete data;
        data = nullptr;
    }

    MaxHeap(const int capacity) {
        data = new Array<T>(capacity);
    }

    MaxHeap(T arr[], const int n) {
        data = new Array<T>(arr, n);
        for (int i = parent(n - 1); i >= 0; --i) {
            shiftDown(i);
        }
    }

    constexpr int getSize() const {
        return data->getSize();
    }

    constexpr bool isEmpty() const {
        return data->isEmpty();
    }
    
    // 返回完全二叉树的数组表示中,一个索引所表示的元素的父亲节点的索引
    constexpr int parent(const int index) const {
        if (index == 0) {
            throw new NoParent();
        }
        return (index - 1) / 2;
    }

    void add(const T &e) {
        data->addLast(e);
        shiftUp(data->getSize() - 1);
    }
	//返回最大元素
    T findMax() const {
        if (data->isEmpty()) {
            throw Empty();
        }
        return data->get(0);
    }
	//取出最大的元素
    T extractMax() {
        T ret = findMax();
        data->swap(0, data->getSize() - 1);
        data->removeLast();
        shiftDown(0);
        return ret;
    }
    //取出堆中最大的元素,并替换成元素e
	T replace(T e) {
        T ret = findMax();
        data->set(0, e);
        shiftDown(0);
        return ret;
	}

    void print() {
        data->print();
    }

private:

    void shiftDown(int k) {
        while (leftChild(k) < data->getSize()) {
            int j = leftChild(k);
            if (j + 1 < data->getSize() && data->get(j + 1) > data->get(j)) {
                j = rightChild(k);
            }
            if (data->get(k) > data->get(j)) {
                break;
            }
            data->swap(k, j);
            k = j;
        }
    }

    void shiftUp(int index) {
        while (index > 0 && data->get(index) > data->get(parent(index))) {
            data->swap(index, parent(index));
            index = parent(index);
        }
    }
 	
    // 返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引
    constexpr int leftChild(const int index) const {
        return (index * 2) + 1;
    }

    // 返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引
    constexpr int rightChild(const int index) const {
        return (index * 2) + 2;
    }

private:
    Array<T> *data;
};

#endif //MAXHEAP_MAXHEAP_H

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
PriorityQueue 是一种数据结构,它可以用来实现最大堆最大堆是一种特殊的二叉树,其中每个节点的值都大于或等于其子节点的值。 在 Java 中,PriorityQueue 类默认实现的是最小堆,但我们可以通过指定自定义的比较器来实现最大堆。比较器可以按照我们的需求来定义元素之间的比较规则。 以下是一个使用 PriorityQueue 实现最大堆的示例代码: ``` import java.util.Comparator; import java.util.PriorityQueue; public class MaxHeapExample { public static void main(String[] args) { // 创建一个自定义比较器,用于实现最大堆 Comparator<Integer> maxHeapComparator = Comparator.reverseOrder(); // 创建一个最大堆 PriorityQueue PriorityQueue<Integer> maxHeap = new PriorityQueue<>(maxHeapComparator); // 向最大堆中添加元素 maxHeap.add(5); maxHeap.add(3); maxHeap.add(8); maxHeap.add(1); maxHeap.add(10); // 输出最大堆中的元素 while (!maxHeap.isEmpty()) { System.out.println(maxHeap.poll()); } } } ``` 在上面的示例中,我们首先创建了一个自定义的比较器 `maxHeapComparator`,它将元素按照降序排列。然后,我们使用这个比较器来创建一个 PriorityQueue 对象 `maxHeap`,从而实现了最大堆。 接下来,我们向最大堆中添加了一些元素,并使用 `poll()` 方法逐个取出并输出最大堆中的元素。由于最大堆的特性,输出结果会按照降序排列。 希望这个示例能够帮助你理解如何使用 PriorityQueue 实现最大堆。如果你有任何疑问,请随时提问!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吃米饭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值