数据结构——冒泡、选择、插入和希尔排序

目录

引言

冒泡排序

1.算法思想

2.算法步骤

3.代码实现

4.复杂度分析

选择排序

1.算法思想

2.算法步骤

3.代码实现

(1)优化前

(2)优化后

4.复杂度分析

插入排序

1.算法思想

2.算法步骤

3.代码实现

4.复杂度分析

希尔排序

1.算法思想

2.算法步骤

3.代码实现

4.复杂度分析

结束语


引言

在数据处理、算法优化等领域中,排序是基础且关键的一环。本文将要探讨的四种排序算法:冒泡排序、选择排序、插入排序和希尔排序

求点赞收藏关注!!!

冒泡排序

1.算法思想

冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地遍历 要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复进行的,直到没有再需要交换的元素为止,这意味着数列已经排序完成。

这个算法的名字由来是因为越小(或越大)的元素会经由交换慢慢"浮"到数列的顶端(或底部)。

2.算法步骤

1.比较相邻的元素。如果第一个比第二个大(或小,根据排序顺序要求),就交换它们两个。

2.对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数(或最小的数)。

3.针对所有的元素重复以上的步骤,除了已完成排序元素。

4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较

注意:如果一轮之后,元素并没有发生任何交换,此时说明此时排序已经完成,那么我们可以提前结束循环。

我们来看个动图就能很直观的理解什么是冒泡排序:

3.代码实现

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void bubble_sort(int arr[], int sz)
{
	// 外层循环,控制排序的轮数
	for (int i = 0; i < sz - 1; i++)
	{
		// 定义一个标志位,用于判断是否在这一轮中有元素交换
		int flag = 0;
		// 内层循环,进行实际的元素比较和交换
		for (int j = 0; j < sz - i - 1; j++)
		{
			// 如果当前元素大于后一个元素,则交换它们
			if (arr[j] > arr[j + 1])
			{
				// 设置标志位为1,表示发生了交换
				flag = 1;
				Swap(&arr[j], &arr[j + 1]);
			}
		}
		// 如果这一轮没有发生任何交换
		// 说明数组已经有序,可以提前结束排序
		if (flag == 0)
		{
			break;
		}
	}
}

4.复杂度分析

时间复杂度:最坏情况是数组完全逆序,此时每一轮都需要进行 n - 1 次比较,并且每一轮都会进行至少一次交换.因此,总的比较次数和交换次数都接近 n (n - 1) / 2,其中 n 是数组的长度。所以,最坏情况下的时间复杂度是 O(n ^ 2) 。

空间复杂度:由于没有开辟额外的空间大小,因此空间复杂度为O(1)。

选择排序

1.算法思想

选择排序(Selection Sort)是一种简单直观的排序算法。通过不断选择剩余元素之中的最小(或最大)元素,然后与起始位置的元素交换(起始位置在每一次选择后都向后移动一位),直到整个序列排序完成。

2.算法步骤

1.**在未排序序列中找到最小(大)元素。**遍历未排序的数组,找到最小(或最大)的元素。

**2.存放到排序序列的起始位置。**将找到的最小(或最大)元素与未排序序列的第一个元素交换位置(如果第一个元素就是最小(大)元素,则它自己和自己交换)。

3.从剩余未排序元素中继续寻找:在剩下的未排序元素中继续执行步骤1和步骤2,直到所有元素都被排序。

看动图直观的感受一下:

3.代码实现

(1)优化前
void SelectSort(int* arr, int len)
{
	for (int i = 0; i < len - 1; i++)
	{
        // 假设当前位置i的元素是最小的,记录其索引为mini
		int mini = i;
		for (int j = i + 1; j < len; j++)
		{
            // 如果发现更小的元素,则更新mini为当前更小元素的索引
			if (arr[j] < arr[mini])
			{
				mini = j;
			}
		}
		swap(&arr[mini], &arr[i]);
	}
}
(2)优化后

我们可以对上面的代码进行点优化,我们可以同时选择最大与最小的元素,同时往起始与结尾位置交换。

注意: 同时交换可能会改变原先最大或者最小元素的位置。因此我们需要进行判断。

代码如下:

//交换两个数据
void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

void SelectSort(int* arr, int n)
{
	int begin = 0;		// 未排序部分的起始索引
	int end = n - 1;	// 未排序部分的结束索引
	while (begin < end)
	{
		int maxi = begin;
		int mini = begin;
		// 遍历未排序部分,找到最小值和最大值
		for (int i = begin + 1; i <= end; i++)
		{
			if (arr[i] < arr[mini])
			{
				mini = i;	// 更新最小值的位置
			}
			if (arr[i] > arr[maxi])
			{
				maxi = i;	// 更新最大值的位置
			}
		}
		// 将当前范围的最小值交换到未排序部分的开始位置
		Swap(&arr[begin], &arr[mini]);

		// 如果begin与maxi重合,则更新maxi
		if (maxi == begin)
		{
			maxi = mini;
		}
		// 将当前范围的最大值交换到未排序部分的结束位置
		Swap(&arr[end], &arr[maxi]);

		// 缩小未排序部分的范围
		++begin;
		--end;
	}
}

4.复杂度分析

时间复杂度:由于每次外层循环中的内层循环需要遍历几乎所有未排序的元素,因此时间复杂度为O(n^2)。

空间复杂度:由于没有开辟额外的空间大小,因此空间复杂度为O(1)。

插入排序

1.算法思想

插入排序(Insertion Sort)是一种简单直观的排序算法。它模拟了我们日常生活中整理扑克牌或排序书籍的过程。其基本思想是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、元素个数加一的有序数据,直到全部待排序的数据元素插完,排序完成。

2.算法步骤

1.将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。

2.遍历未排序部分,将扫描到的每个元素插入有序序列的适当位置。

3.依次重复1,2步骤,直至插入完成。

动图演示如下:

3.代码实现

void InsertSort(int* arr, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		// tmp 存储当前需要插入的元素的值
		int tmp = arr[end + 1];
		// 内层循环,将比 tmp 大的元素向后移动一位
		// 为 tmp 找到正确的插入位置
		while (end >= 0)
		{
			if (arr[end] > tmp)
			{
				arr[end + 1] = arr[end];
				end--;
			}
			else
			{
				break;
			}
		}
		// 将 tmp 插入到找到的正确位置
		arr[end + 1] = tmp;
	}
}

4.复杂度分析

时间复杂度:当处于最坏情况时,由于每次插入都会移动数据,因此时间复杂度为O(n^2)。

空间复杂度:由于没有开辟额外的空间大小,因此空间复杂度为O(1)。

希尔排序

1.算法思想

希尔排序(Shell Sort)是插入排序的一种更高效的改进版本,也称为缩小增量排序。基本思想是将待排序的数组元素按照某种增量(gap)进行分组,对每组使用插入排序算法进行排序。随着增量的逐渐减少,每组包含的元素越来越多,当增量减至1时,整个数组被视为一组进行最后的插入排序,从而完成排序过程。

2.算法步骤

1.选择一个增量 gap ,对数据进行分组,每间隔gap个元素分为一组,一共gap组。

2.以gap为基准单位,对其进行插入排序。

3.逐渐缩小gap的范围,直至gap为1,相当于进行一次正常的插入排序。

动图演示所下所示:

3.代码实现

void ShellSort(int* arr, int n)
{
	// 初始化增量gap为数组长度n,用于分组
	int gap = n;
	while (gap > 1)
	{
		// 逐渐减少增量
		gap = gap / 3 + 1;
		// 对每个分组进行插入排序
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = arr[end + gap];
			while (end >= 0)
			{
				// 如果当前位置的元素大于tmp
				// 则将当前位置的元素向后移动gap位
				if (arr[end] > tmp)
				{
					arr[end + gap] = arr[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			// 将tmp插入到找到的正确位置
			arr[end + gap] = tmp;
		}
	}
}

4.复杂度分析

时间复杂度:希尔排序的时间复杂度一般较为难计算,通过大量测验一般认为其时间复杂为O(N^1.3)。

空间复杂度:由于没有开辟额外的空间大小,因此空间复杂度为O(1)。

结束语

本篇博客是数据结构------排序 的第一篇。

感谢各位大佬能阅读本文。

求点赞收藏关注!!!十分感谢!!!

相关推荐
迷迭所归处4 分钟前
动态规划 —— 子数组系列-单词拆分
算法·动态规划
爱吃烤鸡翅的酸菜鱼4 分钟前
Java算法OJ(8)随机选择算法
java·数据结构·算法·排序算法
寻找码源1 小时前
【头歌实训:利用kmp算法求子串在主串中不重叠出现的次数】
c语言·数据结构·算法·字符串·kmp
Matlab精灵1 小时前
Matlab科研绘图:自定义内置多款配色函数
算法·matlab
诚丞成1 小时前
滑动窗口篇——如行云流水般的高效解法与智能之道(1)
算法
手握风云-1 小时前
数据结构(Java版)第二期:包装类和泛型
java·开发语言·数据结构
带多刺的玫瑰2 小时前
Leecode刷题C语言之统计不是特殊数字的数字数量
java·c语言·算法
爱敲代码的憨仔2 小时前
《线性代数的本质》
线性代数·算法·决策树
熬夜学编程的小王3 小时前
【C++篇】深度解析 C++ List 容器:底层设计与实现揭秘
开发语言·数据结构·c++·stl·list
yigan_Eins3 小时前
【数论】莫比乌斯函数及其反演
c++·经验分享·算法