【数据结构与算法】十大经典排序算法-堆排序

news2024/11/25 8:23:11

🌟个人博客:www.hellocode.top
🏰Java知识导航:Java-Navigate
🔥CSDN:HelloCode.
🌞知乎:HelloCode
🌴掘金:HelloCode
⚡如有问题,欢迎指正,一起学习~~


堆排序是一种高效的排序算法,基于堆数据结构实现。堆是一种特殊的树状结构,具有以下特点:父节点的值大于等于(或小于等于)其子节点的值。堆排序利用堆的性质,将数组看作一个完全二叉树,通过构建最大堆(或最小堆),实现对数组的排序。

基本思想

这里采用五分钟学算法大佬的图解,十分清晰

  1. 构建初始堆:将待排序数组视为一个完全二叉树,从最后一个非叶子节点开始,逐步将树调整为大顶堆(或小顶堆)。
  2. 排序过程:将堆顶元素与最后一个叶子节点交换,然后将堆大小减一,继续调整堆结构,使其重新成为大顶堆(或小顶堆)。
  3. 重复步骤 2,直到堆大小为 1,排序完成。

需要掌握的部分知识:

  • 完全二叉树:指除了最后一层外,其他层的节点都被完全填满,最后一层的节点都靠左排列,并且不存在不规则的空缺。这意味着从根节点到倒数第二层都是满的,最后一层从左到右有可能存在空缺,但不能跳过空缺。
  • 堆:堆是一种基于完全二叉树的数据结构,可分为大顶堆和小顶堆。在大顶堆中,父节点的值大于等于其子节点的值;在小顶堆中,父节点的值小于等于其子节点的值。堆的性质使其适合用来进行排序和实现优先队列等数据结构。
  • 堆的构建和调整:在堆排序中,我们主要关注构建初始堆和调整堆的过程。构建初始堆的目标是将一个无序数组调整为一个最大堆或最小堆。调整堆的目标是保持堆的性质,确保父节点的值大于等于(或小于等于)子节点的值。
  • 完全二叉树中相关计算:对于任意节点来说,左子节点索引计算公式为2*i + 1,右子节点为:2*i + 2,最后一个非叶子节点计算公式为n/2 - 1(n为节点总数,i为当前节点索引)

这里的难点就是对应的概念和计算,拿到待排序数组后,首先需要将其构建为大顶堆(或小顶堆),然后需要进行相应的节点交换,并继续调整结构使其保持大顶堆(或小顶堆)特性,代码层面还需要配合动画多多理解

代码实现

相比之前的几种排序,堆排序就相对复杂一些,需要用到递归的思想。遇到递归,还是要考虑递归的出口,避免无休止的递归,这里使用递归就是不断调整来维持堆结构,自上而下递归,那么递归的出口就是到叶子节点(不能超出数组范围)。

主要分为三个方法:heapSort(对外提供的堆排序方法)、buildMaxHeap(构建初始堆结构方法)、heapify(真正调整堆结构、维持堆规则的方法)

package top.hellocode;


import java.util.Arrays;

/**
 * @author HelloCode
 * @blog https://www.hellocode.top
 * @date 2023年08月13日 20:01
 */
public class HeapSort {
    public static void main(String[] args) {
        int[] arr = {19, 23, 13, 7, 84, 66, 98, 78, 54, 32, 23, 77, 88, 17};
        System.out.println("排序前:" + Arrays.toString(arr));
        heapSort(arr);
        System.out.println("排序后:" + Arrays.toString(arr));
    }

    public static void heapSort(int[] arr) {
        // 构建初始堆结构(默认大顶堆,实现升序排序)
        buildMaxHeap(arr, arr.length);
        // 开始排序
        // 从堆顶取出元素,并和最后一个元素进行交换,并不断调整维持堆结构
        for (int i = arr.length - 1; i > 0; i--) {
            int temp = arr[i];
            arr[i] = arr[0];
            arr[0] = temp;
            heapify(arr, 0, i);
        }
    }

    // 构建初始最大堆
    private static void buildMaxHeap(int[] arr, int length) {
        // 构建大顶堆,从最后一个非叶子节点开始((length - 1) / 2)
        for (int i = length / 2 - 1; i >= 0; i--) {
            heapify(arr, i, length);
        }
    }

    /**
     * 调整堆结构,使其成为最大堆
     * int[] arr:待调整数组
     * int index:子树根节点(从哪里开始向下调整)
     * int length:数组长度,主要用来判断子树递归是否到达了叶子节点(超出数组长度)
     */
    private static void heapify(int[] arr, int index, int length) {
        // 默认假设当前子树根节点为最大值,寻找左右子节点是否有更大值
        int max = index;
        int left = 2 * index + 1;
        int right = 2 * index + 2;
        // 递归终止条件(出口)
        if (left >= length || right >= length) {
            return;
        }
        // 开始比较左右子节点
        if (arr[left] > arr[max]) {
            max = left;
        }
        if (arr[right] > arr[max]) {
            max = right;
        }
        // 如果最大值发生了变化,则进行交换
        // 只要是有交换发生,就需要对其子树继续进行递归调整
        if (max != index) {
            int temp = arr[index];
            arr[index] = arr[max];
            arr[max] = temp;
            // 继续对其子树递归调整
            heapify(arr, max, length);
        }
    }
}

测试:

排序前:[19, 23, 13, 7, 84, 66, 98, 78, 54, 32, 23, 77, 88, 17]
排序后:[7, 13, 17, 19, 23, 23, 32, 54, 66, 77, 78, 84, 88, 98]

优化

堆排序的核心是构建堆和调整堆,可以通过一些优化来提升性能。

  • 在构建堆的时候,有自顶向上和自底向下两种,不同的场景使用不同的方法性能也有不同,这里可以去了解了解,对对应的场景进行优化。

总结

优点

  1. 高效性:堆排序的时间复杂度为 O(n log n),在大规模数据下表现优异。
  2. 不占用额外空间:堆排序是原地排序算法,不需要额外的存储空间。

缺点

  1. 不稳定性:堆排序是不稳定的排序算法,相等元素的相对顺序在排序后可能发生变化。

复杂度

  • 时间复杂度
    • 平均时间复杂度:O(n log n)
    • 最好情况时间复杂度:O(n log n)
    • 最坏情况时间复杂度:O(n log n)
  • 空间复杂度:原地排序,空间复杂度为 O(1)。

使用场景

堆排序适用于大规模数据的排序,尤其在需要稳定排序时(如堆顶元素是最大值时)。虽然实现相对复杂,但其高效性使其成为处理大量数据的有力工具。在实际应用中,堆排序在需要高性能排序时可能是一个不错的选择。

当使用堆排序时,应特别注意其时间和空间复杂度的说明是基于固定的数据集。在实际情况中,堆排序的性能可能因为一些特定因素而有所不同,因此在特定情况下堆排序可能表现更好。

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

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

相关文章

JZ33二叉搜索树的后序遍历序列

题目地址:二叉搜索树的后序遍历序列_牛客题霸_牛客网 题目回顾: 解题思路: 使用栈 栈的特点是:先进后出。 通读题目后,我们可以得出,二叉搜索树是左子节点小于根节点,右子节点大于根节点。 …

wps设置一键标题字体和大小

参考 wps设置一键标题字体和大小:https://www.kafan.cn/A/7v5le1op3g.html 统一一键设置

[FPGA IP系列] 2分钟了解FPGA中的BRAM

FPGA设计中,BRAM是一项非常关键的内置存储资源,FPGA开发需要熟练使用BRAM,今天再复习一下BRAM的知识,包括BRAM的定义、组成、应用等等。 一、BRAM介绍 1、BRAM的定义 RAM是Random Access Memory,也就是随机访问数据…

C字符串练习题(6.3.1)

编写一个程序&#xff0c;从键盘上读入一个小于1000的正整数&#xff0c;然后创建并输出一个字符串&#xff0c;说明该整数的值。例如&#xff0c;输入941&#xff0c;程序产生的字符串是“Nine hundred and forty one”。 #include<stdlib.h> #include<string.h>…

【STM32】FreeRTOS互斥量学习

互斥量&#xff08;Mutex&#xff09; 互斥量又称互斥信号量&#xff08;本质也是一种信号量&#xff0c;不具备传递数据功能&#xff09;&#xff0c;是一种特殊的二值信号量&#xff0c;它和信号量不同的是&#xff0c;它支持互斥量所有权、递归访问以及防止优先级翻转的特性…

Spring Boot 项目实现 Spring AOP

【注】实现在SpringBoot项目中&#xff0c;同时给两个类的方法添加AOP前置通知 1、创建一个SpringBoot项目 2、创建两个目标类和方法 package com.tqazy.learn_spring_project.spring_aop;import org.springframework.stereotype.Service;/*** ClassName SpringAopUserServi…

【树状数组优化哈希DP】CF1801 C

Problem - C - Codeforces 思路&#xff1a; Code&#xff1a; #include <bits/stdc.h>#define lowbit(x) (x & (-x))using i64 long long;constexpr int N 2e5 10; constexpr int mod 1e9 7;std::vector<int> V[N];int n, m, x, mxv 0; int a[N], id[N…

MySQL入门学习教程(一)

mysql简介 1、什么是数据库 &#xff1f; 数据库&#xff08;Database&#xff09;是按照数据结构来组织、存储和管理数据的仓库&#xff0c;它产生于距今六十多年前&#xff0c;随着信息技术和市场的发展&#xff0c;特别是二十世纪九十年代以后&#xff0c;数据管理不再仅仅…

学习笔记整理-JS-02-基本类型

文章目录 一、数据类型简介和检测1. JavaScript中两大数据类型 二、基本数据类型1. 数字类型2. 字符串类型3. 布尔类型4. undefined类型5. null 三、数据类型的转换1. 数据类型的转换 四、重点内容 一、数据类型简介和检测 1. JavaScript中两大数据类型 基本数据类型 Number S…

Android学习之路(3) 布局

线性布局LinearLayout 前几个小节的例程中&#xff0c;XML文件用到了LinearLayout布局&#xff0c;它的学名为线性布局。顾名思义&#xff0c;线性布局 像是用一根线把它的内部视图串起来&#xff0c;故而内部视图之间的排列顺序是固定的&#xff0c;要么从左到右排列&#xf…

最强自动化测试框架Playwright(22)-模拟器

可以使用测试生成器通过仿真生成测试&#xff0c;以便为特定窗口、设备、配色方案生成测试&#xff0c;以及模拟地理位置、语言或时区。测试生成器还可以生成测试&#xff0c;同时保留经过身份验证的状态。 模拟视口大小 Playwright 打开一个浏览器窗口&#xff0c;其视口设置…

电路基础之电容

电容器&#xff08;Capacitor&#xff09;是由两个导体电极之间夹着一个电介质而组成的元件。这两个电极可以是金属板、箔片、涂层等&#xff0c;而电介质则是放置在电极之间的绝缘材料。电容器的基本构成包括以下几个要素&#xff1a; 电极&#xff1a;电容器的电极是两个导体…

无涯教程-Perl - readpipe函数

描述 该函数将EXPR作为命令执行。然后,将输出作为标量文本中的多行字符串返回,或者将行作为列表context中的单个元素返回。 语法 以下是此函数的简单语法- readpipe EXPR返回值 此函数在标量context中返回String,在列表context中返回List。 例 以下是显示其基本用法的示…

HTML详解连载(6)

HTML详解连载&#xff08;6&#xff09; 专栏链接 [link](http://t.csdn.cn/xF0H3)下面进行专栏介绍 开始喽CSS特性继承性注意 层叠性特点 优先级规则公式注意 叠加计算公式&#xff08;每以及之间不存在进位&#xff09;规则 Emmet写法分析属性名属性值注意 背景图平铺方式属性…

【分布式存储】数据存储和检索~B+树

为什么数据存储结构重要 在存储系统中&#xff0c;其实不管数据是什么样的&#xff0c;归根结底其实都还是取决于数据的底层存储结构&#xff0c;而主要常见的就是数据库索引结构&#xff0c;B树、Redis中跳表、以及LSM、搜索引擎中的倒排索引。本质都是如何利用不用的数据结构…

群辉nas看剧设置

首先打开NAS的后台页面&#xff0c;打开“控制面板” 然后依次点开“文件服务--SMB--高级设置”&#xff0c;在最小SMB协议后面的方框选择“SMB1"&#xff0c;然后点击”保存“按钮即可&#xff0c;这里这样设置的原因是因为还有很多旧设备只支持SMB1&#xff08;我几年前…

章节7:Burp Intruder模块

章节7&#xff1a;Burp Intruder模块 参考资料 https://portswigger.net/burp/documentation/desktop/tools/intruder 01 Intruder模块作用与原理 原理 http://xxx.xx.com/bbs/index.php?namewuyanzu&mottogo 对请求参数进行修改&#xff0c;分析响应内容&#xff0…

腾讯:海量小文件场景下CephFS优化之路

Ceph开源社区 2021-02-25 17:58 摘自&#xff1a;https://mp.weixin.qq.com/s/rTNyzY9W3ZunroYo57tjoA 1. 背景 随着大数据、人工智能技术的蓬勃发展&#xff0c;人类对于算力资源的需求也迎来大幅度的增长。在腾讯内部&#xff0c;星辰算力平台以降本增效为目标&#xff0c;…

数据分析 | 随机森林如何确定参数空间的搜索范围

1. 随机森林超参数 极其重要的三个超参数是必须要调整的&#xff0c;一般再加上两到三个其他超参数进行优化即可。 2. 学习曲线确定n_estimators搜索范围 首先导入必要的库&#xff0c;使用sklearn自带的房价预测数据集&#xff1a; import numpy as np import pandas as pd f…