堆排序算法及优化(java)

news2024/11/15 8:17:21

目录

1.1 引言

1.2 堆排序的历史

1.3 堆排序的基本原理

1.3.1 堆的概念

1.3.2 堆排序的过程

1.3.3 堆调整

1.3.4 堆排序算法流程

1.4 堆排序的Java实现

1.4.1 简单实现

1.4.2 代码解释

1.4.3 优化实现

1.4.4 代码解释

1.5 堆排序的时间复杂度

1.5.1 分析

1.5.2 证明

1.6 堆排序的稳定性

1.7 著名案例

1.7.1 应用场景

1.7.2 具体案例

1.8 堆排序的优化方案

1.8.1 循环展开

1.8.2 减少边界检查

1.8.3 位运算

1.8.4 Java示例代码

1.8.5 代码解释

1.9 总结

1.1 引言

堆排序是一种基于比较的排序算法,它利用堆数据结构的性质来高效地排序元素。堆排序可以被看作是选择排序的一种改进版本,通过维护一个堆结构来保证每次都能选取未排序部分的最大(或最小)元素。本文将详细介绍堆排序的历史背景、工作原理,并通过具体案例来阐述其应用。此外,还将探讨堆排序的不同优化方案,并给出相应的Java代码示例。

1.2 堆排序的历史

堆排序的思想可以追溯到20世纪60年代,最初由J.W.J. Williams在1964年的论文中提出。随着时间的发展,堆排序因其简单高效的特点成为了计算机科学中一个重要的排序算法。

堆排序之所以重要,是因为它提供了比传统选择排序更好的性能,并且可以在 O(nlogn) 的时间内完成排序。此外,堆排序是一种原地排序算法,不需要额外的存储空间,这使得它在内存受限的环境中非常有用。

1.3 堆排序的基本原理

1.3.1 堆的概念

在堆排序中使用的堆是一种特殊的完全二叉树结构,满足以下条件:

  • 完全二叉树:除了最后一层外,每一层的节点都是满的,并且最后一层的所有节点都尽可能靠左排列。
  • 堆属性:父节点的键值总是大于(最大堆)或小于(最小堆)其子节点的键值。

1.3.2 堆排序的过程

堆排序的基本过程包括两个主要步骤:

  1. 建堆:将无序数组构造成一个堆结构。
  2. 排序:反复移除堆顶元素,并调整堆结构,直到堆为空。

1.3.3 堆调整

堆调整是指在插入或删除元素后重新调整堆结构以满足堆的性质的过程。堆调整的核心是向下调整(sift down)或向上调整(sift up)。

1.3.4 堆排序算法流程

  1. 构建最大堆:将无序数组构造成一个最大堆。
  2. 提取最大元素:将堆顶元素(即最大元素)与最后一个元素交换,并缩小堆的大小。
  3. 调整堆:对新的堆顶元素执行向下调整操作,以保持堆的性质。
  4. 重复:重复步骤2和3,直到堆为空。

1.4 堆排序的Java实现

1.4.1 简单实现

下面是一个简单的堆排序Java代码示例,其中包含了详细的注释和说明:

import java.util.Arrays;

/**
 * 堆排序类,用于实现堆排序算法。
 */
public class HeapSort {

    /**
     * 打印数组中的元素。
     *
     * @param array 需要打印的数组
     */
    private static void printArray(int[] array) {
        for (int value : array) {
            System.out.print(value + " ");
        }
        System.out.println();
    }

    /**
     * 堆排序方法。
     *
     * @param array 需要排序的数组
     */
    public static void heapSort(int[] array) {
        // 构建最大堆
        buildMaxHeap(array);
        // 对堆进行排序
        for (int i = array.length - 1; i > 0; i--) {
            // 将堆顶元素(最大元素)与最后一个元素交换
            swap(array, 0, i);
            // 对剩下的元素重新调整堆
            maxHeapify(array, 0, i);
        }
    }

    /**
     * 构建最大堆。
     *
     * @param array 需要构建最大堆的数组
     */
    private static void buildMaxHeap(int[] array) {
        // 从最后一个非叶子节点开始,逐个节点向下调整
        int n = array.length;
        for (int i = n / 2 - 1; i >= 0; i--) {
            maxHeapify(array, i, n);
        }
    }

    /**
     * 下调最大堆的指定节点。
     *
     * @param array   需要调整的数组
     * @param i       需要调整的节点索引
     * @param heapSize 当前堆的有效大小
     */
    private static void maxHeapify(int[] array, int i, int heapSize) {
        int largest = i; // 初始化最大索引为当前节点
        int left = (i << 1) + 1; // 左子节点索引
        int right = (i << 1) + 2; // 右子节点索引

        // 如果左子节点存在且大于当前节点
        if (left < heapSize && array[left] > array[largest]) {
            largest = left;
        }

        // 如果右子节点存在且大于当前最大节点
        if (right < heapSize && array[right] > array[largest]) {
            largest = right;
        }

        // 如果最大索引不是当前节点,则需要交换
        if (largest != i) {
            swap(array, i, largest);
            // 递归调整子树
            maxHeapify(array, largest, heapSize);
        }
    }

    /**
     * 交换数组中的两个元素。
     *
     * @param array 数组
     * @param i     第一个元素索引
     * @param j     第二个元素索引
     */
    private static void swap(int[] array, int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    /**
     * 主方法,用于测试堆排序算法。
     */
    public static void main(String[] args) {
        int[] array = {4, 2, 2, 8, 3, 3, 1};
        System.out.println("原始数组:");
        printArray(array);

        heapSort(array);

        System.out.println("排序后的数组:");
        printArray(array);
    }
}

1.4.2 代码解释

  • 建堆:从最后一个非叶子节点开始,逐个节点向下调整,直到根节点。
  • 排序:每次将最大元素移动到最后,然后重新调整堆。
  • 调整堆:从根节点开始,逐层向下调整,直到满足堆的性质。

1.4.3 优化实现

接下来是一个优化后的堆排序Java代码示例,其中考虑了更多的细节,如边界检查、循环展开等优化措施,并包含了详细的注释和说明:

import java.util.Arrays;

/**
 * 堆排序类,用于实现堆排序算法。
 */
public class HeapSortOptimized {

    /**
     * 打印数组中的元素。
     *
     * @param array 需要打印的数组
     */
    private static void printArray(int[] array) {
        for (int value : array) {
            System.out.print(value + " ");
        }
        System.out.println();
    }

    /**
     * 堆排序方法。
     *
     * @param array 需要排序的数组
     */
    public static void heapSort(int[] array) {
        // 构建最大堆
        buildMaxHeap(array);
        // 对堆进行排序
        for (int i = array.length - 1; i > 0; i--) {
            // 将堆顶元素(最大元素)与最后一个元素交换
            swap(array, 0, i);
            // 对剩下的元素重新调整堆
            maxHeapify(array, 0, i);
        }
    }

    /**
     * 构建最大堆。
     *
     * @param array 需要构建最大堆的数组
     */
    private static void buildMaxHeap(int[] array) {
        // 从最后一个非叶子节点开始,逐个节点向下调整
        int n = array.length;
        for (int i = n / 2 - 1; i >= 0; i--) {
            maxHeapify(array, i, n);
        }
    }

    /**
     * 下调最大堆的指定节点。
     *
     * @param array   需要调整的数组
     * @param i       需要调整的节点索引
     * @param heapSize 当前堆的有效大小
     */
    private static void maxHeapify(int[] array, int i, int heapSize) {
        int largest = i; // 初始化最大索引为当前节点
        int left = (i << 1) + 1; // 左子节点索引
        int right = (i << 1) + 2; // 右子节点索引

        // 如果左子节点存在且大于当前节点
        if (left < heapSize && array[left] > array[largest]) {
            largest = left;
        }

        // 如果右子节点存在且大于当前最大节点
        if (right < heapSize && array[right] > array[largest]) {
            largest = right;
        }

        // 如果最大索引不是当前节点,则需要交换
        if (largest != i) {
            swap(array, i, largest);
            // 递归调整子树
            maxHeapify(array, largest, heapSize);
        }
    }

    /**
     * 交换数组中的两个元素。
     *
     * @param array 数组
     * @param i     第一个元素索引
     * @param j     第二个元素索引
     */
    private static void swap(int[] array, int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    /**
     * 主方法,用于测试堆排序算法。
     */
    public static void main(String[] args) {
        int[] array = {4, 2, 2, 8, 3, 3, 1};
        System.out.println("原始数组:");
        printArray(array);

        heapSort(array);

        System.out.println("排序后的数组:");
        printArray(array);
    }
}

1.4.4 代码解释

  • 建堆:从最后一个非叶子节点开始,逐个节点向下调整,直到根节点。
  • 排序:每次将最大元素移动到最后,然后重新调整堆。
  • 调整堆:从根节点开始,逐层向下调整,直到满足堆的性质。
  • 优化:在这个版本中,我们使用了位运算来计算子节点索引,以提高性能。

1.5 堆排序的时间复杂度

1.5.1 分析

堆排序的时间复杂度主要由构建最大堆和堆调整两部分组成。

  • 构建最大堆:构建最大堆的时间复杂度为O(n)。
  • 堆调整:堆调整的时间复杂度为 O(logn)。

因此,堆排序的整体时间复杂度为 O(nlogn)。

1.5.2 证明

堆排序的时间复杂度可以通过分析最大堆的构建和调整过程得出。构建最大堆的时间复杂度可以通过主定理(Master Theorem)来证明,而堆调整的时间复杂度则基于二叉树的高度,即 O(logn)。

1.6 堆排序的稳定性

堆排序不是稳定的排序算法。这是因为相同元素的相对顺序在排序过程中可能会改变。

1.7 著名案例

1.7.1 应用场景

堆排序在需要快速排序大量数据的情况下非常有用。下面通过一个具体的案例来说明堆排序的应用。

1.7.2 具体案例

案例描述:假设我们有一个包含100000个整数的数组,这些整数的范围在1到100000之间。我们需要快速地对这些整数进行排序。

解决方案:使用堆排序可以有效地解决这个问题。

  1. 构建最大堆:将无序数组构造成一个最大堆。
  2. 排序:反复移除堆顶元素,并调整堆结构,直到堆为空。

具体步骤

  1. 构建最大堆:将无序数组构造成一个最大堆。
  2. 排序:反复移除堆顶元素,并调整堆结构,直到堆为空。

效果:由于堆排序的时间复杂度为 O(nlogn),因此对于大规模数据集来说,它可以快速完成排序任务。

1.8 堆排序的优化方案

1.8.1 循环展开

通过循环展开可以减少循环中的分支指令,从而提高性能。

1.8.2 减少边界检查

在调整堆的过程中,减少不必要的边界检查可以提高性能。

1.8.3 位运算

在计算子节点索引时,使用位运算替代乘法和加法运算可以提高效率。

1.8.4 Java示例代码

下面是一个考虑了更多优化因素的堆排序Java代码示例,其中包含了详细的注释和说明:

import java.util.Arrays;

/**
 * 堆排序类,用于实现堆排序算法。
 */
public class HeapSortAdvanced {

    /**
     * 打印数组中的元素。
     *
     * @param array 需要打印的数组
     */
    private static void printArray(int[] array) {
        for (int value : array) {
            System.out.print(value + " ");
        }
        System.out.println();
    }

    /**
     * 堆排序方法。
     *
     * @param array 需要排序的数组
     */
    public static void heapSort(int[] array) {
        // 构建最大堆
        buildMaxHeap(array);
        // 对堆进行排序
        for (int i = array.length - 1; i > 0; i--) {
            // 将堆顶元素(最大元素)与最后一个元素交换
            swap(array, 0, i);
            // 对剩下的元素重新调整堆
            maxHeapify(array, 0, i);
        }
    }

    /**
     * 构建最大堆。
     *
     * @param array 需要构建最大堆的数组
     */
    private static void buildMaxHeap(int[] array) {
        // 从最后一个非叶子节点开始,逐个节点向下调整
        int n = array.length;
        for (int i = n / 2 - 1; i >= 0; i--) {
            maxHeapify(array, i, n);
        }
    }

    /**
     * 下调最大堆的指定节点。
     *
     * @param array   需要调整的数组
     * @param i       需要调整的节点索引
     * @param heapSize 当前堆的有效大小
     */
    private static void maxHeapify(int[] array, int i, int heapSize) {
        int largest = i; // 初始化最大索引为当前节点
        int left = (i << 1) + 1; // 左子节点索引
        int right = (i << 1) + 2; // 右子节点索引

        // 如果左子节点存在且大于当前节点
        if (left < heapSize && array[left] > array[largest]) {
            largest = left;
        }

        // 如果右子节点存在且大于当前最大节点
        if (right < heapSize && array[right] > array[largest]) {
            largest = right;
        }

        // 如果最大索引不是当前节点,则需要交换
        if (largest != i) {
            swap(array, i, largest);
            // 递归调整子树
            maxHeapify(array, largest, heapSize);
        }
    }

    /**
     * 交换数组中的两个元素。
     *
     * @param array 数组
     * @param i     第一个元素索引
     * @param j     第二个元素索引
     */
    private static void swap(int[] array, int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    /**
     * 主方法,用于测试堆排序算法。
     */
    public static void main(String[] args) {
        int[] array = {4, 2, 2, 8, 3, 3, 1};
        System.out.println("原始数组:");
        printArray(array);

        heapSort(array);

        System.out.println("排序后的数组:");
        printArray(array);
    }
}

1.8.5 代码解释

  • 建堆:从最后一个非叶子节点开始,逐个节点向下调整,直到根节点。
  • 排序:每次将最大元素移动到最后,然后重新调整堆。
  • 调整堆:从根节点开始,逐层向下调整,直到满足堆的性质。
  • 优化:在这个版本中,我们使用了位运算来计算子节点索引,以提高性能。

1.9 总结

堆排序是一种基于比较的排序算法,它利用堆数据结构的性质来高效地排序元素。通过合理选择建堆和调整堆的方法,可以大大提高堆排序的效率。无论是在理论研究还是实际工程中,堆排序都是一个值得深入了解的重要算法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2074633.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flutter ListView滑动

在Flutter中&#xff0c;ScrollController可以精确地控制和管理滚动行为。通过ScrollController&#xff0c;可以监听滚动的位置、速度&#xff0c;甚至可以在用户滚动时触发自定义的动作。此外&#xff0c;ScrollController还提供了对滚动位置的直接控制&#xff0c;可以编程地…

Aiseesoft PDF Converter Ultimate 3.3.62 + crack

Aiseesoft PDF Converter Ultimate 是一个用于将 PDF 文件转换为其他格式的程序。它允许您将 PDF 转换为 Word、Text、Excel、PowerPoint、ePub、HTML、JPG、TIFF、PNG、GIF 等。 以下是该程序主要功能的简要说明: 支持不同的格式。该程序支持超过 25 种不同的格式,用于从 P…

PyTorch深度学习网络(一:MLP)

全连接神经网络&#xff0c;又称多层感知机&#xff08;MLP&#xff09;&#xff0c;是深度学习最基础的神经网络。全连接神经网络主要由输入层、隐藏层和输出层构成。本文实现了一个通用MLP网络&#xff0c;包括以下功能&#xff1a; 根据输入的特征数、类别数、各隐藏层神经…

Canvas 动画: atan2 三角函数与鼠标跟随效果

这个案例展示了如何使用HTML5的Canvas和JavaScript实现一个动态效果&#xff1a;在画布上绘制一个箭头&#xff0c;并让它实时跟随鼠标移动。这个小项目不仅有趣&#xff0c;还能帮助你理解编程和基本数学概念的实际应用。 项目需求 我们的目标是在一个画布上绘制一个箭头&…

java-4 final、单例类、枚举类、抽象类、接口

final 1. 认识final 2. 常量 大项目中经常将常量集中写在Constant文件中 单例类 &#xff08;设计模式&#xff09; 为什么要把构造器私有化&#xff0c;你不是私有化&#xff0c;别人就可以 new 好多个对象&#xff0c;还怎么是单例吖 定义一个类变量、类方法&#xff0c;…

海外媒体宣发:著名媒体【越南通讯社VNanet】发布新闻稿

海外媒体宣发&#xff1a;著名媒体【越南通讯社VNanet】发布新闻稿 近日&#xff0c;越南通讯社VNanet发布了一篇关于全球气候变化的新闻稿&#xff0c;引起了广泛关注。本文将详细介绍新闻稿的主要内容以及其对全球气候变化的影响。 一、新闻稿概述 越南通讯社VNanet作为越…

解决WIndows10下更新蓝牙驱动屡屡失败问题

因为换了个1T自带Win10系统的SSD硬盘&#xff0c;导致蓝牙驱动死活装不上了。驱动精灵&#xff0c;官方驱动都没用。这可前所未闻啊。 想起换下来的硬盘系统里面还有系统在&#xff0c;试试看能不能直接用之前的系统蓝牙驱动&#xff0c;原则上是应该没问题的。所以就将之前的…

混合现实UI优化:利用物理环境的直接交互

随着虚拟现实(VR)和混合现实(MR)技术的发展,用户界面(UI)的设计变得越来越重要,尤其是在需要适应多种物理环境的情况下。本文将介绍一种名为 InteractionAdapt 的用户界面优化方法,它专为VR环境中的工作空间适配而设计,能够有效利用物理环境,为用户提供更加灵活和个…

Kafka的Offset(偏移量)详解

Kafka的Offset详解 1、生产者Offset2、消费者Offset2.1、消费者2.2、生产者2.3、实体类对象2.4、JSON工具类2.5、项目配置文件2.6、测试类2.7、测试2.8、总结 1、生产者Offset 2、消费者Offset 2.1、消费者 package com.power.consumer;import org.apache.kafka.clients.consu…

Android - 自定义view

为什么要自定义view&#xff1f; 在Android开发中有很多业务场景&#xff0c;原生的控件无法满足需求&#xff0c;并且经常也会遇到一个UI在多处重复使用情况&#xff0c;于是可以通过自定义View的方式来实现这些UI效果。 自定义view的分类 自定义属性 Window window是一个…

图数据库查询语言 cypher 与 memgraph

Cyper 作为声明式查询语言, SQL 在计算机行业无人不晓, 无人不知. 而 Cypher 就是 Graph Database 图数据库的 SQL. Cypher 用"圆括号"来表示节点, 用"方括号,连接线及箭头"表示关系 这样一句话 - "Sally likes Graphs. Sally is friends with John. …

完成控制器方法获取参数-@RequestParam

文章目录 1.将方法的request和response参数封装到参数数组1.SunDispatcherServlet.java1.根据方法信息&#xff0c;返回实参列表2.具体调用 2.测试 2.封装Http请求参数到参数数组1.自定义RequestParam注解2.MonsterController.java 增加参数3.SunDispatcherServlet.java1.resol…

软件架构的发展经历了从单体结构、垂直架构、SOA架构到微服务架构的过程剖析

1.单体架构 特点: 1、所有的功能集成在一个项目工程中。 2、所有的功能打一个war包部署到服务器。 3、应用与数据库分开部署。 4、通过部署应用集群和数据库集群来提高系统的性能。 优点: 1、项目架构简单,前期开发成本低,周期短,小型项目的首选。 缺点: 1、全部…

c++实现mysql关系型数据库连接与增删改查操作

最近老师让我实现这个功能&#xff0c;顺便发个东西&#xff0c;我感觉mysql从入门到精通这本书写的蛮好的&#xff0c;其实连接数据库就是调用mysql-c-api库里面的函数mysql_real_connect,下来的增删改查&#xff0c;也无非就是cmd命令台里面的语句&#xff0c;插入&#xff1…

Javaweb学习之Vue实践小界面(四)

目录 前情回顾 本期介绍 效果图 第一步&#xff1a;前期工作 第二步&#xff1a;建立页眉 效果图 第三步&#xff1a;建立导航栏 效果图 第四步&#xff1a;主要内容放置 效果图 第五步&#xff1a;建立页脚 效果图 综合&#xff1a;将文字和背景更改成自己喜欢的…

PEM燃料电池启停控制策略优化的simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 PEM燃料电池启停控制策略优化的simulink建模与仿真。 1.燃料电池提供是燃料转换为电能和热能的装置。 2.功率的输出的改变通过很多因素&#xff0c;如温度&#xff0c;压力…

谷歌、火狐及Edge等浏览器如何使用allWebPlugin中间件响应ActiveX插件事件

allWebPlugin简介 allWebPlugin中间件是一款为用户提供安全、可靠、便捷的浏览器插件服务的中间件产品&#xff0c;致力于将浏览器插件重新应用到所有浏览器。它将现有ActiveX控件直接嵌入浏览器&#xff0c;实现插件加载、界面显示、接口调用、事件回调等。支持Chrome、Firefo…

模型 OGSM(战略规划)

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。目标引领&#xff0c;策略驱动&#xff0c;量化衡量。 1 OGSM模型的应用 1.1 电商企业年度增长战略 某电商企业面临激烈的市场竞争&#xff0c;决定运用OGSM模型来规划其年度战略&#xff0c;以实现…

代码随想录Day 25|回溯篇完结,题目:491.递增子序列、46、全排列、47.全排列Ⅱ

提示&#xff1a;DDU&#xff0c;供自己复习使用。欢迎大家前来讨论~ 文章目录 第七章 回溯算法part05一、题目题目一&#xff1a;491.递增子序列解题思路&#xff1a;回溯三部曲优化 题目二&#xff1a;46.全排列[46. 全排列](https://leetcode.cn/problems/permutations/)解…

日撸Java三百行(day34:图的深度优先遍历)

目录 一、深度优先搜索 二、图的深度优先遍历 三、代码实现 总结 一、深度优先搜索 深度优先搜索&#xff08;Depth First Search&#xff1a;DFS&#xff09;是一种用于遍历树或图的算法&#xff0c;具体来说就是从起始节点开始&#xff0c;沿某一分支路径不断深入&#…