数据结构-排序的概念、应用及其算法实现1(直接插入排序、希尔排序、选择排序、堆排序、冒泡排序)

news2024/11/24 4:23:30

本篇文章主要讲解直接插入排序、希尔排序、选择排序、堆排序、冒泡排序算法实现以及时间复杂度,稳定性分析。将在接下来的文章讲解快速排序、归并排序和计数排序。

本文全部代码在文章最后

目录

一、常见的排序算法

a.排序实现的接口

1.1插入排序

1.1.1基本思想:

1.2直接插入排序:

1.2.1直接插入排序的特性总结:

1.2.2代码讲解与实现:

1.3希尔排序( 缩小增量排序 )

1.3.1基本思想:

1.3.2希尔排序的特性总结:

1.3.3代码讲解与实现

2.1选择排序

2.2基本思想:

2.3.1直接选择排序:

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

2.2.4代码讲解以及实现

2.3.1堆排序

3.1交换排序

3.1.1冒泡排序

3.1.2代码

二、完整代码

Sort.h

Sort.c

Test.c



这是用于测试排序性能对比的函数,便于我们更加直观观察每个排序的性能差异

void TestOP()
{
srand(time(0));
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);
int* a4 = (int*)malloc(sizeof(int)*N);
int* a5 = (int*)malloc(sizeof(int)*N);
int* a6 = (int*)malloc(sizeof(int)*N);
for (int i = 0; i < N; ++i)
{
a1[i] = rand();
a2[i] = a1[i];
a3[i] = a1[i];
a4[i] = a1[i];
a5[i] = a1[i];
a6[i] = a1[i];
}
int begin1 = clock();
InsertSort(a1, N);
int end1 = clock();
int begin2 = clock();
ShellSort(a2, N);
int end2 = clock();
int begin3 = clock();
SelectSort(a3, N);
int end3 = clock();
int begin4 = clock();
HeapSort(a4, N);
int end4 = clock();
int begin5 = clock();
QuickSort(a5, 0, N-1);
int end5 = clock();
int begin6 = clock();
MergeSort(a6, N);
int end6 = clock();
printf("InsertSort:%d\n", end1 - begin1);
printf("ShellSort:%d\n", end2 - begin2);
printf("SelectSort:%d\n", end3 - begin3);
printf("HeapSort:%d\n", end4 - begin4);
printf("QuickSort:%d\n", end5 - begin5);
printf("MergeSort:%d\n", end6 - begin6);
free(a1);
free(a2);
free(a3);
free(a4);
free(a5);
free(a6);
}

一、常见的排序算法

a.排序实现的接口

// 排序实现的接口
// 插入排序
void InsertSort(int* a, int n);
// 希尔排序
void ShellSort(int* a, int n);
// 选择排序
void SelectSort(int* a, int n);
// 堆排序
void AdjustDwon(int* a, int n, int root);
void HeapSort(int* a, int n);
// 冒泡排序
void BubbleSort(int* a, int n)

1.1插入排序

1.1.1基本思想:

直接插入排序是一种简单的插入排序法,其基本思想是:

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。(例如,玩扑克牌)


1.2直接插入排序:

当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移。


1.2.1直接插入排序的特性总结:

1. 元素集合越接近有序,直接插入排序算法的时间效率越高
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1),它是一种稳定的排序算法
4. 稳定性:稳定

1.2.2代码讲解与实现:

void InsertSort(int* a, int n)//升序
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}
}

1.3希尔排序( 缩小增量排序 )

1.3.1基本思想:

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。

1.3.2希尔排序的特性总结:

1. 希尔排序是对直接插入排序的优化。
2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,
因此在好些树中给出的希尔排序的时间复杂度都不固定:
《数据结构(C语言版)》--- 严蔚敏:

《数据结构-用面相对象方法与C++描述》--- 殷人昆:

我们一般参照Knuth所提出的方式取值。

1.3.3代码讲解与实现

预排序:

gap越大,大的值可以更快调到后面,小的值可以更快调在前面,越不接近有序

gap越小,跳的越慢,但是越接近有序,如果gap==1就是直接插入排序

如图:

但是我们更喜欢多组并排:

void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}

			a[end + gap] = tmp;
		}
		
	}
}

其实我们可以明显观察到,希尔排序的底层思想是在直接插入排序上加入了gap,让每次的排序先排一部分,再排一部分,直到gap减小为1时,就与直接排序一样,但此时数组中的顺序已经几乎有序。

2.1选择排序

2.2基本思想:

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


2.3直接选择排序:

在元素集合array[i]--array[n-1]中选择关键码最大(小)的数据元素

若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换

在剩余的array[i]--array[n-2](array[i+1]--array[n-1])集合中,重复上述步骤,直到集合剩余1个元素

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

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

2.3.3代码讲解以及实现

我们使用两个变量一个作为小值下标,一个作为大值下标,在每一次找到最小值后就将小值与a[begin]交换将该值防御数组开头,找到最大值后就将改值与数组末尾值交换,将最大值存入数组最后,再++begin,--end,新的较小较大值依次进行排序。但需要注意当maxi 的值与begin相同时,因Swap(&a[begin], &a[mini]);导致maxi值被覆盖,要进行还原maxi = mini;

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

2.4堆排序

详细讲解与实现见博客堆排序的详解

2.4.1堆排序的特性总结

1. 堆排序使用堆来选数,效率就高了很多。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(1)
4. 稳定性:不稳定

3.1交换排序

3.1.1基本思想:

所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

3.2冒泡排序

3.2.1冒泡排序的特性总结:

1. 冒泡排序是一种非常容易理解的排序
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:稳定

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

交换排序的快速排序以及剩下的排序将在下篇文章详细讲解

二、完整代码

Sort.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
#include<time.h>

void PrintArray(int* a, int n);
void InsertSort(int* a, int n);
void BubbleSort(int* a, int n);
void ShellSort(int* a, int n);
void SelectSort(int* a, int n);

void HeapSort(int* a, int n);

Sort.c

#include"Stact.h"
void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}
void InsertSort(int* a, int n)//升序
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}
}
void Swap(int* t1,int* t2)
{
	int tmp = *t1;
	*t1 = *t2;
	*t2 = tmp;
}
void BubbleSort(int* a, int n)
{
	for (int j = 0; j < n; j++)
	{
		for (int i = 1; i < n-j; i++)
		{
			if (a[i - 1] > a[i])
			{
				Swap(&a[i - 1], &a[i]);
			}
		}
	}
}

void ShellSort0(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int j = 0; j < gap; ++j)
		{
			for (int i = j; i < n - gap; i += gap)
			{
				int end = i;
				int tmp = a[end + gap];
				while (end >= 0)
				{
					if (tmp < a[end])
					{
						a[end + gap] = a[end];
						end -= gap;
					}
					else
					{
						break;
					}
				}
				a[end + gap] = tmp;
			}

		}
	}
}


void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
		
	}
}


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

	}
}

void AdjustDown(int* a, int size, int parent)
{
	int child = parent * 2 + 1;

	while (child < size)
	{
		if (child + 1 < size && a[child + 1] > a[child])
		{
			++child;
		}

		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

// 升序
void HeapSort(int* a, int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}

	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}
}
int Getmid(int* a, int begin, int end)
{
	int midi = (begin + end) / 2;
	if (a[begin] > a[end])
	{
		if (a[end] > a[midi])
		{
			return end;
		}
		else if(a[begin]<a[midi])
		{
			return begin;
		}
		else
		{
			return midi;
		}
	}
	else
	{
		if (a[begin] < a[midi])
		{
			return begin;
		}
		else if (a[end] > a[midi])
		{
			return end;
		}
		else
		{
			return midi;
		}
	}
}

Test.c

#include"Sort.h"

void TestInsertSort()
{
	int a[] = { 3, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };
	InsertSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}
//
void TestBubbleSort()
{
	int a[] = { 3, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };
	BubbleSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}

void TestShellSort()
{
	int a[] = { 3, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };
	ShellSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}
void TestSelectSort()
{
	//int a[] = { 3, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };
	int a[] = { 13, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };

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

void TestHeapSort()
{
	//int a[] = { 3, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };
	int a[] = { 13, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };

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

void TestOP()
{
	srand(time(0));
	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);
	int* a4 = (int*)malloc(sizeof(int) * N);

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

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

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

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

	int begin4 = clock();
	HeapSort(a4, N);
	int end4 = clock();


	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);
	printf("SelectSort:%d\n", end3 - begin3);
	printf("HeapSort:%d\n", end4 - begin4);


	free(a1);
	free(a2);
	free(a3);
	free(a4);
}
int main()
{
	TestInsertSort();
	TestBubbleSort();
	TestShellSort();
	TestSelectSort();
	TestHeapSort();


	//TestOP();

	return 0;
}

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

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

相关文章

【C++】BFS解决边权唯一的最短路径问题

目录 介绍 迷宫中离入口最近的出口 算法思路 代码实现 最小基因变化 算法思路 代码实现 单词接龙 算法思路 代码实现 为高尔夫比赛砍树 算法思路 代码实现 介绍 最短路问题是图论中非常经典的一种问题,其实就是通过代码找到两点之间的最优路径(往往是距离最短),最…

用MobaXterm,TightVNC和secure SSH实现两台windows电脑之间的连接和通信

今天给大家分享一个非常有趣的技术&#xff0c;那便是如何使用MobaXterm来实现两台电脑之间的通信。实验成功&#xff0c;保证能跑。 首先&#xff0c;给大家介绍我们今天最重要的工具&#xff1a;那便是MobaXterm&#xff08;以下由ChatGPT生成&#xff09;&#xff1a; Moba…

C++初阶_2: inline内联函数 宏函数

C推出了inline关键字&#xff0c;其目的是为了替代C语言中的宏函数。 我们先来回顾宏函数&#xff1a; 宏函数 现有个需求&#xff1a;要求你写一个Add(x,y)的宏函数。 正确的写法有一种&#xff0c;错误的写法倒是五花八门&#xff0c;我们先来“见不贤而自省也。” // …

我怎么会这么依赖 GUI?

AWS CLI、.NET 和 Lambda 函数 欢迎来到雲闪世界。在 Windows 上使用 Visual Studio 和 AWS Explorer 绝对会让你变得懒惰。我的意思是&#xff0c;能够通过右键单击项目来构建和部署 Lambda 函数之类的东西真是太棒了&#xff0c;但有时最好了解幕后发生了什么。 尽管如此&am…

Java - 异常

异常处理&#xff1a; ①捕获异常 选中代码后ctrlAltt: try catch捕捉异常 ②抛出异常 加上throws 异常类型 自定义异常 Exception.java: package Exception;//自定义运行时异常 public class ExceptionTest {public static void main(String[] args) {//保存一个合法的年…

知识库管理软件购买指南:2024年十大选择

本篇文章介绍了以下工具&#xff1a;PingCode、Worktile、蓝湖、语雀、幕布、Guru、Helpjuice、Stack Overflow for Teams、KnowledgeOwl、eXo Platform。 在企业中&#xff0c;信息分散、难以获取是个常见的痛点。无论是新员工入职、团队协作&#xff0c;还是项目管理&#xf…

电脑高手必备!这款数据恢复神器你值得拥有

哎呀&#xff0c;现在我们天天在用的手机、电脑和各种移动设备都是有很多日常和工作中的重要数据&#xff0c;丢失重要的文件和数据的时候就很着急&#xff0c;所以我也很感同身受&#xff0c;为此困扰过我好几次&#xff0c;所以今天特地借此文章整理了常用的失易得数据恢复软…

Unity游戏开发002

Unity游戏开发002 目录 第一章&#xff1a;Hello&#xff0c;Unity&#xff01;第二章&#xff1a;创建一个游戏体 本文目录 Unity游戏开发 Unity游戏开发002目录本文目录前言一、创建一个游戏体1. 编辑器语言设置2. 创建游戏对象的两种方法3. 快速复制和粘贴物体4. 注意事项…

/springmvc/xxx.html和/springmvc/xxx的区别

起因&#xff1a; 今天在访问webapp目录下的html文件时&#xff0c;突然报了500错误&#xff08;如下图&#xff09;&#xff0c;让我检查视图解析器&#xff0c;我寻思我访问的是静态资源&#xff0c;不是直接交给servlet处理嘛&#xff1f;需要什么视图解析器&#xff1f;&a…

13.C基础_预处理

预处理语句就是以#开头的语句。这些语句类型如下&#xff1a; #include&#xff1a;包含头文件#define&#xff1a;宏定义#undef&#xff1a;取消宏定义#ifdef&#xff0c;#endif&#xff1a;成对使用&#xff0c;判断是否定义了某个宏 宏定义 宏定义的本质就是原样替换&…

【Material-UI】Floating Action Button (FAB) 详解:动画效果 (Animation)

文章目录 一、FAB 按钮的动画概述1. 默认动画效果2. 多屏幕横向切换时的动画 二、FAB 动画效果的实现1. 代码示例&#xff1a;跨标签页的 FAB 动画2. 代码解析3. 多个 FAB 的切换 三、动画效果的最佳实践四、总结 在现代网页设计中&#xff0c;动画不仅提升了用户界面的动态感&…

React+AntDesign做一个日历,展示节假日,节气,并且在某几个时间上添加活动备注

直接贴效果图😄 首先日历是用的AntDesign提供的Calendar组件,这个组件还是蛮强大的,可以自定义头部时间下拉;渲染每个时间段,或者重置时间段内容,玩的空间是很大的 直接贴代码,结尾最后我会将开发中遇到的问题贴出来解答一下 第一步:下载js-calendar-converter添加…

SpringBoot集成日志框架

SpringBoot集成日志框架 Java生态体系日志框架介绍 简介 在Java生态体系中&#xff0c;围绕着日志&#xff0c;有很多成熟的解决方案。关于日志输出&#xff0c;主要有两类工具。 一类是日志框架&#xff08;Log4j、Logback&#xff09;&#xff0c;主要用来进行日志的输出的…

Unity 使用 NewtonSoft Json插件报错

JsonReaderException: Unexpected character encountered while parsing value: . Path , line 0, position 0. 通过断点发现&#xff0c;头有一串ZWNBSP&#xff0c;这个是BOM格式的JSON。在文件下看不到。 解决方法&#xff1a;改编码格式&#xff0c;Remove BOM.

Linux信号的概念信号的产生

前言 我们前面已经对进程已做了介绍&#xff01;知道进程具有独立性&#xff0c;但在运行起来后可能会"放飞自我"&#xff0c;即不受控制的执行&#xff0c;这就会导致系统崩溃等问题&#xff0c;非常不利于管理。因此OS需要一种机制来协调和控制进程的运行&#xf…

【C++】拓扑排序(BFS)

目录 拓扑排序介绍 有向无环图 如何解决这类问题 课程表 算法思路 代码实现 课程表2 算法思路 代码实现 火星词典 代码实现 拓扑排序介绍 有向无环图 入度&#xff1a;指向活动节点的箭头个数&#xff1b; 出度&#xff1a;从活动节点出去指向别的节点的箭头个数。…

交互式实时距离测量-单目测距-社交距离检测

使用说明 使用鼠标点击两个目标框要删除在距离计算过程中绘制的点&#xff0c;你可以使用鼠标右键点击。这会清除所有已绘制的点 使用 Ultralytics YOLOv8 进行距离计算 距离计算是在指定空间内测量两个物体之间间隙的基本概念。在 Ultralytics YOLOv8 的情况下&#xff0c;通…

React学习-初始化react项目

目标: reactv18&#xff1a;->1.核心的22中api2路由3.数据状态管理&#xff1a;redux项目&#xff1a; 1.b端业务闭环:登录方案、权限设计、用户管理方案、业务功能、系统架构设计、路由设计流程闭环&#xff1a;开发环境、生产环境、测试环境、代码规范、分支管理规范、项…

SpringBoot整合knife4j配置使用直接拷贝即可(快速入门超详细版)

1. SpringBoor整合Knife4j添加maven 1.1 第一种maven <!--添加Knife4j依赖--><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>4.5.0</ver…

Unity新输入系统 之 PlayerInput(真正的最后封装部分)

本文仅作笔记学习和分享&#xff0c;不用做任何商业用途 本文包括但不限于unity官方手册&#xff0c;unity唐老狮等教程知识&#xff0c;如有不足还请斧正​ 首先你应该了解新输入系统的基本单位和输入配置文件 Unity新输入系统 之 InputAction&#xff08;输入配置文件最基本的…