数据结构与算法之C语言版排序算法汇总

一、总述

本文是基于我对数据结构与算法的学习后,针对书中提及到的各类排序算法进行汇总,并通过C语言以代码的形式来对排序算法进行总结

二、排序算法

(一)什么是排序算法

排序算法是一种用于将一组数据按照特定顺序进行排列的算法。排序算法通常根据元素之间的大小关系来确定它们在最终排序结果中的位置。

常见的排序算法包括:

  1. 冒泡排序(Bubble Sort):比较相邻元素的大小,若逆序则交换它们的位置。重复这个过程直到整个数组有序。

  2. 插入排序(Insertion Sort):将数组分为已排序和未排序两部分,每次从未排序部分取出一个元素,在已排序部分找到合适的位置插入它。

  3. 选择排序(Selection Sort):每次从未排序部分选取最小(或最大)的元素,与已排序部分的末尾交换位置。重复这个过程直到整个数组有序。

  4. 快速排序(Quick Sort):通过选择一个基准元素,将数组划分为左右两部分,并递归地对左右部分进行快速排序。

  5. 归并排序(Merge Sort):将数组不断二分,直到无法再分,然后将子数组按序合并,最终得到有序数组。

  6. 堆排序(Heap Sort):利用堆这种数据结构进行排序,首先构建一个最大堆,然后依次将最大元素移到数组末尾。

  7. 希尔排序(Shell Sort):将数组按一定间隔进行分组,对每个分组进行插入排序,然后减小间隔再进行分组排序,最终整个数组基本有序。

不同的排序算法在时间复杂度、空间复杂度和稳定性等方面有所差异。选择适合问题规模和数据特点的排序算法可以提高排序效率。

(二)、C语言实现的排序算法

1、交换排序

冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地比较相邻两个元素的大小,并根据需要交换它们的位置,直到整个数组有序为止。冒泡排序的思想类似于水泡在水中上浮的过程,较大的元素会逐渐向右(或向左)移动到正确的位置。

具体步骤如下:

  1. 从数组的第一个元素开始,依次比较相邻的两个元素的大小,如果它们顺序不正确,则交换它们的位置。

  2. 经过一轮比较后,最大的元素将被放置在数组的最后一个位置。然后再对剩余的 n-1 个元素进行相同的操作,直到整个数组有序。

  3. 这个过程类似于气泡逐渐冒泡到水面的过程,所以称之为冒泡排序。

 (1)普通冒泡排序(交换排序)1
/*------------------------------
编辑作者:小小扫地僧
编辑日期:2023年4月24日
函数功能:冒泡排序法
--------------------------------*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define N 10                                //宏定义排序的数目

/*------------------------------
函数功能:主函数
--------------------------------*/
int main()
{
	int i;
	int j;
	int a[N];
	int t;

	printf("请输入%d个数字作为排序前的数字:",N);
	for (i = 0; i < N; i++)
	{
		scanf("%d", &a[i]);
	}
	printf("\n");

	for (i = 0; i < (N - 1); i ++)             //共需要9轮比较
	{
		for (j = 0; j < (9 - i); j ++)         //第i轮共需要9-i次比较
		{
			if (a[j] > a[j + 1])               //如果前一个数比后一个数大,则对调两个元素
			{
				t = a[j];
				a[j] = a[j + 1];               //数组元素交换
				a[j + 1] = t;
			}
		}
	}

	printf("\n排序后的数组为:");
	for (i = 0; i < N; i ++)
	{
		printf("%d", a[i]);
	}
	printf("\n");

	system("pause");
	return 0;
}
(2)常用冒泡排序
/*----------------------------
编辑作者:小小扫地僧
编辑日期:2023年4月25日
函数功能:冒泡排序的改进1
改进思路:在有些情况下,算法执行若干次后,可能已经是有序序列了,
但是上面的冒泡算法已经执行后面的比较,知道执行完n-1趟排序,这样
显然不是最佳的方式。这时候我们可以设置一个标记,用来判断一趟排序
过后有没有发生交换,如果没有发生交换,则说明数组已经有序了,已经
不需要再进行余下的比较
------------------------------*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

/*-----------------------------
函数功能:冒泡排序的改进
参数:两个入口参数,*N表示待排序的数组,n表示数组的大小
-------------------------------*/
void sort(int* N, int n) 
{
	int i;
	int j;
	int k;
	int l;
	
	for (i = n - 1; i > 0; i --)               //外循环控制趟数,一共需要n-1趟
	{		
		l = 0;                                 //l初始化为0,用于记录每一趟排序的交换次数,
		                                       //如果一趟排序过后交换次数依然是0,则表示数组已经是有序的了                              
		for (j = 0; j < i; j ++)               //内循环控制每一趟比较的次数
		{		
			if (N[j] > N[j + 1])               //相邻两个数进行比较,如果前一个数较大则进行交换
			{		
				l ++;                          //如果发生了交换,l自增
				k = N[j];
				N[j] = N[j + 1];
				N[j + 1] = k;
			}
		}
		if (l != 0)
		{
			printf("第%d趟排序,发生了%d次交换\n", n - i, l);
		}
		else 
		{
			printf("第%d趟排序,没有发生交换,数组已经有序\n", n - i);
			break;
		}
	}
}

/*-------------------------------
函数功能:主函数
--------------------------------*/
int main(int argc, char **argv)
{
	int N[6] = { 1,2,3,4,5,6 };
	int n = 6;
	int i;
	sort(N, n);
	
	for (i = 0; i < n; i ++) 
	{
		printf("%d", N[i]);
		if (i != n - 1)
			printf(",");
	}

	system("pause");
	return 0;
}
(3)冒泡排序优化版本
/*-------------------------
编辑作者:小小扫地僧
编辑日期:2023年4月25日
函数功能:冒泡排序的优化算法
---------------------------*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define W 10

/*-------------------------
*函数功能:对数组中的数据使用优化后的冒泡排序算法排序
*优化思想:在某些情况下,如果进行了若干次排序之后,后
面的若干个数已经是有序的,那么下一趟排序就只需要比较
前面无序的那一段就可以了。所以我们可以设置一个标记用
来记录每趟排序最后一个发生交换的位置,下一趟排序值比
较到此位置即可。
---------------------------*/
void sort(int* N, int n) 
{
	int i;
	int j;
	int k;
	int flag;

	flag = n - 1;                 // 初始化标记为-1
	while (flag > 0)
	{
		i = flag;
		flag = 0;
		for (j = 0; j < i; j++) 
		{
			if (N[j] > N[j + 1]) 
			{
				k = N[j];
				N[j] = N[j + 1];
				N[j + 1] = k;
				flag = j;
			}
		}
		printf("最后一次发生交换的位置为%d\n", flag);
	}
}

/*--------------------------
函数功能:主函数
----------------------------*/
int main(int argc, char **argv)
{
	int N[W];
	int n;
	int i;

	printf("请输入%d个待排的数据:",W);
	for (n = 0; n < W; n++)
	{
		scanf("%d",&N[n]);
	}

	sort(N, W);
	for (i = 0; i < W; i ++)
	{
		printf("%d", N[i]);
		if (i != W - 1) printf(",");
	}

	system("pause");
	return 0;
}
(4)快速排序法

快速排序(Quick Sort)是一种高效的排序算法,它采用了分治的策略。快速排序的基本思想是选择一个基准元素,将数组划分为左右两部分,使得左边的元素都比基准元素小,右边的元素都比基准元素大,然后递归地对左右两部分进行快速排序。

具体步骤如下:

  1. 选择一个基准元素(通常是数组的第一个或最后一个元素)。

  2. 定义两个指针,一个指向数组的开始位置,称为左指针,另一个指向数组的末尾位置,称为右指针。

  3. 左指针往右移动,直到遇到比基准元素大的元素;右指针往左移动,直到遇到比基准元素小的元素。

  4. 如果左指针小于等于右指针,则交换左指针和右指针所指向的元素,然后继续移动指针。

  5. 重复步骤3和步骤4,直到左指针大于右指针。

  6. 分别对基准元素左边和右边的子数组进行递归调用快速排序。

  7. 当子数组的长度为1或0时,表示已经有序,递归过程结束。

快速排序的时间复杂度为平均情况下 O(nlogn),最坏情况下(当每次选取的基准元素都是当前数组的最大或最小值)为 O(n^2)。虽然快速排序的最坏情况下时间复杂度较高,但由于其在平均情况下具有较好的性能,且实现简单,常被用作默认的排序算法。

/*---------------------------
编辑作者:小小扫地僧
编辑日期:2023年4月25日
函数功能:快速排序法
-----------------------------*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>


#define SIZE 6

//快速排序
void quick_sort(int num[], int low, int high)
{
	int i, j, temp;
	int tmp;

	i = low;
	j = high;
	tmp = num[low];   //任命为中间分界线,左边比他小,右边比他大,通常第一个元素是基准数

	if (i > j)  //如果下标i大于下标j,函数结束运行
	{
		return;
	}

	while (i != j)
	{
		while (num[j] >= tmp && j > i)
		{
			j--;
		}

		while (num[i] <= tmp && j > i)
		{
			i++;
		}

		if (j > i)
		{
			temp = num[j];
			num[j] = num[i];
			num[i] = temp;
		}
	}

	num[low] = num[i];
	num[i] = tmp;

	quick_sort(num, low, i - 1);
	quick_sort(num, i + 1, high);
}

int main(int argc, char **argv)
{
	//创建一个数组
	int num[SIZE] = { 0 };
	int i;

	//输入数字
	for (i = 0; i < SIZE; i++)
	{
		scanf("%d", &num[i]);
	}

	quick_sort(num, 0, SIZE - 1);

	for (i = 0; i < SIZE; i++)
	{
		printf(" %d ", num[i]);
	}

	return 0;
}

 (5)选择排序

选择排序(Selection Sort)是一种简单直观的排序算法。它通过不断地选择未排序部分中的最小(或最大)元素,将其与未排序部分的第一个元素交换位置,从而逐步构建有序数组。

具体步骤如下:

  1. 找到数组中最小(或最大)的元素,并将其与数组的第一个元素交换位置,此时第一个元素已经排好序。

  2. 在剩余的未排序部分中,继续找到最小(或最大)的元素,并将其与未排序部分的第一个元素交换位置,此时前两个元素已经排好序。

  3. 重复上述步骤,每次确定一个最小(或最大)元素并放置在已排序部分的末尾,直到整个数组有序。

选择排序的特点是每次找到最小(或最大)元素需要遍历剩余未排序部分,因此它的时间复杂度始终为 O(n^2),其中 n 是待排序数组的长度。

尽管选择排序的时间复杂度较高,但由于其简单易实现的特点,在某些特定情况下可以是一个合理的选择。然而,在大规模数据集上,更高效的排序算法通常更加适用。

 

/*-----------------------------
编辑作者:小小扫地僧
编辑日期:2023年4月25日
函数功能:选择排序法的应用
-------------------------------*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define N 10

/*-----------------------------
函数功能:输出数组
参数:两个参数,其中n表示数组的大小
-------------------------------*/
void arr_out(int a[],int n)
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

/*---------------------------
函数功能:选择排序
-----------------------------*/
void arr_sort(int *p, int n)
{
	int i, j;
	int min = 0;
	for (i = 0; i < n - 1; i++)           //排序次数
	{
		min = i;
		for (j = i + 1; j < n; j++)
		{
			if (p[j] < p[min])
			{
				min = j;                  //记录交换的元素下标值
			}
		}
		if (i != min)
		{
			int temp = p[i];
			p[i] = p[min];
			p[min] = temp;
		}
	}
}

/*---------------------------
函数功能:主函数
-----------------------------*/
int main()
{
	int a[N] = { 0 };
	int i = 0;

	printf("请输入%d个待排数据:", N);
	for (i = 0; i < N; i++)
	{
		scanf("%d", &a[i]);
	}
	arr_sort(a, N);                     //排序函数
	arr_out(a,N);                       //输出函数

	system("pause");
	return 0;
}

 2、插入排序

(1)、直接插入排序

直接插入排序(Insertion Sort)是一种简单的排序算法。它的基本思想是将待排序的元素逐个插入到已排序序列的合适位置,从而逐步构建有序序列。

具体步骤如下:

  1. 将数组分为已排序和未排序两部分,初始状态下已排序部分只包含第一个元素,即将第一个元素视为已排序序列。

  2. 从未排序部分依次取出一个元素,在已排序部分中找到合适的位置插入该元素。

  3. 比较当前要插入的元素与已排序部分的元素,如果当前元素小于某个已排序元素,则将该已排序元素后移一位,继续比较下一个已排序元素。

  4. 当找到插入位置时,将当前元素插入到该位置。

  5. 重复步骤2到步骤4,直到未排序部分的所有元素都被插入到已排序部分。

直接插入排序的时间复杂度为 O(n^2),其中 n 是待排序数组的长度。在数组基本有序或规模较小的情况下,直接插入排序有较好的性能。并且该算法是稳定的,即相等元素的相对顺序在排序前后保持不变。

然而,对于大规模乱序的数组,直接插入排序的效率较低,因此在实际应用中,往往使用更高效的排序算法。

/*------------------------
函数功能:插入排序
编辑日期:2023年4月23日
编辑人  :小小扫地僧
--------------------------*/

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>
#define N 10

/*------------------------
函数功能:打印出数组内的元素
参数    :n表示数组的大小
--------------------------*/
void print(int data[], int n)
{
	int i;
	for (i = 0; i < n; i ++)
	{
		printf("%d\n", data[i]);
	}
	printf("\n");
}

/*--------------------------
函数功能 :对数组进行插入排序
参数     :n表示数组的大小
----------------------------*/
void insertSort(int data[], int n)
{
	int i = 0;
	for (i = 0; i < n - 1; i ++)            //for循环遍历数组元素
	{
		int end = i;                       //使用临时变量end来决定每一步排序开始的位置
		int tmp = data[end + 1];           //使用临时变量tmp来存放end的下一个元素的值

		while (end >= 0)                   //如果end小于0,则end已经超出数组的范围,即会跳出循环
		{
			if (tmp < data[end])           //如果tmp的值小于end,则end向后移动一位(将 < 改为 > 可以变为降序)
			{
				data[end + 1] = data[end];
				end --;                     //end往前移,使得前一个元素标记位新的end
			}
			else                           //如果tmp的值大于end,则跳出while循环
			{
				break;
			}
		}

		data[end + 1] = tmp;               //将tmp插入到end的后一位
	}
}

/*-----------------------------
函数功能 :主函数
-------------------------------*/
int main()
{
	int i;
	int data[N];

	printf("请输入%d个要排序的数据:", N);   //提示输入数据
	for (i = 0; i < N; i ++)
	{
		scanf("%d", &data[i]);
	}

	insertSort(data, N);
	print(data, N);

	system("pause");                         //使运行框停留
	return 0;
}


 

/*-------------------------
编辑作者:小小扫地僧
编辑日期:2023年4月24日
函数功能:插入排序并显示排序过程
---------------------------*/

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>                       //for system("pause")
#define N 8

/*--------------------------
函数功能:将指定大小的数组打印出来
参数    :n表示数组的大小
----------------------------*/
void print(int data[], int n)     
{
	int i;
	for (i = 0; i < n; i ++)
	{
		printf("%d", data[i]);
	}
	printf("\n");
}

/*----------------------------
函数功能:将指定的数组进行插入排序
参数    :n表示待排数组的大小
------------------------------*/
void insertSort(int data[], int n)   
{
	int i, j;
	int t;
	for (i = 1; i < n; i ++)
	{
		t = data[i];
		j = i - 1;

		while (j >= 0 && data[j] > t)
		{
			data[j + 1] = data[j];
			j --;
		}
		data[j + 1] = t;

		print(data, n);              //显示排序的过程 
	}

}

/*-----------------------------
函数功能:主函数
-------------------------------*/
int main()
{
	int data[N];
	int i;

	printf("please input %d number :", N);
	for (i = 0; i < N; i ++)
	{
		scanf("%d",&data[i]);
	}
	printf("排序后的数组为:\n");
	insertSort(data, N);

	system("pause");                  //使运行框停留
	return 0;
}
(2)折半插入排序

折半插入排序(Binary Insertion Sort)是对直接插入排序的一种改进。它通过利用二分查找的思想来确定插入位置,减少了比较的次数,从而提高了插入排序的效率。

具体步骤如下:

  1. 将数组分为已排序和未排序两部分,初始状态下已排序部分只包含第一个元素,即将第一个元素视为已排序序列。

  2. 从未排序部分依次取出一个元素,在已排序部分中使用折半查找的方式找到合适的插入位置。

  3. 采用二分查找的方法,在已排序部分中确定插入位置时,首先将已排序部分的首尾指针设为low和high,计算中间位置mid。

  4. 比较当前要插入的元素与已排序部分的中间元素,如果当前元素小于mid所指的元素,则将high指针移到mid位置之前;否则将low指针移到mid位置之后。

  5. 不断重复步骤4,缩小low和high指针之间的范围,直到low和high指针相遇,此时插入位置即为该位置。

  6. 将当前元素插入到插入位置,并将插入位置之后的元素都后移一位。

  7. 重复步骤2到步骤6,直到未排序部分的所有元素都被插入到已排序部分。

折半插入排序的时间复杂度仍然是 O(n^2),但相比直接插入排序,它减少了比较的次数,因此在某些情况下可以稍微提高性能。然而,对于大规模乱序的数组,折半插入排序的效率仍然较低,所以在实际应用中,通常会选择更高效的排序算法。

/*--------------------------
编辑作者:小小扫地僧
编辑日期:2023年4月24日
函数功能:对数据实现折半插入排序
----------------------------*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define size 10

/*--------------------------
函数功能:折半插入排序
参数    :*num是待排序的数组(指针形式),len是数组长度
----------------------------*/
void BInsertSort(int *num, int len)
{
	int i;
	int j;
	int low;
	int mid;
	int key;

	for (i = 1; i < len; i ++)
	{
		if (num[i - 1] > num[i])                   // 判断当前数据是否需要进行插入
		{			
			key = num[i];                          // 获取需要插入的数据
			low = 0;			                   // 初始查找范围为 [0, i-1](有序)
			high = i - 1;
			while (low <= high)			           // 进行折半二分算法查找插入的位置
			{
				mid = (low + high) / 2;			   // 获取中间下标
				if (key <= num[mid])
				{
					high = mid - 1;				   // 如果key小于中间值,则缩小查找范围到左子序列
				}
				else
				{
					low = mid + 1;				   // 如果key大于中间值,则缩小查找范围到右子序列
				}
			}
			for (j = i - 1; j >= high + 1; j--)	   // 整体后移
			{
				num[j + 1] = num[j];
			}
			num[high + 1] = key;			       // 插入数据
		}
	}
}


/*-----------------------------
函数功能:主函数
-------------------------------*/
int main()
{
	int i;
	int	num[size];

	printf("请输入%d个待排数据:",size);
	for (i = 0; i < size; i++)                    //输入待排数据,存入数组中
	{
		scanf("%d",&num[i]);
	}
	printf("\n");

	printf("排序后的数据为:\n");
	BInsertSort(num, size);
	for (i = 0; i < size; i++)
	{
		printf("%d ", num[i]);
	}
	printf("\n");

	system("pause");
	return 0;
}

 3、希尔排序

希尔排序(Shell Sort)是一种基于插入排序的改进算法,也被称为缩小增量排序。它通过将数组划分为多个较小的子数组,并对这些子数组进行插入排序,逐步缩小子数组的规模,最终完成整个数组的排序。

希尔排序的基本思想是,首先选择一个递减的增量序列(通常是使用 Knuth 序列),根据这个增量序列将数组分成多个子数组。然后对每个子数组进行插入排序,使得每个子数组变得部分有序。随后逐步缩小增量,再次对子数组进行插入排序。最后当增量为1时,进行最后一次插入排序,完成整个数组的排序。

具体步骤如下:

  1. 选择一个递减的增量序列,通常是使用 Knuth 序列(3h + 1,其中 h 是指数)。这样可以保证增量序列的最后一个值为1。

  2. 根据当前增量,将数组分成多个子数组。每个子数组的相邻元素之间的距离为当前增量。

  3. 对每个子数组进行插入排序,使得每个子数组变得部分有序。

  4. 缩小增量,重复步骤2和步骤3,直到增量为1。

  5. 当增量为1时,进行最后一次插入排序,完成整个数组的排序。

希尔排序的时间复杂度与增量序列的选择有关。在最坏情况下,其时间复杂度为 O(n^2),但在平均情况下可以达到 O(nlogn) 的性能。希尔排序的优势是相对于直接插入排序,它可以通过提前部分排序而减少后续的移动操作,从而使排序效率得到提高。

/*-----------------------------
编辑作者:小小扫地僧
编辑日期:2023年4月24日
函数功能:希尔排序
-------------------------------*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define N 8

/*-----------------------------
函数功能:打印数组
-------------------------------*/
void arr_out(int a[],int n)
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

/*-------------------------------
函数功能:利用希尔排序算法对数据进行排序
---------------------------------*/
void arr_sort(int *p, int n)
{
	int step = 0;                                               //步长
	int temp = 0;                                               //用来保存第二段数据
	int i;
	int j;

	for (step = n / 2; step > 0; step /= 2)
	{
		for (i = step; i < n; i ++)
		{
			temp = p[i];
			for (j = i - step; j >= 0 && p[j] > temp; j -= step)	
			{
				p[j + step] = p[j];                             //当满足条件时第一次j+step = i;
				                                                //后面的j+step = 上一次j的值				
			}
			p[j + step] = temp;
		}
	}
}

/*------------------------------
函数功能:主函数
--------------------------------*/
int main()
{
	int a[N] = { 0 };
	int i = 0;

	printf("请输入%d个待排数据:",N);
	for (i = 0; i < N; i ++)
	{
		scanf("%d", &a[i]);
	}

	arr_sort(a, N);                          //排序函数
	arr_out(a,N);                            //输出函数

	system("pause");
	return 0;
}

 4、堆排序

堆排序(Heap Sort)是一种基于二叉堆数据结构的排序算法。它利用堆的性质进行排序,通过将数组构建成一个最大堆(或最小堆),然后逐步取出堆顶元素并调整堆来实现排序。

具体步骤如下:

  1. 构建最大堆(或最小堆):将待排序的数组看作完全二叉树,并从最后一个非叶子节点开始,依次向上调整每个子树,使其满足堆的性质。这样就得到一个最大堆,即每个父节点的值都大于等于其子节点(或最小堆,每个父节点的值都小于等于其子节点)。

  2. 交换堆顶元素和最后一个叶子节点:将堆顶元素与数组末尾的元素交换位置,此时数组末尾的元素就是当前最大的(或最小的)元素。

  3. 调整堆:将堆顶元素下沉至合适位置,重新满足堆的性质。这样可以确保下一个要取出的元素是次最大的(或次最小的)元素。

  4. 重复步骤2和步骤3,直到堆为空或只剩一个元素。

  5. 最终得到的数组就是有序的。

堆排序的时间复杂度是 O(nlogn),其中 n 是待排序数组的长度。堆排序在实践中具有较好的性能,尤其适用于对大规模数据进行排序,但相比其他排序算法,其实现较为复杂。此外,堆排序是一种不稳定的排序算法,即相等元素的相对顺序可能会发生改变。

/*---------------------------
编辑作者:小小扫地僧
编辑日期:2023年4月25日
函数作用:堆排序算法
-----------------------------*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

/*--------------------------
函数功能:打印出数组
----------------------------*/
void Print(int* arr, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
}

/*----------------------------
函数功能:交换两个数据的地址
------------------------------*/
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

/*------------------------------
函数功能:向下调整算法
--------------------------------*/
void AdjustDown(int* arr, int n, int root)
{
	int parent = root;
	int child = parent * 2 + 1;                              //默认先对左孩子进行操作
	while (child < n)
	{
															 //选出左右孩子较小的那一个
															 //防止没有右孩子,数组越界
		if (child + 1<n && arr[child + 1] > arr[child])      //建小堆用小于,建大堆用大于
		{
			child += 1;
		}

		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 = (n - 1 - 1) / 2; i >= 0; i --)                //建堆,排升序建大堆,排降序建小堆
	{
		AdjustDown(arr, n, i);
	}

	//排升序
	int end = n - 1;                                         //end为最后一个数的下标
	while (end > 0)                                          //剩余超过一个数就交换
	{
		Swap(&arr[0], &arr[end]);
		AdjustDown(arr, end, 0);
		end--;
	}
}

/*---------------------------
函数功能:测试函数
-----------------------------*/
void test()
{
	int arr[] = { 10,9,8,7,6,5,3,5,4,2,0,1 };
	int n = sizeof(arr) / sizeof(arr[0]);
	HeapSort(arr, n);
	Print(arr, n);
}

/*----------------------------
函数功能:主函数
------------------------------*/
int main()
{
	test();
	system("pause");
	return 0;
}

 5、归并排序

归并排序(Merge Sort)是一种基于分治思想的排序算法。它将待排序的数组不断地划分为较小的子数组,然后对这些子数组进行递归地排序,最后将排好序的子数组合并成一个有序数组。

具体步骤如下:

  1. 将待排序的数组递归地划分为两个子数组,直到每个子数组只包含一个元素。

  2. 对每对相邻的子数组进行合并操作,将它们合并为一个有序的子数组。合并时,比较两个子数组的首个元素,将较小(或较大)的元素放入新的临时数组中,并将该子数组指针向后移动一位。

  3. 重复步骤2,直到所有子数组都合并成一个完整的有序数组。

归并排序的关键在于合并操作,它要求合并的两个子数组都是有序的。通过递归地将数组划分为较小的子数组再进行合并,可以保证最终整个数组是有序的。

归并排序的时间复杂度始终为 O(nlogn),其中 n 是待排序数组的长度。由于归并排序需要额外的空间来存储临时数组,在空间复杂度上需要额外的 O(n) 空间。归并排序是稳定的排序算法,即相等元素的相对顺序在排序前后保持不变。

归并排序的优点是稳定且适用于各种数据规模,但其缺点是需要额外的空间,并且在实际应用中可能比其他排序算法稍慢

/*---------------------------
编辑作者:小小扫地僧
编辑日期:2023年4月25日
函数功能:归并排序
-----------------------------*/
#define _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>

/*------------------------------
归并排序:将一个数组中两个相邻有序空间合并成一个

参数说明:
 a -- 包含两个有序区间的数组
 start -- 第一个有序区间的起始地址
 mid -- 第一个有序区间的结束地址。也是第二个有序区间的起始地址
 end -- 第二个有序区间的结束地址
-------------------------------*/
void merge(int a[], int start, int mid, int end) 
{
	int* tmp = (int*)malloc((end - start + 1) * sizeof(int));    // tmp是汇总2个有序区间的临时区域。	
	int i = start;                                               // 第一个有序区的索引
	int j = mid + 1;                                             // 第二个有序区的索引
	int k = 0;                                                   // 临时区域的索引

	while (i <= mid && j <= end) 
	{
		if (a[i] <= a[j]) 
		{
			tmp[k ++] = a[i ++];
		}
		else
		{
			tmp[k ++] = a[j ++];
		}
	}

	while (i <= mid) 
	{
		tmp[k ++] = a[i ++];
	}

	while (j <= end)
	{
		tmp[k ++] = a[j ++];                                        // 将两个有序区间合并
	}
	
	for (i = 0; i < k; i ++) 
	{
		a[start + i] = tmp[i];                                      // 排序后的元素,全部都整合到数组a中
	}
	free(tmp);
	tmp = NULL;
}

// 归并排序--从上往下
// 参数说明:
// a -- 待排序数组
// start -- 数组的起始地址
// end -- 数组的结束地址
//
void merge_sort_up_to_down(int a[], int start, int end) 
{
	if (a == NULL || start >= end)
	{
		return;
	}
	int mid = (end + start) / 2;

	merge_sort_up_to_down(a, start, mid);                  // 递归排序a[start..mid]
	merge_sort_up_to_down(a, mid + 1, end);                // 递归排序a[mid..end]
											               // a[start..mid]和a[mid..end]是两个有序空间
											               // 将它们排序成一个有序空间a[start..end]
	merge(a, start, mid, end);
}

int main()
{
	int arr[] = { 9,5,1,6,2,3,0,4,8,7 };
	merge_sort_up_to_down(arr, 0, 9);
	for (int i = 0; i < 10; i++) 
	{
		printf("%d ", arr[i]);
	}
	printf("\n");

	system("pause");
	return 0;
}

 6、基数排序

前面的算法都是基于比较,而基数排序法则不需要比较,通过关键字中的信息进行分类,进行分配和采集从而实现排序

 举个简单的例子如下L:

先按照个位排序:

 再按照十位排序:

再按照百位排序: 

#include <stdio.h>

// 获取数组中的最大值
int getMax(int arr[], int n) {
    int max = arr[0];
    for (int i = 1; i < n; i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }
    return max;
}

// 基数排序函数
void radixSort(int arr[], int n) {
    int max = getMax(arr, n); // 获取数组中的最大值

    // 根据位数进行排序
    for (int exp = 1; max / exp > 0; exp *= 10) {
        int output[n]; // 存储排序后的结果数组
        int count[10] = {0}; // 存储每个数字出现的次数(0-9)

        // 计算每个数字出现的次数
        for (int i = 0; i < n; i++) {
            count[(arr[i] / exp) % 10]++;
        }

        // 计算累加次数
        for (int i = 1; i < 10; i++) {
            count[i] += count[i - 1];
        }

        // 将元素按照当前位数排序到 output 数组中
        for (int i = n - 1; i >= 0; i--) {
            output[count[(arr[i] / exp) % 10] - 1] = arr[i];
            count[(arr[i] / exp) % 10]--;
        }

        // 将排序后的结果复制回原数组
        for (int i = 0; i < n; i++) {
            arr[i] = output[i];
        }
    }
}

// 打印数组元素
void printArray(int arr[], int n) {
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main() {
    int arr[] = {170, 45, 75, 90, 802, 24, 2, 66};
    int n = sizeof(arr) / sizeof(arr[0]);

    printf("原始数组:");
    printArray(arr, n);

    radixSort(arr, n);

    printf("排序后的数组:");
    printArray(arr, n);

    return 0;
}

以上就是我对各类排序算法的整理与汇总,希望我的分享能 够对你有所帮助,敬请关注,持续更新中!

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小_扫地僧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值