【初阶数据结构】冒泡排序和选择排序(用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. 写排序算法的小技巧

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


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

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

相关推荐
xiaoshiguang33 小时前
LeetCode:222.完全二叉树节点的数量
算法·leetcode
爱吃西瓜的小菜鸡3 小时前
【C语言】判断回文
c语言·学习·算法
别NULL3 小时前
机试题——疯长的草
数据结构·c++·算法
TT哇4 小时前
*【每日一题 提高题】[蓝桥杯 2022 国 A] 选素数
java·算法·蓝桥杯
ZSYP-S5 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos5 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习5 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA5 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
chengooooooo5 小时前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
jackiendsc5 小时前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法