排序——选择排序(直接选择排序和堆排)

news2024/11/26 17:46:22

本专栏和大家分享关于排序的算法,其中有插入排(直接插入排序和希尔排序)、选择排序(直接选择排序和堆排)、交换排序(冒泡排序和快速排序)、归并排序以及其他非基于比较的排序

本文与大家分享选择排序

目录

 1.直接选择排序

优化:

时空复杂度:

稳定性:

2.堆排

向下调整的代码如下:

那么我们将数组向下调整后有什么用呢??

具体代码:

时空复杂度:

稳定性:

感谢您的访问!!!期待您的关注!!!


 1.直接选择排序

实际上就是在遍历的时候,每次记录i下标之后的最小值,放到i下标位置,这样最后就能形成一个有序数组,比较容易理解,我们直接通过代码演示

public static void selectSort(int[] array){
        for(int i = 0; i < array.length - 1; i++){
            int maxIndex = i;
            for(int j = i + 1; j < array.length; j++){
                if(array[j] > array[maxIndex]){
                    maxIndex = j;
                }
            }
            if(maxIndex != i){
                int tmp = array[maxIndex];
                array[maxIndex] = array[i];
                array[i] = tmp;
            }
        }
    }

但是时间复杂度太大!为O(N^2),是不稳定的排序

优化:

基于前面,我们可以在每一次遍历中不只是找最小值,而是最大值和最小值一起找

如图所示,我们定义一个左指针和一个右指针,接着利用i在left ~ right 里面遍历,找到一个最小值的下标和一个最大值的下标,将最小值与left的值进行交换,最大值与right的值进行交换,使得该区间的最小值和最大值分别在最左边和最右边的位置;接着 left ++,right--,缩小范围继续查找

但是有一个细节问题需要考虑:

如果我们某一段区间出现这种情况,我们找出最小值为1,最大值为10,交换left的元素与最小值:

接着我们要交换最大值与right的元素就会发现最大值原本是left上面的10,现在变成了1.这是因为我们的最大值刚好是left上的元素,而原来left上的元素又被最小值交换到minIndex的位置去了,因此当我们的maxIndex == left的时候,我们在交换完最小值后,需要将maxIndex = minIndex,去交换后的地方找原来的最大值

    public static void OptimizedSelectSort(int[] array){
        int left = 0;
        int right = array.length-1;
        while(left < right){
            int minIndex = left;
            int maxIndex = left;
            for(int i = left+1; i <= right; i++){
                if(array[i] < array[minIndex]){
                    minIndex = i;
                }
                if(array[i] > array[maxIndex]){
                    maxIndex = i;
                }
            }
            swap(array,left,minIndex);
            if(maxIndex == left){
                maxIndex = minIndex;
            }
            swap(array,right,maxIndex);
            left++;
            right--;
        }
    }

    private static void swap(int[] array,int i,int j){
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

时空复杂度:

但是实际上时间复杂度还是O(N^2),分析时间复杂度的一种方法是考虑比较的次数。内层循环中,每个元素都可能与最大值和最小值进行比较,所以比较的次数仍然是 O(n^2)。虽然通过同时找到最大和最小值,每个元素可能会被比较两次,但这并不改变算法的时间复杂度。

空间复杂度为O(1)

稳定性:

是不稳定的排序:主要是因为在选择最小元素的过程中,相同元素的相对位置可能被打乱。


2.堆排

实际上认识了优先级队列的向下调整就能够理解堆排序

如果我们有如上图这样的数据,我们怎么将它设置为大根堆呢??

我们采用从最后一棵子树开始,依次向下调整的方法

假设每一棵子树的父亲节点下标为parent,子节点为child,那么最后一棵子树的子节点下标为 arr.length - 1,就能得到最后一棵子树的父亲节点下标为parent = (child - 1) / 2,即(arr.length - 1 ) /2 ;那么我们就从(arr.length - 1 ) /2 这个节点往前遍历,直到parent < 0,每次遍历都要针对每课子树进行向下调整

由于我们要建立的是大根堆,那么要求每棵子树的父亲节点都要大于任意一个子节点,那么我们就要在子节点里面找到一个最大的子节点,判断这个子节点是否大于父亲节点,如果大于,则交换

以4下标为根节点的子树已经判断完了,接下来parent--,判断以3下标为节点的所有子树

找到子节点中最大的一个,即为8,判断 9 > 4,那么9 与 4交换

那么以3下标为根节点的子树也就判断完了,接下来判断以2为节点的子树

可以看到,此时子树就不止一棵了,我们先判断以2为父亲节点的树,找到子节点中最大的,为10, 10 > 2,那么10与2交换,

此时我们会发现,交换完后,以4下标为父亲节点的树就不满足大根堆的要求了,因此我们要让parent = child,再继续向下调整,直到每课子树都满足向下调整的要求,交换完后是这样的

 

然后parent继续--,继续重复上述的流程即可

向下调整的代码如下:

public static void heap(int[] array){
         for(int parent = (array.length - 1 - 1) / 2; parent >= 0; parent--){
             siftDown(array,parent);
         }
     }
 ​
     private static void siftDown(int[] array,int parent){
         int child = 2 * parent + 1;
         while(child < array.length){
             if(child + 1 < array.length && array[child] < array[child+1]){//找到子节点最大的一个
                 child++;
             }
             if(array[parent] < array[child]){
                 swap(array,parent,child);
                 parent = child;
                 child = 2 * parent + 1;
             }else{
                 break;//不需要交换则说明这颗子树满足条件
             }
 ​
         }
     }
 ​
     private static void swap(int[] array,int i,int j) {
         int tmp = array[i];
         array[i] = array[j];
         array[j] = tmp;
     }

那么我们将数组向下调整后有什么用呢??

如图所示,我们以数组{6,4,7,5,8,3,9,2,10,1}建立完大根堆后,堆顶元素就是数组里面最大的元素,那么我们如果要得到增序列,就可以将堆顶元素与最后一个元素进行交换,就是将最大的数往后放接着再次进行向下调整,但是此时的向下调整不包括刚刚放进去的最大值,这样我们就能将第二大的元素通过向下调整放到堆顶,再次进行交换...依次类推

具体代码:

 public static void heapSort(int[] array){
        if(array.length == 0){
            return;
        }
        for(int parent = (array.length-1-1) / 2; parent >= 0; parent--){
            siftDown(array,parent,array.length);
        }
        int end = array.length - 1;
        while(end > 0){
            swap(array,0,end);
            siftDown(array,0,end);
            end--;
        }
    }

    private static void siftDown(int[] array,int parent,int end){
        int child = 2 * parent + 1;
        while(child < end){
            if(child + 1 < end&& array[child] < array[child+1]){//找到子节点最大的一个
                child++;
            }
            if(array[parent] < array[child]){
                swap(array,parent,child);
                parent = child;
                child = 2 * parent + 1;
            }else{
                break;//不需要交换则说明这颗子树满足条件
            }

        }
    }

    private static void swap(int[] array,int i,int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

时空复杂度:

堆排序包括两个主要步骤:建堆和排序。建堆过程的时间复杂度为O(n),排序过程,需要遍历每个节点,将第一个节点(最大的节点)放到末尾,同时每次都要进行向下调整,时间复杂度为O(n log n)。因此,整体的时间复杂度为O(n + n log n),简化后为O(n log n)。

空间复杂度为O(1)

稳定性:

不稳定的排序

在堆排序中,每次从堆中取出最大或最小元素时,都会与堆中的最后一个元素交换,然后重新调整堆以维持堆的性质。这个交换过程可能会导致相同元素的相对顺序发生改变,因此堆排序是不稳定的。

感谢您的访问!!!期待您的关注!!!

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

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

相关文章

多微信聚合聊天神器,让你的社交更高效!

对于那些拥有多个微信号的用户来说&#xff0c;频繁地在不同微信号和设备之间切换既麻烦又容易搞混。这时候&#xff0c;一款多微信聚合聊天神器——微信管理系统应运而生&#xff0c;为我们带来了极大的便利与高效。 下面一起来看看它都有哪些功能吧&#xff01; 1、多微信同…

webpack项目打包console git分支、打包时间等信息 exec

相关链接 MDN toLocaleString child_process Node.js strftime 格式 代码 buildinfo.js const { execSync, exec } require("child_process"); // exec: 在 Windows 执行 bat 和 cmd 脚本// execSync 同步 // exec 异步// exec 使用方法 // exec(git show -s,…

什么是递归与示例

什么是递归&#xff1a; 递归是将大问题拆成相同的若干小问题&#xff0c;利用自己调用自己的方式解决问题。 递归的重点就是如何将问题拆解&#xff0c;并在什么样的条件跳出自我循环&#xff0c;这是递归的难点。 如何理解递归&#xff1a; 示例&#xff1a;已知有五个人…

Lazarus 4 Android 环境搭建

一、下载相关文件 最关键的就是这两个文件&#xff0c;其他的JDK、NDK、Java环境另说。 这里要注意&#xff0c;gradle版本必须小于7&#xff0c;不然无法使用。 二、IDE内部设置 安装完成laz4A之后 可能会报错&#xff0c;无需理会&#xff0c;启动IDE即可。 安装卸载软件…

首个基于SSM-Transformer混合架构,开源商业大模型Jamba

3月29日&#xff0c;知名AI研究实验室AI21在官网开源了&#xff0c;首个基于SSM-Transformer混合架构的商业大模型——Jamba。 目前&#xff0c;ChatGPT、Stable Difusion 、Lyria等产品使用的皆是Transformer架构&#xff0c;虽然在捕捉序列内长距离依赖关系、泛化能力、特征…

NineAi3.5 –支持GPT绘图,语音播报,联网访问,上下文关联,语音模式

NineAi3.5 –支持GPT绘图&#xff0c;语音播报&#xff0c;联网访问&#xff0c;上下文关联&#xff0c;语音模式 基于ChatGPT开发的一个人工智能技术驱动的自然语言处理工具&#xff0c;它能够通过学习和理解人类的语言来进行对话&#xff0c; 还能根据聊天的上下文进行互动&…

5.6 物联网RK3399项目开发实录-Android开发之U-Boot 编译及使用(wulianjishu666)

物联网入门到项目实干案例下载&#xff1a; https://pan.baidu.com/s/1fHRxXBqRKTPvXKFOQsP80Q?pwdh5ug --------------------------------------------------------------------------------------------------------------------------------- U-Boot 使用 前言 RK U-B…

多系统使用ffmpeg读取麦克风数据

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、命令行1.Ubuntu1.alsa2.pulseaudio 2.Windows1.dshow 二、代码总结 前言 最近在搞一个项目需要用到麦克风读取数据并分析&#xff0c;我的开发环境是Ubunt…

[Windows]服务注册工具(nssm)

文章目录 官网下载地址百度云下载地址NSSM常用命令 使用场景&#xff1a;例如现在我们想开启自动启动一个Java服务,nginx,node等。 官网下载地址 https://nssm.cc/download 百度云下载地址 链接&#xff1a;https://pan.baidu.com/s/111fkBWIS7CTlWIj80Kc8Sg?pwdanan 提取码…

解码视频流在opengl中的贴图投影计算

解码视频流在opengl中的贴图投影计算 修改顶点着色器cpp 文件放大缩小 我们把视频当成纹理,首先要确定贴入的坐标&#xff0c;原始坐标如下所示 static float vertices[] {// ---- 位置 ---- ---- 颜色 ---- - 纹理坐标 -1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f…

js计算点到直线的距离并使用canvas可视化

使用方程计算 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Distance to Line Visualization</…

题目:小明的背包2(蓝桥OJ 1175)

问题描述&#xff1a; 解题思路&#xff1a; 本题是完全背包模板题&#xff0c;与01背包的不同处在于状态是由小到大转移。 代码&#xff1a; #include <bits/stdc.h> using namespace std; const int N 1e3 9;int dp[N]; // N表示体积int main() {int n, m;cin >…

健身运动蓝牙耳机什么牌子好?五大业内顶级优品推荐

在当下这个健身热潮席卷的时代&#xff0c;越来越多的人开始注重运动与健康&#xff0c;而音乐作为运动时的最佳伴侣&#xff0c;无疑为锻炼过程增添了不少乐趣。为了在运动时享受音乐&#xff0c;一款优质的健身运动蓝牙耳机显得尤为重要&#xff0c;市场上各大品牌纷纷推出自…

每日面经分享(SpringBoot part4:Controller层)

SpringBoot Controller层的作用 a. 请求映射&#xff1a;Controller层使用注解&#xff08;如RequestMapping、GetMapping、PostMapping等&#xff09;将HTTP请求映射到相应的方法上。这些方法根据URL路径、请求方法、请求参数等来决定要执行的操作。 b. 参数解析&#xff1a;C…

KUKA机器人更改时间和HMI最小化设置

在使用 KUKA 机器人时&#xff0c;示教器上左边有个“表”的图标&#xff0c;点一下就会显示时间。但一般不准&#xff0c;想要更改时间可以通过HMI最小化后进行更改设置。更改时间需要将示教器界面最小化&#xff0c;也就是进入Windows 界面。通过以下步骤可以进行设置&#x…

ES学习日记(二)-------集群设置

上一节写了elasticsearch单节点安装和配置,现在说集群,简单地说就是在多台服务器上搭建单节点,在配置文件里面增加多个ip地址即可,过程同单节点部署,主要说集群配置 注意:不建议在之前单节点es上修改配置为集群,据说运行之后会生成很多文件,在单点基础上修改容易出现未知问题,…

微信支付平台与微信服务号关联配置要点

目录 JSAPI支付 前期资料及相关准备 申请微信服务号 服务号配置要点 微信认证 基本配置 功能设置 申请微信支付号 支付号配置要点 设置操作密码 API安全 开发设置 与服务号关联 小结 JSAPI支付 我们的开发应用场景以JSAPI支付为举例&#xff0c;这也是常用的一…

基于Java+SpringBoot+Mybaties+layui+Vue+elememt 实习管理系统 的设计与实现

一.项目介绍 前台功能&#xff1a;用户进入系统可以实现首页&#xff0c;系统公告&#xff0c;个人中心&#xff0c;后台管理等功能进行操作 后台由管理员&#xff0c;实习单位&#xff0c;教师和学生&#xff0c;主要功能包括首页&#xff0c;个人中心&#xff0c;班级管理&am…

Python多任务处理---多线程

引入 生活中&#xff0c;我们在电脑上打开了一个word, 这个word对操作系统来说就是一个进程。我们在进行word操作的时候&#xff0c;比如在你打字的时候&#xff0c;该word同时可以进行文字检查。发现了没&#xff0c;在同一个进程中&#xff0c;我们也可以进行同时操作。…

编程语言 MoonBit 本周有超多重磅更新等你来探索:expect 测试添加 inspect 函数,还有……

MoonBit 更新 1. expect 测试添加 inspect 函数 expect 测试添加针对 Show 接口的 inspect 函数&#xff0c;签名如下&#xff1a; pub fn inspect(obj: Show,~content: String "",~loc: SourceLoc _,~args_loc: ArgsLoc _ ) -> Result[Unit, String]⚠️ 此…