数据结构与算法分析(Java语言描述)(12)—— 堆排序与数组建堆

本文详细介绍了两种堆排序算法:一种是通过逐步插入元素构建最大堆再提取最大值完成排序;另一种是利用heapify过程直接构建最大堆,从而提高构建堆的效率。

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

基本的堆排序

HeapSortBasic.java

package com.algorithm.sort;

import com.dataStructure.heap.MaxHeap;

public class HeapSortBasic {
    private HeapSortBasic() {
    }

    // 对整个arr数组使用 HeapSortBasic 排序
    // HeapSortBasic, 将所有的元素依次添加到堆中, 在将所有元素从堆中依次取出来, 即完成了排序
    // 无论是创建堆的过程, 还是从堆中依次取出元素的过程, 时间复杂度均为O(nlogn)
    // 整个堆排序的整体时间复杂度为O(nlogn)
    public static void sort(Integer[] arr) {
        int n = arr.length;
        MaxHeap maxHeap = new MaxHeap(n);

        // 将 arr 中的元素依次取出,插入最大堆中
        for (Integer num: arr){
            maxHeap.insert(num);
        }

        // 取出最大堆中最大的元素,赋值给 arr 中的最后一个元素
        for (int i = n-1; i>=0; i--){
            arr[i] = maxHeap.extractMax();
        }
    }

    public static void main(String [] args){
        int N = 1000000;
        Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);

        System.out.println("排序前的数组为:");
        SortTestHelper.printArray(arr);
        Long start = System.currentTimeMillis();
        HeapSortBasic.sort(arr);
        Long end = System.currentTimeMillis();
        System.out.println("-------------------------------------------");

        System.out.println("排序后的数组为:");
        SortTestHelper.printArray(arr);
        System.out.println("-------------------------------------------");

        System.out.println("排序后的数组是否有序:");
        if (SortTestHelper.isSorted(arr)) {
            System.out.println("数组有序~");
        } else {
            System.out.println("数组无序!");
        }
        System.out.println("-------------------------------------------");

        System.out.println("排序算法的运行时间为" + " : " + (end - start) + "ms");

    }
}

使用 Heapify 方式的堆排序


HeapSortHeapify.java

package com.algorithm.sort;

import com.dataStructure.heap.MaxHeap;

public class HeapSortHeapify {
    private HeapSortHeapify() {
    }

    // 对整个arr数组使用 HeapSortHeapify 排序
    // HeapSortHeapify, 借助我们的heapify过程创建堆
    // 此时, 创建堆的过程时间复杂度为O(n), 将所有元素依次从堆中取出来, 实践复杂度为O(nlogn)
    // 堆排序的总体时间复杂度依然是O(nlogn), 但是比 HeapSortBasic 性能更优, 因为创建堆的性能更优
    public static void sort(Integer[] arr) {
        int n = arr.length;
        MaxHeap maxHeap = new MaxHeap(arr);

        for (int i = n - 1; i>=0;i--){
            arr[i] = maxHeap.extractMax();
        }
    }

    public static void main(String[] args){
        int N = 1000000;
        Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);

        System.out.println("排序前的数组为:");
        SortTestHelper.printArray(arr);
        Long start = System.currentTimeMillis();
        HeapSortHeapify.sort(arr);
        Long end = System.currentTimeMillis();
        System.out.println("-------------------------------------------");

        System.out.println("排序后的数组为:");
        SortTestHelper.printArray(arr);
        System.out.println("-------------------------------------------");

        System.out.println("排序后的数组是否有序:");
        if (SortTestHelper.isSorted(arr)) {
            System.out.println("数组有序~");
        } else {
            System.out.println("数组无序!");
        }
        System.out.println("-------------------------------------------");

        System.out.println("排序算法的运行时间为" + " : " + (end - start) + "ms");
    }
}

MaxHeap.java

package com.dataStructure.heap;

import java.lang.*;

public class MaxHeap {
    protected Integer[] data;
    protected int count;
    protected int capacity;

    // 构造函数,构造一个空的堆,可以容纳 capacity 个元素
    public MaxHeap(int capacity) {
        data = new Integer[capacity + 1];
        count = 0;
        this.capacity = capacity;
    }

    // 构造函数,用 Heapify 的方式使用数组构建最大堆
    // 根据堆的性质, 只要保证部分有序即可, 即根节点大于左右节点的值.
    // 将数组抽象为一个完全二叉树, 所以只要从【最后一个非叶子节点】向前遍历每一个节点即可.
    // 如果当前节点比左右子树节点都大, 则已经是一个最大堆, 否则将当前节点与左右节点较大的一个交换。
    public MaxHeap(Integer[] arr){
        int n = arr.length;
        data = new Integer[n+1];
        capacity = n;

        for (int i =0; i<n;i++){
            data[i+1] = arr[i];
        }
        count = n;

        // 从最后一个非叶子节点向前遍历到根节点,从每一个节点进行 shiftDown 操作
        for (int i = count/2; i >= 1; i--){
            shiftDown(i);
        }

    }

    // 返回堆中元素的个数
    public int size() {
        return count;
    }

    // 返回一个布尔值,表示堆是否为空
    public boolean isEmpty() {
        return count == 0;
    }

    // 向最大堆中插入一个新的元素 item
    public void insert(Integer item) {
        if (count + 1 > capacity) {
            System.out.println("元素数量已经达到堆容量的最大值!");
            return;
        }
        // 先将 item 放在堆的末尾
        data[count + 1] = item;
        count++;
        // 上浮 item
        shiftUp(count);
    }

    // 将堆中 k 位置的元素 上浮 到合适的位置
    private void shiftUp(int k) {
        // 如果 当前节点(k指向的位置) 不是根节点,且 父节点 < 当前节点
        // 进行循环
        while (k > 1 && data[k / 2].compareTo(data[k]) < 0) {
            swap(k, k / 2);
            k /= 2;
        }
    }

    // 将堆中 i 和 j 位置的元素进行交换
    private void swap(int i, int j) {
        Integer temp = data[i];
        data[i] = data[j];
        data[j] = temp;
    }

    // 从堆中取出堆顶元素,即堆中所存储的最大元素
    public Integer extractMax() {
        if (count <= 0 ){
            System.out.println("堆已空!");
            return null;
        }
        Integer ret = data[1];
        swap(1, count);
        count--;
        shiftDown(1);
        return ret;
    }

    // 将堆顶元素 下沉 到合适的位置
    private void shiftDown(int k){
        while (2*k <= count){
            int j = 2*k;

            // 如果 有 右子节点 且 右子节点 > 左子节点
            if (j+1 <= count && data[j+1].compareTo(data[j]) > 0){
                // j 移动到 右子节点 处
                j++;
            }

            // 如果 根节点 > 子节点中较大者
            if (data[k].compareTo(data[j]) >= 0){
                // 跳出循环
                break;
            }

            // 交换 根节点 和 子节点中较大者
            swap(k, j);

            // 移动 k
            k = j;
        }
    }

    public static void main(String[] args) {
        MaxHeap maxHeap = new MaxHeap(100);

        int N = 50; // 堆中元素个数
        int M = 100; // 堆中元素取值范围[0, M)

        for (int i = 0; i < N; i++)
            maxHeap.insert(new Integer((int) (Math.random() * M)));
        System.out.println(maxHeap.size());

//        for (Integer item : maxHeap.data) {
//            System.out.print(item + " ");
//        }

        while (maxHeap.count != 0){
            System.out.print(maxHeap.extractMax() + " ");
        }
    }


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值