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

2024-04-01 16:28
文章标签 选择 排序 直接 堆排

本文主要是介绍排序——选择排序(直接选择排序和堆排),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

本文与大家分享选择排序

目录

 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.chinasem.cn/article/867612

相关文章

如何选择适合孤独症兄妹的学校?

在探索适合孤独症儿童教育的道路上,每一位家长都面临着前所未有的挑战与抉择。当这份责任落在拥有孤独症兄妹的家庭肩上时,选择一所能够同时满足两个孩子特殊需求的学校,更显得尤为关键。本文将探讨如何为这样的家庭做出明智的选择,并介绍星贝育园自闭症儿童寄宿制学校作为一个值得考虑的选项。 理解孤独症儿童的独特性 孤独症,这一复杂的神经发育障碍,影响着儿童的社交互动、沟通能力以及行为模式。对于拥有孤独症兄

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

hdu 1285(拓扑排序)

题意: 给各个队间的胜负关系,让排名次,名词相同按从小到大排。 解析: 拓扑排序是应用于有向无回路图(Direct Acyclic Graph,简称DAG)上的一种排序方式,对一个有向无回路图进行拓扑排序后,所有的顶点形成一个序列,对所有边(u,v),满足u 在v 的前面。该序列说明了顶点表示的事件或状态发生的整体顺序。比较经典的是在工程活动上,某些工程完成后,另一些工程才能继续,此时

cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个?

跨平台系列 cross-plateform 跨平台应用程序-01-概览 cross-plateform 跨平台应用程序-02-有哪些主流技术栈? cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个? cross-plateform 跨平台应用程序-04-React Native 介绍 cross-plateform 跨平台应用程序-05-Flutte

如何选择SDR无线图传方案

在开源软件定义无线电(SDR)领域,有几个项目提供了无线图传的解决方案。以下是一些开源SDR无线图传方案: 1. **OpenHD**:这是一个远程高清数字图像传输的开源解决方案,它使用SDR技术来实现高清视频的无线传输。OpenHD项目提供了一个完整的工具链,包括发射器和接收器的硬件设计以及相应的软件。 2. **USRP(Universal Software Radio Periphera

《数据结构(C语言版)第二版》第八章-排序(8.3-交换排序、8.4-选择排序)

8.3 交换排序 8.3.1 冒泡排序 【算法特点】 (1) 稳定排序。 (2) 可用于链式存储结构。 (3) 移动记录次数较多,算法平均时间性能比直接插入排序差。当初始记录无序,n较大时, 此算法不宜采用。 #include <stdio.h>#include <stdlib.h>#define MAXSIZE 26typedef int KeyType;typedef char In

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h>#include <stdlib.h> void shellSort(int data[], int n){// 划分的数组,例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量,换值int temp;in