首页 > 编程语言 >【初阶数据结构与算法】八大排序算法之选择排序(直接选择排序、堆排)

【初阶数据结构与算法】八大排序算法之选择排序(直接选择排序、堆排)

时间:2024-12-16 22:31:48浏览次数:5  
标签:mini arr 初阶 int begin 算法 child 排序

在这里插入图片描述

文章目录

一、单向直接选择排序

   选择排序很好理解,就是按照字面意思来,比如我们想要将一个数组排成升序,那么小的值就会被放在前面,我们就可以每次都找到数组中的最小值,把它往前面放,循环往复,最终我们就可以将数组排成升序
   降序也是如此,就是每次我们都找到数组中的最小值,把它往后面放,最后我们就能将数组排成降序,这就是单向直接选择排序,我们画个简易图理解理解:
在这里插入图片描述
   如上图就是使用单向直接选择进行排序的例子,思路很简单,那么有了思路,我们接下来就开始写代码,如下:

void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

//直接选择排序(单向)
void SelectSort1(int* arr, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int mini = begin;
		for (int i = begin; i <= end; i++)
		{
			if (arr[i] < arr[mini])
			{
				mini = i;
			}
		}
		Swap(&arr[begin], &arr[mini]);
		begin++;
	}
}

我们来使用它排序试试,如图:
在这里插入图片描述
   可以看到单向直接选择排序很好地帮我们完成了任务,接下来我们分析一下它的时间复杂度,它拥有两层用于遍历的循环,所以时间复杂度为O(N^2),虽然不是很好,但是它的思路很简单
   其中单向直接选择排序就是选最小的元素往前或往后放,一次只选一个元素,所以叫单向,那么双向选择排序是什么样子的呢?我们一起来学习一下

二、双向直接选择排序

   单向直接选择排序就是一次只选一个值出来往前或往后放,那么双向选择排序就是,一次把当前最小的和最大的元素的下标找出来,把最小的元素往前放,把最大的元素往后放
   这样我们是不是一次遍历就可以排好两个元素,而单向的一次遍历只能排好一个元素,相当于是一种优化,那么是不是双向一定比单向快呢?我们在最后的测试阶段给出答案
   由于双向直接选择排序和单向直接选择排序很相似,所以我们这里直接就根据上面的思路来写代码了,就是每次找最小值往前放,同时找最大值往后放,但是其实会有坑,但是先不管,我们先把大思路写出来,后面再分析坑在哪里,如下:

void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

//直接选择排序(双向)
void SelectSort2(int* arr, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int mini = begin, maxi = begin;
		for (int i = begin; i <= end; i++)
		{
			if (arr[i] < arr[mini])
			{
				mini = i;
			}
			if (arr[i] > arr[maxi])
			{
				maxi = i;
			}
		}
		Swap(&arr[begin], &arr[mini]);
		Swap(&arr[end], &arr[maxi]);
		begin++, end--;
	}
}

   这里的双向直接选择排序几乎和单向直接选择排序差别不大,那么是否它已经没有问题了呢?我们来看看代码的运行结果:
在这里插入图片描述
   可以看到代码出现了一些小问题,没有将数组完全排成升序,那么问题在哪里呢?这里我也不再墨迹了,直接举一个例子画图讲解,如图:
在这里插入图片描述
   根据上图我们已经发现了问题,我们来分析一下为什么会出现这种问题,根本原因就是数组中最大值就在begin的位置上,当最小值和begin位置元素进行交换时,把这个最大值交换到mini位置上去了
   然后maxi和end位置数据进行交换时,end就拿到了最小值,所以最后导致了上面的问题,根本原因就是最大值在begin的位置上,所以我们想个办法来优化一下
   由于begin和mini位置的数据总是先交换,如果begin位置就是最大值的话,经过begin和mini的交换后,这个最大值就会跑到mini的位置上去,所以我们解决的方案就是,如果begin位置就是最大值的话,让maxi = mini
   这样的话经过begin和mini的一次交换,将最大值交换到了mini的位置, 现在我们又让maxi走到mini的位置,maxi现在指向的就是最大值,可以放心交换,如图:
在这里插入图片描述
在这里插入图片描述
现在我们就解决掉这个问题了,我们改正一下我们的代码,如下:

void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

//直接选择排序(双向)
void SelectSort2(int* arr, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int mini = begin, maxi = begin;
		for (int i = begin; i <= end; i++)
		{
			if (arr[i] < arr[mini])
			{
				mini = i;
			}
			if (arr[i] > arr[maxi])
			{
				maxi = i;
			}
		}
		if (begin == maxi)
		{
			maxi = mini;
		}
		Swap(&arr[begin], &arr[mini]);
		Swap(&arr[end], &arr[maxi]);
		begin++, end--;
	}
}

接下来我们再次来测试一下代码,如图:
在这里插入图片描述
   可以看到之前的问题被我们完全解决了,这就是我们真正的双向直接选择排序,接着我们分析一下它的时间复杂度,虽然比起单向的循环少了一半,但是还是属于O(N^2)级别,也不算太优,到最后我们再将它们来进行比较,我们继续学习下面的内容

三、堆排

   堆排我在堆的应用部分就已经讲过了,如果有兴趣就去看看原理,这里直接给出源代码,如下:

//向上调整算法
void AdjustUp(int* arr, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (arr[child] > arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//向下调整算法
void AdjustDown(int* arr, int parent, int n)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && arr[child + 1] > arr[child])
		{
			child++;
		}
		if (arr[child] > arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

//堆排
void HeapSort(int* arr, int n)
{
	//向上调整建堆
	/*for (int i = 0; i < n; i++)
	{
		AdjustUp(arr, i);
	}*/
	
	//向下调整建堆
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(arr, i, n);
	}

	//建堆后进行排序
	int end = n - 1;
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDown(arr, 0, end);
		end--;
	}
}

   我们之前在堆的应用也讲过堆排的时间复杂度,这里再提一下,大致为O(Nl * logN),这个时间复杂度非常优秀,在最后我们进行对比时也可以看出来

四、直接选择排序以及堆排性能对比

   我们还是按照上一篇文章的样子来写一个专门测试排序性能的函数,它可以帮我们生成10万个随机数,如下:

void TestOP()
{
    srand((unsigned int)time(NULL));
    const int N = 100000;
    int* a1 = (int*)malloc(sizeof(int) * N);
    int* a2 = (int*)malloc(sizeof(int) * N);
    int* a3 = (int*)malloc(sizeof(int) * N);

    for (int i = 0; i < N; ++i)
    {
        a1[i] = rand();
        a2[i] = a1[i];
        a3[i] = a1[i];
    }
     int begin1 = clock();
     SelectSort1(a1, N);
     int end1 = clock();

     int begin2 = clock();
     SelectSort2(a2, N);
     int end2 = clock();

    int begin3 = clock();
    HeapSort(a3, N);
    int end3 = clock();

    printf("SelectSort1(单向):%d\n", end1 - begin1);
    printf("SelectSort2(双向):%d\n", end2 - begin2);
    printf("HeapSort:%d\n", end3 - begin3);

    free(a1);
    free(a2);
    free(a3);
}

int main()
{
    TestOP();
    return 0;
}

   注意我们在运行这段代码之前,要把模式改成Release,这样才能真正测试它们的性能,运行结果如下:

在这里插入图片描述
   可以看到,我们排10万个随机数,堆排只用了5毫秒,而单向和双向直接选择排序则直接来到了4秒左右,我们再来测试一下排序100万个随机数的时间,如图:
在这里插入图片描述
   可以发现,堆排的速度依旧非常非常快,仍然在0.1秒之内,而两个直接选择排序则是慢了非常多,已经以分钟来计数了,这就是O(N * log N)的力量
   当我们比较完堆排和直接选择排序之后,我们来比较一下单向和双向两个直接选择排序,我们惊讶地发现,单向的直接选择排序居然更快,理论上来说,双向是单向的优化,循环次数更少,为什么还会出现这种问题呢?
   大致原因应该是单向选择排序它的交换逻辑和条件判断更简单,编译器在编译代码时,就做了更多和更好的优化,导致单向比双向快,但是我们要知道理论上双向是单向的优化

   那么到这里我们选择排序就到这里介绍完毕了,如果有什么疑问欢迎私信我,我会及时作出回应,感谢支持!
   bye~

标签:mini,arr,初阶,int,begin,算法,child,排序
From: https://blog.csdn.net/hdxbufcbjuhg/article/details/144478767

相关文章

  • 负载均衡算法的原理及优缺点
    一、轮询(Round-Robin)算法原理轮询算法是最简单的负载均衡算法之一。按照顺序依次将请求分配给后端服务器列表中的每一个服务器。例如,假设有服务器A、B、C,第一个请求分配给A,第二个请求分配给B,第三个请求分配给C,然后第四个请求又回到A,如此循环。优点和缺点优点:实现简单,易......
  • Express的使用笔记9 使用bcrypt算法给用户密码加密
    先了解一下bcrypt算法,一种基于Blowfish密码学算法的密码散列函数,用于在密码存储时抵抗暴力破解攻击,通过在散列过程中加salt来提高安全性,salt是个随机生成的数据串,与密码一起被散列,使得即使两个相同的密码也会产生不同的散列值。bcrypt算法允许开发者指定工作因子(成本因子),决定散列......
  • 【数据结构】八大排序
    目录一、直接插入排序二、希尔排序三、选择排序四、堆排序五、冒泡排序六、快速排序七、归并排序八、计数排序稳定性结论稳定性:排序后相同元素之间的相对顺序是否保持不变。一、直接插入排序基本思想:通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找......
  • 街面环卫算法视频分析服务器浅析智能视频监控在智慧城市的应用与趋向
    在数字化浪潮的推动下,智慧城市的建设已成为全球范围内城市发展的重要趋势。智慧城市不仅仅是技术的集合,它更是一个系统工程,涉及到城市管理的各个方面,旨在通过高科技手段提升城市的运行效率和居民的生活质量。其中,智能视频监控技术作为智慧城市建设的关键组成部分,正逐渐渗透到城市......
  • 算法网关视频分析网关无线视频监控技术如何以智能化手段提升抗干扰与数据安全效果
    在当今这个信息化快速发展的时代,无线技术的应用已经渗透到我们生活的方方面面,尤其是在视频监控领域,无线传输技术正以其独特的优势,改变着传统的监控系统部署方式。本文将探讨无线视频监控业务的发展情况、面临的挑战以及如何通过技术手段提高系统的抗干扰能力和数据安全性。随着......
  • 人员乘坐皮带识别智慧矿山一体机:矿山达到智能化最终要求需要哪些AI算法及关键因素?
    在数字化转型的大潮中,非煤矿山行业正站在智能化升级的风口浪尖。随着人工智能、大数据、物联网等技术的飞速发展,矿山智能化已成为提升行业竞争力、保障作业安全、优化资源利用的关键路径。本文将深入探讨实现矿山智能化所需的AI算法及其应用,并分析在构建智能化矿山生态系统过程中......
  • 多源最短路Floyd算法
    多源最短路算法-Floyd使用Floyd(弗洛伊德)算法,可以以\(O(n^3)\)的时间复杂度求出一张多源图的任意两点间的最短路径一般采用邻接矩阵的方法来存储图:intg[N][N];g[i][j]其中,g[i][j]的意义为第i个节点到第j个节点的权重我们需要对邻接矩阵进行路径初始化,将自身到自身的权重......
  • 代码随想录算法训练营第四十八天|739.每日温度、496.下一个更大元素、503.下一个更大
    1leetcode739.每日温度题目链接:739.每日温度-力扣(LeetCode)文章链接:代码随想录视频链接:单调栈,你该了解的,这里都讲了!LeetCode:739.每日温度哔哩哔哩bilibili思路:就真的是暴力搜索来写这道题目,但是呢,有些示例里面就超时了,至少有点思路了吧,也算是好消息1.1自己的方法能......
  • 代码随想录算法训练营第四十六天|leetcode647. 回文子串、leetcode516.最长回文子序列
    1leetcode647.回文子串题目链接:647.回文子串-力扣(LeetCode)文章链接:代码随想录视频链接:动态规划,字符串性质决定了DP数组的定义|LeetCode:647.回文子串哔哩哔哩bilibili思路:嘿,看不懂有一点,看解析吧1.1视频后的方法其实看完视频以后,感觉这个题目真的不难,我想到了二维......
  • 代码随想录算法训练营第四十五天|leetcode115.不同的子序列、leetcode583. 两个字符串
    1leetcode115.不同的子序列题目链接:115.不同的子序列-力扣(LeetCode)文章链接:代码随想录视频链接:动态规划之子序列,为了编辑距离做铺垫|LeetCode:115.不同的子序列哔哩哔哩bilibili思路:确实看不懂题目,还是看解析吧1.1视频后的方法有一种我看了视频,也没有那么理解是为......