Java数据结构(九)——选择排序、堆排序

news2024/9/21 2:32:46

文章目录

  • 选择排序
    • 算法介绍
    • 代码实现
    • 复杂度和稳定性
  • 堆排序
    • 算法介绍
    • 代码实现
    • 复杂度和稳定性

选择排序

算法介绍

选择排序是一种简单直观的排序算法。它的工作原理是:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

算法步骤:

  1. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
  2. 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
  3. 重复第二步,直到所有元素均排序完毕。

【图示】

在这里插入图片描述


代码实现

    public void selectSort(int[] array) {
        for (int i = 0; i < array.length; i++) {
            int min = i;
            for (int j = i + 1; j < array.length; j++) {
                //不断更新最小值的下标
                if(array[j] < array[min]) {
                    min = j;
                }
            }
            //交换
            int tmp = array[i];
            array[i] = array[min];
            array[min] = tmp;
        }
    }

复杂度和稳定性

时间复杂度O(N^2)

空间复杂度O(1)

稳定性不稳定,因为相同的元素可能在排序过程中被交换到不同的位置。


堆排序

算法介绍

堆排序是一种基于比较选择的排序算法,它利用堆这种数据结构所设计。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。

堆排序就要用到堆。关于堆以及建堆,在这一篇Java数据结构(七)——优先级队列与PriorityQueue-CSDN博客中已经介绍了。不过,为了方便以及复习需要,我们再介绍一遍堆的一些知识点以及建堆的思考过程:


堆的逻辑结构是一棵完全二叉树堆的物理结构是一个数组,通过下标表示父子结点的关系,这个数组的元素是按照层序遍历(广度优先)的方式存储的。

以下左图为例,其堆的数组元素为 { 6 ,8,10,12,14,16,18 }

【算法】小白的算法笔记:堆排序 (,,• ₃ •,,) - 知乎


堆的重要公式:(parent、child、leftchild、rightchild均为下标值)

  • parent = (child - 1) / 2
  • leftchild = parent * 2 + 1
  • rightchild = parent * 2 + 2

堆的两个特性:

  1. 结构性:用数组表示一棵完全二叉树
  2. 有序性:任一结点的关键字是其子树所有节点的最大值(或最小值)。
    “最大堆”,也称“大顶堆”(或“大根堆”),简称大堆。特点是:父亲大于等于孩子
    “最小堆”,也称“小顶堆”(或“小根堆”),简称小堆。特点是:父亲小于等于孩子

建堆以及排序的思考

建堆的关键是向下调整算法:

向下调整算法

前提:左右子树必须同为大堆或小堆

以小堆为例,父亲跟它的左右孩子的较小值比较,如果父亲比较小值大,那么交换较小孩子和父亲,父亲的值不变位置变化,然后继续和现在位置的左右孩子的较小值比较,父亲大继续交换,直到叶子节点终止(这是算法终止的第一种情况)。如果在到达叶子节点前,出现父亲比左右孩子较小值还要小的情况,那么不执行交换,终止算法执行(第二种终止情况)。

以大堆为例,父亲跟它的左右孩子的较大值比较,如果父亲比较大值小,那么交换较大孩子和父亲,父亲的值不变位置变化,然后继续和现在位置的左右孩子的较大值比较,父亲小继续交换,直到叶子节点终止(这是算法终止的第一种情况)。如果在到达叶子节点前,出现父亲比左右孩子较大值还要大的情况,那么不执行交换,终止算法执行(第二种终止情况)。

如果逻辑上的完全二叉树不满足前提条件,怎么办?

既然对根节点没法使用向下调整算法,我们不妨从树的其他满足前提条件的结点开始使用向下调整算法。

从哪些结点开始使用向下调整算法?从叶子结点?

叶子节点没有左右孩子,那么默认就是一个小堆(或大堆),没必要对它使用向下调整算法。

因此,我们开始使用向下调整算法的结点是倒数第一个非叶子节点,然后将下标减1,就是倒数第二个非叶子节点,以此类推,结果建堆成功。

怎么找到倒数第一个非叶子结点?

堆数组最后一个元素是逻辑完全二叉树的最右边的叶子节点,它的父亲就是倒数第一个非叶子结点。

最右边的叶子节点的在数组中的下标是len - 1(len是数组的元素个数),要找它的父亲,用到父子结点关系公式:parent = (child - 1) / 2

代入得到倒数第一个非叶子节点对应的下标值:(len - 1 - 1) / 2

建堆的过程了解了,假如我要排升序,那么我该建大堆还是小堆?建堆后怎么使用堆实现排序?

第一反应就是建小堆,最小数在堆顶,堆的物理结构是数组,当我们把第一个元素(建小堆后第一个元素是序列中的最小元素)拿出来后,之后需要在剩下的数中再去选数,但是剩下的树的结构都乱了,需要重新建堆才能选出下一个数,建堆的时间复杂度为O(N),这样反复建堆可以实现排序的目的,不过堆排序就失去了效率优势。

所以我们排升序需要建大堆,最大数在堆顶,每次取堆顶元素,与末尾元素交换,之后再对交换后的堆顶元素执行一次向下调整算法即可继续保持大堆,并且每次交换后待排序的元素就会减少一个。

比如:某个序列建大堆后数组元素顺序为:{ 9,8,6,7,3,2,1,5,4,0 },9是堆顶元素,将堆顶元素取出来与0元素交换,得到{ 0,8,6,7,3,2,1,5,4,9 },9是最大元素,不需要再参与排序,除去9后前 len - 1 个元素可以执行向下调整算法,0的左右子树都是大堆,算法执行完毕后,次大的元素到达堆顶,将次大的元素与倒数第二个元素交换,此时倒数一二个元素已经不需要参与排序,以此类推,便实现了顺序堆排序。


所以,堆排序的过程分为两个主要阶段:

  1. 建堆:将无序序列构建成一个堆,根据升序或降序需求选择最大堆或最小堆。
  2. 排序:将堆顶元素(最大或最小元素)与堆的末尾元素进行交换,此时末尾就为最大值(或最小值)。然后将剩余n-1个元素重新调整结构,使其满足堆的定义,之后再重复此过程,直到整个序列有序。

【图示】

仍然以排升序为例,按照上面的分析,我们首先要将序列建为大根堆,建堆

在这里插入图片描述

接着排序

在这里插入图片描述


代码实现

//向下调整    
private void shitDown(int[] array, int root, int len) {
        int parent = root;
        int child = parent * 2 + 1;
        while(child < len) {
            if(child + 1 < len && array[child] < array[child+1]) {
                child++;
            }
            if(array[child] > array[parent]) {
                int tmp = array[child];
                array[child] = array[parent];
                array[parent] = tmp;
                parent = child;
                child = parent * 2 + 1;
            }else {
                break;
            }
        }
    }
	//堆排序
    public void heapSort(int[] array) {
        //建堆(从第一个非叶子结点开始)
        for(int i = (array.length - 1 - 1) / 2; i >= 0; i--) {
            shitDown(array, i, array.length);
        }
        //排序(每趟:交换+向下调整)
        int end = array.length - 1;
        while(end > 0) {
            int tmp = array[0];
            array[0] = array[end];
            array[end] = tmp;
            shitDown(array, 0, end);
            end--;
        }
    }
  • 注意排序时,每趟排好一个元素,排好的元素不参与堆的组成了,即向下调整算法与它们无关,体现在代码中为end--;

复杂度和稳定性

时间复杂度O(N*log2N)

堆排序的整体时间复杂度由建堆和排序两部分组成:

  • 建堆时间复杂度:O(N)
  • 排序时间复杂度:O(N*log2N)

由于排序部分的时间复杂度更高,所以堆排序的整体时间复杂度是 O(n log n)

空间复杂度O(1)

稳定性不稳定,相同值的元素可能会经过多次交换,从而导致它们在排序后的相对位置发生改变。


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

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

相关文章

【二分查找】锦集

二分查找锦集 二分前言1. 二分查找1.1 题目来源1.2 题目描述1.3 代码展示 2. 在排序数组中查找元素的第一个和最后一个位置2.1 题目来源2.2 题目描述2.3 解题分析 3. 搜索插入位置3.1 题目来源3.2 题目描述3.3 解题分析 4. x 的平方根4.1 题目来源4.2 题目描述4.3 解题分析 5. …

Oracle rman 没有0级时1级备份和0级大小一样,可以用来做恢复 resetlogs后也可以

文档说了 full backup 不能 用于后续的level 1&#xff0c;没说level 1没有level 0 是不是level 1就是level 0&#xff1f; 1级备份变0级的原因 及 Enabling Change Tracking生效没有-CSDN博客 这个文档说明1级备份时没有找到0级就是0级备份&#xff0c;可以用来完整恢复的。…

春日美食家:SpringBoot网上订餐系统

1 绪论 1.1 研究背景 随着互联网技术的快速发展&#xff0c;网络时代的到来&#xff0c;网络信息也将会改变当今社会。各行各业在日常企业经营管理等方面也在慢慢的向规范化和网络化趋势汇合[13]。电子商务必将成为未来商务的主流&#xff0c;因此对于餐饮行业来说&#xff0c;…

51单片机的无线病床呼叫系统【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块温湿度传感器模块矩阵按键时钟模块等模块构成。适用于病床呼叫系统、16床位呼叫等相似项目。 可实现基本功能: 1、LCD1602实时显示北京时间、温湿度信息、呼叫床位等信息&#xff1b; 2、DHT11采集病房温湿度信息&…

RTMP播放器延迟最低可以做到多少?

技术背景 RTMP播放器的延迟可以受到多种因素的影响&#xff0c;包括网络状况、推流设置、播放器配置以及CDN分发等。因此&#xff0c;RTMP播放器的延迟并不是一个固定的数值&#xff0c;而是可以在一定范围内变化的。 正常情况下&#xff0c;网上大多看到的&#xff0c;针对R…

【GIS系列】通过Java代码高效实现ArcGIS SDE数据库的数据叠加分析

作者&#xff1a;后端小肥肠 &#x1f347; 我写过的文章中的相关代码放到了gitee&#xff0c;地址&#xff1a;xfc-fdw-cloud: 公共解决方案 &#x1f34a; 有疑问可私信或评论区联系我。 &#x1f951; 创作不易未经允许严禁转载。 本文涉及GDAL及GeoTools代码实践&#xff…

计算机毕业设计选题推荐-宠物店管理系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

机器学习(9.2-9.8)pytorch学习(二)

文章目录 摘要Abstract 1 torch 和 torchvision1.1 查看CIFAR10数据集内容1.2 Dataloader的使用 2 神经网络的构建2.1 神经网络的基本骨架2.2 卷积层原理2.2.1 卷积基本原理2.2.2 padding 2.3 构建一个卷积神经网络2.4 池化层2.5 非线性激活2.5.1 RELU的使用2.5.2 Sigmoid的使用…

【开源免费】基于SpringBoot+Vue.J大学生租房平台(JAVA毕业设计)

本文项目编号 T 019 &#xff0c;文末自助获取源码 \color{red}{T019&#xff0c;文末自助获取源码} T019&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

java基础概念21-权限修饰符、代码块

一、权限修饰符 1-1、作用 权限修饰符&#xff0c;是用来控制一个成员能够被访问的范围的。 可以修饰&#xff1a;成员变量&#xff0c;方法&#xff0c;构造方法&#xff0c;内部类。 1-2、权限修饰符的分类 二、代码块 局部代码块构造代码块静态代码块 2-1、局部代码块 …

【C++ Primer Plus习题】12.5

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream> #include <cstdlib> #in…

Linux-【组管理、权限管理、定时任务调度】

目录 前言 Linux组基本介绍 文件/目录 所有者 查看文件 所有者 修改文件所有者 文件/目录 所在组 修改文件/目录 所在组 其它组 改变用户所在组 权限的基本介绍 rwx权限 rwx作用到文件 rwx作用到目录 修改权限 第一种方式&#xff1a;、-、变更权限 第二种方式…

Java进阶13讲__第11讲

配置文件 日志 1. Properties属性文件 1.1 特点、作用 都只能是键值对键不能重复文件后缀一般是.properties结尾的 1.2 读取 package cn.hdc.oop10.properties;import java.io.FileNotFoundException; import java.io.FileReader; import java.util.Properties; import j…

「iOS」折叠cell

iOS学习 前言简单的折叠cell效果原理 稍作修改总结 前言 在暑期仿写中&#xff0c;3G share项目里我们简单的使用了折叠cell。现在写一篇博客来总结该方法。 简单的折叠cell 效果 先看效果&#xff1a; 原理 将cell的高度设置为一个单元格的高度。创建一个按钮&#xff0…

【C++】作用域指针、智能指针、共享指针、弱指针

十、智能指针、共享指针 从上篇文章 【C】如何用C创建对象&#xff0c;理解作用域、堆栈、内存分配-CSDN博客 中我们知道&#xff0c;你的对象是创建在栈上还是在堆上&#xff0c;最大的区别就是对象的作用域不一样。所以在C中&#xff0c;一旦程序进入另外一个作用域&#xf…

【xinference】(19):在L40设备上通过Xinference框架,快速部署CogVideoX-5b模型,可以生成6秒视频,速度比409D快一点

1&#xff0c;关于Xinference Xorbits Inference (Xinference) 是一个开源平台&#xff0c;用于简化各种 AI 模型的运行和集成。借助 Xinference&#xff0c;您可以使用任何开源 LLM、嵌入模型和多模态模型在云端或本地环境中运行推理&#xff0c;并创建强大的 AI 应用。 htt…

鸿蒙开发笔记_电商严选01_登录页面(静态页面)

由于上班较忙,抽空闲暇时间,快速更新中。。。 效果图 登录页面(静态页面) import CommonConstants from ./CommonConstants;/*** 登录页面*/ // 输入文本框,的自定义样式扩展 // @Extend装饰器表示继承、扩展的意思。这里代表:自定义样式扩展 @Extend(TextInput) functio…

Qt使用小技巧之按钮动态变化

前言 最近写小demo中无意发现的&#xff0c;是想实现当鼠标悬停到按钮上面的时候&#xff0c;按钮实现动态变化&#xff0c;让人知道鼠标经过了按钮&#xff0c;效果如下 hoverDynamicPushButton 正文 首先是将按钮的边框给去掉&#xff0c;然后设置下它的悬停伪状态就行了 格…

linux日常使用命令总结

一、文件复制 在 Linux 中&#xff0c;复制文件是一个常见的操作&#xff0c;通常使用 cp 命令来完成。cp 命令提供了丰富的选项来满足不同的需求。下面是使用 cp 命令复制文件的一些基本用法和示例。 基本用法 cp 命令的基本语法如下&#xff1a; cp [选项] 源文件 目标文…

京东获得JD商品详情 API 返回值说明||京东商品详情数据采集API接口详解

item_get-获得JD商品详情 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,item_get,item_search_shop等]cacheStri…