首页 > 编程语言 >堆排序算法及优化(java)

堆排序算法及优化(java)

时间:2024-08-26 10:51:44浏览次数:13  
标签:java int 堆排序 param 算法 largest array 节点

目录

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 总结

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

标签:java,int,堆排序,param,算法,largest,array,节点
From: https://blog.csdn.net/weixin_43841461/article/details/141436355

相关文章

  • 【java计算机毕设】车联网位置信息管理系统MySQL springcloud vue maven项目设计源码
    目录1项目功能2项目介绍3项目地址 1项目功能【java计算机毕设】车联网位置信息管理系统MySQLspringcloudvuemaven项目设计源码前后端可分离也可不分离 2项目介绍系统功能:车联网位置信息管理系统包括管理员、用户两种角色。管理员功能包括个人中心模块用......
  • B站宋红康JAVA基础视频教程个人笔记chapter08-09(异常处理+多线程)
    文章目录1.异常处理方式1:try-catch-finally2.异常处理方式1:throws3.程序,进程,线程的区别4.线程的创建4.1线程的创建方式1:4.2线程的创建方式2:5.线程类的常用方法和生命周期5.1线程的生命周期jdk5之前6.线程的安全问题和同步机制6.线程之间的通信6.1为什么需要线程之间......
  • 算法与数据结构——内存与缓存
    内存与缓存数组和链表两种数据结构分别代表了“连续存储”和“分散存储”两种物理结构。实际上,物理结构在很大程度上决定了程序对内存和缓存的使用效率,进而影响算法程序的整体性能。计算机存储设备计算机中包括三种类型的存储设备:硬盘(harddisk)、内存(random-accessmemory,RAM)、......
  • java中哪些集合类是安全的,哪些是不安全的?
    在Java中,有一些集合类是线程安全的,主要包括以下几种:Vector:Vector是ArrayList的线程安全版本,它使用同步方法来确保线程安全。当对Vector进行操作时,需要使用synchronized关键字来同步访问。Hashtable:Hashtable是HashMap的线程安全版本,它使用同步方法来确保线程安......
  • 【精选】数码论坛系统设计与实现(计算机毕业设计福利,计算机毕业设计参考,JAVA毕业设计)
    博主介绍:  ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W+粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台的优质作者。通过长期分享和实战指导,我致力于帮助更多学生......
  • 第四章:行为型模式(11种)《Java设计模式宝典:从入门到精通》
      在进入第四章之前,我为大家准备了设计模式专栏的快速访问链接,方便大家随时查阅:《Java设计模式宝典:从入门到精通》专栏目录地址,点击我即可快速访问到所有设计模式的章节,不漏掉一处学习的知识 [点赞]等会儿兄弟们,这篇文章的干货真的是满满的!......
  • Java | Leetcode Java题解之第374题猜数字大小
    题目:题解:publicclassSolutionextendsGuessGame{publicintguessNumber(intn){intleft=1,right=n;while(left<right){//循环直至区间左右端点相同intmid=left+(right-left)/2;//防止计算时溢出......
  • 电商导购平台的推荐算法与大数据应用
    电商导购平台的推荐算法与大数据应用大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!电商导购平台的核心竞争力之一就是为用户提供个性化的购物体验,而推荐算法和大数据技术的应用是实现这一目标的关键。本文将探讨电商导购平台中推荐算法的设计......
  • 前端宝典二十:高频算法之双指针、滑动窗口、二叉树
    一、前言学好算法的根基是:刷题!刷题!刷题!本文将深入探讨高频算法中的双指针、滑动窗口以及二叉树。题目均来源于https://leetcode.cn/。重点关注每种题目的解题思路和总结,通过详尽的解答,包括解答思路和每一步的代码实现,以帮助读者快速理解并掌握这些算法。二、双指针双指......
  • 基于javaweb+jsp的鲜花商城系统
    基于javaweb+jsp的鲜花商城系统的设计与实现~开发语言:Java数据库:MySQL技术:Spring+SpringMVC+MyBatis+JSP工具:IDEA/Ecilpse、Navicat、Maven系统展示前台后台摘要  当下,正处于信息化的时代,许多行业顺应时代的变化,结合使用计算机技术向数字化、信息化建设迈......