数据结构从入门到精通——直接选择排序

news2024/11/23 21:23:13

直接选择排序

  • 前言
  • 一、选择排序的基本思想:
  • 二、直接选择排序
  • 三、直接选择排序的特性总结:
  • 四、直接选择排序的动画展示
  • 五、直接选择排序的代码展示
    • test.c
  • 六、直接选择排序的优化
    • test.c


前言

直接选择排序是一种简单的排序算法。它的工作原理是每一次从未排序部分选出最小(或最大)的一个元素,存放到排序序列的起始位置,然后再从剩余未排序元素中继续寻找最小(或最大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。这种算法的时间复杂度为O(n^2),其中n是待排序元素的数量,因此在处理大数据集时效率较低。然而,它的实现简单,对于小规模的数据排序是一个不错的选择。


一、选择排序的基本思想:

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。

选择排序的基本思想是从未排序的序列中找到最小(或最大)的元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(或最大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

这种排序算法的优点在于其实现过程相对简单,对于小规模的数据排序,其效率是可以接受的。然而,选择排序也存在明显的缺点。由于其每次都需要从未排序的元素中找到最小(或最大)的元素,这导致算法的时间复杂度为O(n^2),其中n为待排序元素的数量。这意味着当处理大规模数据时,选择排序的性能可能会变得非常低下。

在实际应用中,选择排序往往不是最优的选择,特别是对于大规模数据的排序。更高效的排序算法,如快速排序、归并排序、堆排序等,在处理大规模数据时,通常会有更好的性能表现。

但是,选择排序的思想在某些特定情境下仍然有其应用价值。例如,在某些需要稳定排序且数据量较小的场景中,选择排序可以作为一个简单且有效的解决方案。此外,选择排序的思想也可以作为其他更复杂排序算法的基础,帮助理解和学习更高级的排序算法。

总的来说,选择排序的基本思想虽然简单,但其性能上的局限性使得它在实际应用中并不常见。然而,这并不意味着它没有价值,通过深入理解和应用选择排序的思想,我们可以更好地理解排序算法的本质,并为学习更高级的排序算法打下坚实的基础。

二、直接选择排序

  • 在元素集合array[i]--array[n-1]中选择关键码最大(小)的数据元素
  • 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
  • 在剩余的array[i]--array[n-2](array[i+1]--array[n-1])集合中,重复上述步骤,直到集合剩余1个元素

直接选择排序是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。

首先,我们假设有一个无序的整数列表,我们想要通过直接选择排序将其按升序排列。算法的工作流程可以分为以下几个步骤:

  1. 找到最小(大)元素:在列表中找到最小(大)的元素。这个步骤通常涉及遍历整个列表,比较每个元素的值。
  2. 交换位置:一旦找到最小(大)元素,将其与列表的第一个元素交换位置。这样,最小(大)的元素就被放到了它应该在的位置。
  3. 移除已排序元素:从列表中移除已排序的第一个元素(现在是最小(大)元素),然后对剩余的元素重复上述两个步骤。
  4. 重复过程:继续这个过程,每次从剩余的未排序元素中找到最小(大)元素,并将其与未排序部分的第一个元素交换。
  5. 结束条件:当整个列表都被排序时,算法结束。

直接选择排序的时间复杂度是O(n^2),其中n是列表的长度。这是因为它包含两个嵌套循环:一个用于找到最小(大)元素,另一个用于遍历整个列表。尽管这种排序方法在处理小型或中型列表时可能是有效的,但对于大型列表,更高效的排序算法(如快速排序、归并排序或堆排序)通常是更好的选择。

然而,直接选择排序有一个显著的优点,那就是它的实现相对简单,对于初学者来说是一个很好的学习工具。通过理解这个算法,可以对排序的基本概念有一个直观的认识,从而为学习更复杂的排序算法打下基础。

在实际应用中,直接选择排序可能不是最优选择,但它在教育、演示和教学方面仍然具有很高的价值。此外,对于某些特定类型的数据集(如部分有序的数据集),直接选择排序的性能可能会比其他算法更好。

三、直接选择排序的特性总结:

  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定

直接选择排序的特性总结起来,主要是其直观易懂、原地排序和不稳定排序的特点。

直接选择排序是一种简单直观的排序算法,其基本原理是每一次从未排序的部分选出最小(或最大)的一个元素,存放到排序序列的起始位置。它的操作过程是从左到右逐个选择剩余元素中的最小者,并将其与未排序部分的第一个元素交换。这种选择过程一直持续到未排序部分为空,排序也就完成了。因此,直接选择排序的直观性是其显著特点之一,使得初学者容易理解和实现。

另一个特性是原地排序,这意味着直接选择排序不需要额外的存储空间来进行排序,它直接在原始数组上进行操作,改变了原始数组的顺序。这一特性使得它在处理内存受限的场景时非常有用。

然而,直接选择排序也是一种不稳定的排序算法。稳定性是指如果两个元素的键值相等,那么在排序之后它们的相对位置不会改变。由于直接选择排序在每次选择最小(或最大)元素进行交换时,可能会改变相等元素的原始相对位置,因此它不具备稳定性。

总的来说,直接选择排序是一种简单直观、原地进行的排序算法,但它是不稳定的。在实际应用中,根据数据的特性和排序要求,可能需要选择更合适的排序算法。例如,对于大规模数据集,直接选择排序的效率可能较低,因为它需要多次遍历和交换操作。而对于小规模数据集或者对稳定性要求不高的场景,直接选择排序则是一个简单有效的选择。

四、直接选择排序的动画展示

直接选择排序是一种简单的排序算法。它通过每次从未排序部分选择最小(或最大)元素,与未排序部分的第一个元素交换位置,从而达到排序的目的。在动画展示中,可以看到每次选择的最小(或最大)元素逐步“冒泡”到已排序部分的末尾,直到整个序列有序。这种排序方法的时间复杂度为O(n^2),适用于小规模数据的排序。

选择排序

五、直接选择排序的代码展示

test.c

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

void SelectSort(int* a, int n);
void PrintArray(int* a, int n);
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void TestSelectSort()
{
	int a[] = { 5, 13, 9, 16, 12, 4, 7, 1, 28, 25, 3, 9, 6, 2, 4, 7, 1, 8 };
	//int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };
	PrintArray(a, sizeof(a) / sizeof(int));

	SelectSort(a, sizeof(a) / sizeof(int));

	PrintArray(a, sizeof(a) / sizeof(int));
}


void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}
void TestOP()
{
	srand(time(0));
	const int N = 10000;
	int* a1 = (int*)malloc(sizeof(int) * N);

	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand();
	}


	int begin1 = clock();
	SelectSort(a1, N);
	int end1 = clock();

	
	
	printf("SelectSort:%d\n", end1 - begin1);

	free(a1);
}
void SelectSort(int* a, int n)
{
	for (int k = 0; k < n; k++)
	{
		int max = 0;
		for (int i = 0; i < n - k; i++)
		{
			if (a[i] >= a[max])max = i;
		}
		Swap(&a[n - 1 - k], &a[max]);
	}
	
}
int main()
{
	TestSelectSort();
	TestOP();

	return 0;
}

这段代码实现的是选择排序(Selection Sort)算法,而不是通常的冒泡排序(Bubble Sort)。选择排序是一种简单直观的排序算法,它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。

接下来我将逐步解释这段代码:

  1. 函数定义:
void SelectSort(int* a, int n)

这是一个名为 SelectSort 的函数,它接受一个整数数组 a 和一个整数 n 作为参数。n 是数组 a 的长度。

  1. 外层循环:for (int k = 0; k < n; k++)

这个循环从 0n-1 迭代,用于确定当前应该放置最小元素的位置。

  1. 初始化 max 变量:int max = 0;

在每次外层循环开始时,max 被初始化为 0。这个变量用于存储当前找到的最小元素的位置。

  1. 内层循环:for (int i = 0; i < n - k; i++)

这个循环从 0n-k-1 迭代。每次迭代,它都会检查从 a[0]a[n-k-1] 的元素,以找到当前最小元素的位置。

  1. 判断并更新最小元素的位置:if (a[i] >= a[max]) max = i;

这个条件检查 a[i] 是否大于或等于 a[max]。如果是,则更新 maxi。注意这里使用了 >= 而不是 >,这意味着如果有多个相同的最小元素,它们都会被正确地处理。

  1. 交换元素:Swap(&a[n - 1 - k], &a[max]);

在内层循环结束后,我们已经找到了从 a[0]a[n-k-1] 中的最小元素,它的位置是 max。现在,我们需要将这个最小元素与当前位置 n-1-k 的元素交换。

整体上,这段代码通过不断地选择并交换最小元素,最终将数组 a 排序为升序。

六、直接选择排序的优化

使用min和max对直接选择排序进行优化可以减少交换的次数。

在原始的直接选择排序算法中,每次迭代会通过查找最小值和最大值的索引来确定需要交换的元素。然后分别进行交换。这样可能会导致不必要的交换操作。

优化的思路是,在每次迭代中,同时查找最小值和最大值的索引,然后将它们记录下来,最后再进行一次交换操作。

具体实现如下:

void SelectSort(int* a, int n)
{
    int begin = 0, end = n - 1;
    while (begin < end)
    {
        int min = begin, max = end;
        for (int i = begin; i <= end; i++)
        {
            if (a[i] <= a[min]) min = i;
            if (a[i] >= a[max]) max = i;
        }

        // 将最小值放到已排序部分的起始位置
        Swap(&a[begin], &a[min]);
        
        // 如果最大值的索引是begin,将最大值的索引更新为min
        if (max == begin) max = min;
        
        // 将最大值放到已排序部分的末尾位置
        Swap(&a[end], &a[max]);
        
        begin++;
        end--;
    }
}

这样的优化可以减少交换的次数,提高排序的效率。同时,可以确保每次迭代只进行一次交换操作,减少了内存的读写次数,提高了算法的性能。

 if (max == begin) max = min;

关于这个代码是为了防止min正好在end,而max正好在begin这种的特殊情况

test.c

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

void SelectSort(int* a, int n);
void PrintArray(int* a, int n);
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void TestSelectSort()
{
	int a[] = { 5, 13, 9, 16, 12, 4, 7, 1, 28, 25, 3, 9, 6, 2, 4, 7, 1, 8 };
	//int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };
	PrintArray(a, sizeof(a) / sizeof(int));

	SelectSort(a, sizeof(a) / sizeof(int));

	PrintArray(a, sizeof(a) / sizeof(int));
}


void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}
void TestOP()
{
	srand(time(0));
	const int N = 10000;
	int* a1 = (int*)malloc(sizeof(int) * N);

	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand();
	}


	int begin1 = clock();
	SelectSort(a1, N);
	int end1 = clock();

	
	
	printf("SelectSort:%d\n", end1 - begin1);

	free(a1);
}
void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int min = begin, max = end;
		for (int i = begin; i <= end; i++)
		{
			if (a[i] <= a[min])min = i;
			if (a[i] >= a[max])max = i;
		}
		Swap(&a[begin], &a[min]);
		if (max == begin)max = min;
		Swap(&a[end], &a[max]);
		begin++;
		end--;
	}
	
	
}
int main()
{
	TestSelectSort();
	TestOP();

	return 0;
}

这段代码实现的是选择排序算法,用于对数组a中的元素进行排序。传入参数是数组a和数组长度n

代码的主要思路是:通过每一次迭代,从未排序的元素中找到最小值和最大值,并将它们分别放到已排序部分的起始位置和末尾位置。然后缩小未排序部分的范围,再次进行迭代直至完成排序。

  1. 初始化变量begin为数组的起始索引0end为数组的终止索引n-1
  2. 进入循环,判断begin是否小于end。如果是,继续下面的操作;如果不是,说明排序已完成,退出循环。
  3. 在每一次迭代中,定义变量minmax,分别用于记录当前未排序部分的最小值和最大值的索引,初始值分别设为beginend
  4. beginend遍历数组a,找到当前最小值和最大值的索引,更新minmax
  5. 交换最小值和begin位置的元素,使当前最小值放到已排序部分的起始位置。
  6. 如果max等于begin,说明最大值原本就在begin位置,交换后已经移到了最小值应该在的位置,所以需要将max更新为min
  7. 交换最大值和end位置的元素,使当前最大值放到已排序部分的末尾位置。
  8. 完成一次迭代后,已排序部分的范围向两端缩小,begin增加1end减少1
  9. 重复2-8步骤,直到begin不小于end,排序完成。

总结起来,选择排序每次迭代都会确定未排序部分的最小值和最大值的位置,并将它们交换到已排序部分的起始和末尾位置。通过多次迭代,最终达到整个数组的有序状态。

在这里插入图片描述


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

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

相关文章

kafka集群介绍及搭建

介绍 kafka是一个高性能、低延迟、分布式的消息传递系统&#xff0c;特点在于实时处理数据。集群由多个成员节点broker组成&#xff0c;每个节点都可以独立处理消息传递和存储任务。 路由策略 发布消息由key、value组成&#xff0c;真正的消息是value&#xff0c;key是标识路…

Springboot+vue的船舶维保管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的船舶维保管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09…

第二证券|比特币重拾升势 新高背后风险涌动

近期价格连涨的比特币再度创出新高。金融出资报记者注意到&#xff0c;3月13日&#xff0c;在改写2021年11月69000美元纪录的基础上&#xff0c;比特币价格首次打破73000美元关口&#xff0c;创下73678.5美元的历史新高。 检查近日社交平台话题可见&#xff0c;“比特币站上73…

【明道云】如何自动将一笔记录拆分成多比并插入数据库

【背景】 用户录入包含开始日期和结束日期的交易数据&#xff0c;希望系统最终能够给出精确到日次的利润统计图表。 【分析】 颗粒度细化到日次&#xff0c;意味着需要在追加期间交易数据时能够自动拆分为日次颗粒度存储在用于统计的子表中。 这就涉及如何构建数据结构&…

JMeter 二次开发之环境准备

通过JMeter二次开发&#xff0c;可以充分发挥JMeter的潜力&#xff0c;定制化和扩展工具的能力以满足具体需求。无论是开发自定义插件、函数二次开发还是定制UI&#xff0c;深入学习和掌握JMeter的二次开发技术&#xff0c;将为接口功能测试/接口性能测试工作带来更多的便利和效…

win提权第二弹服务提权

阅读须知&#xff1a; 探索者安全团队技术文章仅供参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作,由于传播、利用本公众号所提供的技术和信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者 本人负责&#xff0c;作者不为此承担任何责任,如…

2352.相等行列对

题目&#xff1a;给一个下标从0开始、大小为n x n的整数矩阵grid&#xff0c;返回满足Ri 行和 Cj 列相等的行列对&#xff08;Ri,Cj&#xff09;的数目。 如果行和列以相同的顺序包含相同的元素&#xff08;即相等的数组&#xff09;&#xff0c;则认为二者是相等的。 解题思路…

信息收集:端口扫描原理,端口扫描分类,端口扫描工具,手动判断操作系统,操作系统识别工具

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「专栏简介」&#xff1a;此文章已录入专栏《网络安全自学教程》 端口&系统版本 一、端口扫描1、telnet2、Nmap3、Masscan4、端口扫描原…

【考研数学】《1800》如何衔接《660》/《880》?

基础题做完&#xff0c;不要急着强化 首先做一个复盘&#xff0c;1800基础的正确率如何&#xff0c;如果70%以下的话&#xff0c;从错题入手&#xff0c;把掌握不扎实的地方再进行巩固&#xff0c;否则接下来做题的话效率会很低。 接下来考虑习题衔接的问题。 关于线代复习的…

视频素材库排行榜前六名,推荐大家收藏

大家好&#xff01;今天我要给大家带来的是视频素材库排行榜前十名&#xff0c;让你的视频创作更加别出心裁&#xff01; 蛙学网在视频素材库排行榜中&#xff0c;蛙学网绝对是值得使用的。这里有大量的高质量视频素材&#xff0c;涵盖了各种风格和主题&#xff0c;特别是对于展…

一文读懂什么是序列 (sequence)

sequence 序列 sequence(序列)是一组有顺序的元素的集合 (严格的说&#xff0c;是对象的集合&#xff0c;但鉴于我们还没有引入“对象”概念&#xff0c;暂时说元素) 序列可以包含一个或多个元素&#xff0c;也可以没有任何元素。 我们之前所说的基本数据类型&#xff0c;都…

Vue3:标签的ref属性用法

一、情景说明 我们在写前端页面的时候&#xff0c;肯定会遇到获取DOM内容的情况。 以往&#xff0c;我们是用原生的js方法去获取&#xff0c;如document.getXxxx 但是&#xff0c;这中方法会有个问题&#xff0c;如果父组件和子组件的id相同&#xff0c;则会出错。 在Vue3中&…

【Flutter 面试题】讲一讲 Dart 的一些重要概念?

【Flutter 面试题】讲一讲 Dart 的一些重要概念&#xff1f; 文章目录 写在前面口述回答补充说明完整代码运行结果详细说明 写在前面 &#x1f64b; 关于我 &#xff0c;小雨青年 &#x1f449; CSDN博客专家&#xff0c;GitChat专栏作者&#xff0c;阿里云社区专家博主&#…

04.组件的组成和组件间通信

一、scoped解决样式冲突 1.默认情况&#xff1a; 写在组件中的样式会 全局生效 → 因此很容易造成多个组件之间的样式冲突问题。 全局样式: 默认组件中的样式会作用到全局&#xff0c;任何一个组件中都会受到此样式的影响 局部样式: 可以给组件加上scoped 属性,可以让样式只…

MyBatis核心配置文件:解锁数据之美的密码

MyBatis&#xff0c;这位编程的诗人&#xff0c;通过其独特的核心配置文件&#xff0c;为我们描绘出一幅数据之美的画卷。本篇博客将带你深入探讨MyBatis核心配置文件的奥秘&#xff0c;让你能够更好地理解和运用这个优雅的数据持久化框架。 最近想搞私域&#xff0c;欢迎各位…

腾讯云8核16G轻量服务器支持多少人在线?CPU性能如何?

腾讯云8核16G轻量服务器CPU性能如何&#xff1f;18M带宽支持多少人在线&#xff1f;轻量应用服务器具有100%CPU性能&#xff0c;18M带宽下载速度2304KB/秒&#xff0c;折合2.25M/s&#xff0c;系统盘为270GB SSD盘&#xff0c;月流量3500GB&#xff0c;折合每天116.6GB流量&…

超分辨率(3)--基于RCAN网络实现图像超分辨率重建

目录 一.项目介绍 二.项目流程详解 2.1.数据处理模块 2.2.损失函数设置 2.3.网络模型构建 三.测试网络 一.项目介绍 RCAN&#xff1a;Residual Channel Attention Network&#xff08;残差通道注意网络 ) 卷积神经网络(CNN)的深度对于图像超分辨率(SR)是极其关键的因素…

Python文件处理---os模块、pathlib模块、open()函数

Python 文件处理是一个核心编程概念&#xff0c;涉及到文件的读取、写入、创建、删除以及管理文件路径等操作&#xff0c;让我们循序渐进的一起探究吧 目录 一、open()函数 1、打开文件 2、读取文件 使用 .read() 方法 使用 .readline() 方法 使用 .readlines() 方法 3…

c/c++ 深拷贝和浅拷贝

深拷贝与浅拷贝 深拷贝&#xff08;Deep Copy&#xff09;和浅拷贝&#xff08;Shallow Copy&#xff09;是对象复制的两种不同方式&#xff0c;它们涉及到对象成员数据的复制方式和内存管理。 浅拷贝&#xff08;Shallow Copy&#xff09;&#xff1a; 浅拷贝是指将一个对象的…

移动端滚轮插件mobile-select.js的使用记录

#小李子9479# 把文件下载放到static/mobile-select里面&#xff0c;如下图 分别引入js和css。 注意&#xff1a;一定要在页面加载完成后就初始化这个组件 &#xff0c;否则的话&#xff0c;第一次点击无效。