【初阶数据结构】冒泡排序和选择排序(用C语言实现,主要讲思维)

文章目录

  • 前言
  • [1. 冒泡排序](#1. 冒泡排序)
    • [1.1 算法思想](#1.1 算法思想)
    • [1.2 冒泡排序的代码实现](#1.2 冒泡排序的代码实现)
    • [1.3 冒泡排序算法的改进](#1.3 冒泡排序算法的改进)
  • [2. 选择排序](#2. 选择排序)
    • [2.1 算法思想](#2.1 算法思想)
    • [2.2 选择排序的代码实现](#2.2 选择排序的代码实现)
  • [3. 写排序算法的小技巧](#3. 写排序算法的小技巧)

前言

讲到排序相信大家一定对一种排序很熟悉,它的名字就叫做冒泡排序。这个排序大家在学习各种语言时,都是一道绕不去的坎。本文还会介绍另一个比较简单的排序 ------ 选择排序,以及给大家讲一下选择排序的另一种写法(但是效率没有发生大的改变)。

本章内容比较简单,主要是讲一下算法的思想,以及给大家总结一下我们在写排序算法时的一些小技巧。

1. 冒泡排序

在讲冒泡排序的算法之前,先给大家看一个动图,大家可以结合动图的演示理解我下面所讲的话语。

1.1 算法思想

这里我们先理解冒泡排序算法的单趟排序思想。这里我们主要讲的是升序排序(降序一样的道理 )。

🍉结合上面的动图,我们可以很清楚的看到,冒泡排序的核心思想就是将数组中相邻的两个数进行比较,如果左边的数比右边的数要大,那它们两个就交换数据,接着继续比较下一组相邻的数据,如果右边的数大于左边的数,那就直接进入比较下一组相邻的数据的环节。一直比较到一组相邻数据中包含了数组末尾的数据,才算是结束。

以上就是冒泡排序单趟排序的思想!

接着我们就得组合多次单趟排序形成一个完整的冒泡排序。

🍉我们可以这么想:冒泡排序单趟排序的目的是为了将待排序数组中的最大值给放到待排序数组的结尾处,紧接着就缩小待排序的区间范围。又得读者可能会问为什么要缩小待排序的区间范围?原因很简单,因为单趟排序的最大数引进放到了正确的位置上了,下一次排序可以不用动它了。这样一来,只要一开始数组有n个元素,我们执行单趟排序的次数就为(n-1)次。

好了,思想讲解完了,下面直接上代码。

1.2 冒泡排序的代码实现

c 复制代码
//交换两个数字
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

//冒泡排序 -- 第一种写法
void BubbleSort(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		for (int j = 1; j < n - i; j++)
		{
			if (a[j - 1] > a[j])
			{
				Swap(&a[j - 1], &a[j]);
			}
		}
	}
	
}
c 复制代码
//冒泡排序 -- 第二种写法
//交换两个数字
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void BubbleSort(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n - i - 1; j++)
		{
			if (a[j] > a[j + 1])
			{
				Swap(&a[j + 1], &a[j]);
			}
		}	
	}
	
}

1.3 冒泡排序算法的改进

大家如果仔细的一点的话可以发现一个问题,如果我给的数组是一个已经有序的数组,它仍会给我进行一次完整的冒泡排序,可以通过以下代码给大家测试一下:

可以看到无论我们喂给这个函数什么数组,这个排序的时间复杂度都为O( N 2 N^2 N2)。但是这跟我们的预期是不一样的,因为冒泡排序在排有序数组的时间复杂度应该为O(N)。所以我们该怎么对上面的代码进行优化呢?

优化之后的代码:

c 复制代码
//冒泡排序 -- 第二种写法
//交换两个数字
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void BubbleSort(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		int flag = 0; //设定一个标志位,又来标明该数组是否有序
		
		for (int j = 1; j < n - i; j++)
		{
			if (a[j - 1] > a[j])
			{
			    flag = 1; //说明数组时无序的
				Swap(&a[j - 1], &a[j]);
			}
		}
		
		if(flag == 0)
		{
			break;
		}
	}
	
}

可以很清楚的看到,我们的改良大获成功。


2. 选择排序

老规矩(以升序为例),先上动图:

2.1 算法思想

选择排序的单趟排序是选择待排序数组中最小的数,将它与待排序数组中的开头的位置的数交换位置。这样单趟排序就完成了。

接下来讲一下完整的选择排序,可以看到的是每当我们完成了一个选择排序算法的单趟排序是将待排序的数组中最小的数放到正确的位置上。直到最后只剩最后一个数字时,排序终止。

2.2 选择排序的代码实现

选择排序算法的第一种写法:

c 复制代码
void SelectSort(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		int mini = a[i];
		for (int j = i; j < n; j++)
		{
			if (a[mini] > a[j])
			{
				mini = j;
			}
		}
		Swap(&a[mini],&a[i]);
	}
	
}

选择排序算法的第二种写法:

c 复制代码
void SelectSort(int* a, int n)
{
	int left = 0, right = n - 1;
	int maxi = 0, mini = 0;


	while (left < right)
	{
		for (int i = left; i <= right; i++)
		{
			if (a[maxi] < a[i])
			{
				maxi = i;
			}

			if (a[mini] > a[i])
			{
				mini = i;
			}

		}

		Swap(&a[left], &a[mini]);

		if (left == maxi)
		{
			maxi = mini;
		}
		Swap(&a[right], &a[maxi]);

		left++;
		right--;
	}
	
}

上面的写法比较难以理解,如果实在理解不了的话,第一种写法也是可以的。因为这两种写法的效率是差不多的,时间复杂度都为O(N^2)。

3. 写排序算法的小技巧

🍉相信大家在看完每个排序算法的思路讲解时,会发现一个规律:从单趟排序开始分析,在逐步向全局排序进行延伸。核心就在于我们得理解单趟排序的目的是什么,这样才能为我们的全局排序做铺垫。


为此,本文的讲解就到这里了。

如果觉得本文对你有帮助的话,麻烦会给偶点个赞吧!!!

相关推荐
Coovally AI模型快速验证5 小时前
农田扫描提速37%!基于检测置信度的无人机“智能抽查”路径规划,Coovally一键加速模型落地
深度学习·算法·yolo·计算机视觉·transformer·无人机
pusue_the_sun5 小时前
数据结构:二叉树oj练习
c语言·数据结构·算法·二叉树
RaymondZhao346 小时前
【全面推导】策略梯度算法:公式、偏差方差与进化
人工智能·深度学习·算法·机器学习·chatgpt
zhangfeng11336 小时前
DBSCAN算法详解和参数优化,基于密度的空间聚类算法,特别擅长处理不规则形状的聚类和噪声数据
算法·机器学习·聚类
啊阿狸不会拉杆7 小时前
《算法导论》第 32 章 - 字符串匹配
开发语言·c++·算法
小学生的信奥之路7 小时前
洛谷P3817题解:贪心算法解决糖果分配问题
c++·算法·贪心算法
曙曙学编程8 小时前
stm32——GPIO
c语言·c++·stm32·单片机·嵌入式硬件
你知道网上冲浪吗8 小时前
【原创理论】Stochastic Coupled Dyadic System (SCDS):一个用于两性关系动力学建模的随机耦合系统框架
python·算法·数学建模·数值分析
地平线开发者9 小时前
征程 6 | PTQ 精度调优辅助代码,总有你用得上的
算法·自动驾驶
Tisfy10 小时前
LeetCode 837.新 21 点:动态规划+滑动窗口
数学·算法·leetcode·动态规划·dp·滑动窗口·概率