八大排序算法(c语言实现)

小知识:

1)八大排序算法皆是内部排序。

2)稳定的算法在排序的过程中不会改变元素彼此的位置的相对次序。反之不稳定的算法会经常改变这个相对次序。

排序方法 时间复杂度(平均) 时间复杂度(最坏) 时间复杂度(最好) 空间复杂度 稳定性
冒泡排序 O(n^2) O(n^2) O(n) O(1) 稳定
选择排序 O(n^2) O(n^2) O(n^2) O(1) 不稳定
简单插入排序 O(n^2) O(n^2) O(n) O(1) 稳定
希尔排序 O(n^1.5) O(n^2) O(n) O(1) 不稳定
归并排序 O(nlogn) O(nlogn) O(nlogn) O(n) 稳定
快速排序 O(nlogn) O(n^2) O(nlogn) O(nlogn) 不稳定
堆排序 O(nlogn) O(nlogn) O(nlogn) O(1) 不稳定
基数排序 O(n*k) O(n*k) O(n*k) O(n+k) 稳定

1、冒泡排序

思路:外层从1到n-1,控制排序趟数;内循环依次比较相邻元素,出现逆序就交换。每趟排序把最大的/最小的移到最后面。

#include <stdio.h>

/*
* 功能:冒泡排序,将最大元素放在最后。
* 传入参数:arr,待排序数组;arr_size,待排序数组的元素个数。
*/
void bubble_sort(int* arr, int arr_size)
{
	int i,j,tmp;
	for (i = 0; i < arr_size-1; ++i)			//外层循环控制排序趟数
	{
		for (j = 0; j < arr_size-1-i; ++j)
		{
			if (arr[j] > arr[j + 1]) {
				tmp = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = tmp;
			}
		}
	}
}

/*
* 功能:依次打印数组每个元素。
* 传入参数:arr,待打印元素的数组;arr_size,待打印元素数组的元素格式。
*/
void print_arr(int* arr, int arr_size)
{
	int i;
	for (i = 0; i < arr_size; ++i)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

int main()
{
	int arr[10] = {2,5,6,4,3,7,9,8,1,0};
	print_arr(arr, 10);
	bubble_sort(arr, 10);
	print_arr(arr, 10);
	return 0;
}

2、选择排序

思路:首先在未排序元素中,找到最大/最小值,存放在排序序列的起始位置;然后再在剩余未排序元素中寻找最大/最小值,放到已排序序列的末尾。

#include <stdio.h>

/*
* 功能:选择排序,将最大元素放到最后。
* 传入参数:arr, 待排序数组;arr_size,待排序数组的元素个数。
*/
void select_sort(int* arr, int arr_size)
{
	int i, j, tmp, min_index;
	for (i = 0; i < arr_size-1; ++i)
	{
		min_index = i;
		for (j = i+1; j < arr_size; ++j)
		{
			if (arr[j] < arr[min_index]) {
				min_index = j;
			}
		}
		if (min_index != i) {
			tmp = arr[i];
			arr[i] = arr[min_index];
			arr[min_index] = tmp;
		}
	}
}

/*
* 功能:依次打印数组每个元素。
* 传入参数:arr,待打印元素的数组;arr_size,待打印元素数组的元素格式。
*/
void print_arr(int* arr, int arr_size)
{
	int i;
	for (i = 0; i < arr_size; ++i)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

int main()
{
	int arr[10] = { 2,5,6,4,3,7,9,8,1,0 };
	print_arr(arr, 10);
	select_sort(arr, 10);
	print_arr(arr, 10);
	return 0;
}

3、简单插入排序

思路:构建有序序列,对于未排序的数据,从已排序序列中从后往前扫描,找到相应位置并插入。像扑克牌的理牌。

#include <stdio.h>

/*
* 功能:简单插入排序。将最大元素放到最后。
* 传入参数:arr, 待排序数组;arr_size,待排序数组的元素个数。 
*/
void insert_sort(int* arr, int arr_size)
{
	int i, j, key;
	//从第二个数字开始,每一个数字都试图跟它前一个比较并交换,直到前一个数字不存在或者比它小或相等时停下来。
	for (i = 1; i < arr_size; ++i)
	{
		key = arr[i];
		j = i - 1;
		while (j >= 0 && arr[j] > key)
		{
			arr[j + 1] = arr[j];
			--j;
		}
		arr[j + 1] = key;
	}
}

/*
* 功能:依次打印数组每个元素。
* 传入参数:arr,待打印元素的数组;arr_size,待打印元素数组的元素格式。
*/
void print_arr(int* arr, int arr_size)
{
	int i;
	for (i = 0; i < arr_size; ++i)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

int main()
{
	int arr[10] = { 2,5,6,4,3,7,9,8,1,0 };
	print_arr(arr, 10);
	insert_sort(arr, 10);
	print_arr(arr, 10);
	return 0;
}

4、希尔排序

思路:希尔排序也是一种插入排序,它是简单插入排序经改进后一个更高效的版本,也称为缩小增量排序。

#include <stdio.h>

/*
* 功能:希尔排序。将最大元素放到最后。
* 传入参数:arr,待排序数组;arr_size,待排序数组的元素个数。
*/
void shell_sort(int* arr, int arr_size)
{
	int gap,i, j, tmp;
	for (gap = arr_size / 2; gap > 0; gap /=  2)
	{
		for (i = gap; i < arr_size; ++i)
		{
			j = i;
			while (j - gap >= 0 && arr[j-gap] > arr[j])	//插入排序采用交换法
			{
				tmp = arr[j];
				arr[j] = arr[j - gap];
				arr[j - gap] = tmp;
				j -= gap;
			}
		}
	}
}

/*
* 功能:依次打印数组每个元素。
* 传入参数:arr,待打印元素的数组;arr_size,待打印元素数组的元素格式。
*/
void print_arr(int* arr, int arr_size)
{
	int i;
	for (i = 0; i < arr_size; ++i)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

int main()
{
	int arr[10] = { 2,5,6,4,3,7,9,8,1,0 };
	print_arr(arr, 10);
	shell_sort(arr, 10);
	print_arr(arr, 10);
	return 0;
}

5、归并排序

思路:将已有序的子序列合并,得到完全有序的序列。

先使每个子序列有序,再使子序列段间序列有序。若将两个有序表合成一个有序表,称为2-路归并。

它使用了递归分治的思想:相当于,左半边用尽,则取右边元素;右半边用尽,则取左边元素;右半边的当前元素小于左半边的当前元素,则取右半边元素;右半边的当前元素大于左半边的当前元素,则取左半边的当前元素。

#include <stdio.h>
#include <stdlib.h>

/*
* 功能:归并两个有序区。
* 传入参数:nums,待排序数组;low,归并区的最小下标;mid,归并区的中间下标;high,归并区的最大下标。
*/
void merge(int* nums, int low, int mid, int high)
{
	int* tmp = (int*)malloc((high - low + 1) * sizeof(int));
	if (NULL == tmp) {
		return;
	}
	int i = low;
	int j = mid + 1;
	int k = 0;
	while (i <= mid && j <= high)
	{
		if (nums[i] <= nums[j]) {
			tmp[k++] = nums[i++];
		}
		else {
			tmp[k++] = nums[j++];
		}
	}

	while (i <= mid)
	{
		tmp[k++] = nums[i++];
	}
	while (j <= high)
	{
		tmp[k++] = nums[j++];
	}

	for (i = 0; i < k; ++i)
	{
		nums[low + i] = tmp[i];
	}
}

/*
* 功能:归并排序。将最大元素放到最后。
* 传入参数:nums,待排序数组;low,待排序数组最小下标;high,待排序数组最大下标。
*/
void merge_sort(int* nums, int low, int high)
{
	if (low < high) {
		int mid = (low + high) / 2;
		merge_sort(nums, low, mid);
		merge_sort(nums, mid + 1, high);
		merge(nums, low, mid, high);
	}
}

/*
* 功能:依次打印数组每个元素。
* 传入参数:arr,待打印元素的数组;arr_size,待打印元素数组的元素格式。
*/
void print_arr(int* arr, int arr_size)
{
	int i;
	for (i = 0; i < arr_size; ++i)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

int main()
{
	int arr[10] = { 2,5,6,4,3,7,9,8,1,0 };
	print_arr(arr, 10);
	merge_sort(arr, 0, 9);
	print_arr(arr, 10);
	return 0;
}

6、快速排序

思路:挖坑填数+分治法

1)从序列中选择一个基准数;

2)将序列中的所有数依次遍历,比它大的数位于其右侧,比它小的数位于其左侧。

3)重复前两步,对左右序列分别排序,直到序列有序(子序列只剩下一个数)。

#include <stdio.h>

/*
* 功能:按照基准值,将传入序列分为小于基准值的序列和大于基准值的序列,在中间位置插入该基准值。基准值选择为序列的第一个值。
* 传入参数:nums,待排序数组;low,待排序数组的最小下标;high,待排序数组的最大下标。
* 返回值:基准值的位置。
*/
int partition(int *nums, int low, int high)
{
	int elem = nums[low];
	while (low < high)
	{
		while (low<high && nums[high]>elem)
		{
			--high;
		}
		nums[low] = nums[high];
		while (low < high && nums[low] < elem)
		{
			++low;
		}
		nums[high] = nums[low];
		nums[low] = elem;
	}
	return low;
}

/*
* 功能:快速排序。将最大元素放在最后。
* 传入参数:nums,待排序数组;low,待排序数组的最小小标;high,待排序数组的最大下标。
* 传出参数:nums,排序后的数组。
*/
void quick_sort(int* nums, int low, int high)
{
	if (low < high) {
		int pos = partition(nums, low, high);
		quick_sort(nums, low, pos);
		quick_sort(nums, pos + 1, high);
	}
}

/*
* 功能:依次打印数组每个元素。
* 传入参数:arr,待打印元素的数组;arr_size,待打印元素数组的元素格式。
*/
void print_arr(int* arr, int arr_size)
{
	int i;
	for (i = 0; i < arr_size; ++i)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

int main()
{
	int arr[10] = { 2,5,6,4,3,7,9,8,1,0 };
	print_arr(arr, 10);
	quick_sort(arr, 0, 9);
	print_arr(arr, 10);
	return 0;
}

7、堆排序

1)二叉堆的定义:完全二叉树或近似完全二叉树。二叉堆满足两个特性:

a.父节点的键值总是大于或等于(小于或等于)任何一个子节点的键值。

b.每个节点的左子树和右子树都是二叉堆。

当父节点的键值总是大于或等于任何一个子节点的键值时,是大根堆;当父节点的键值总是小于或等于任何一个子节点的键值时,是小根堆。

2)二叉堆一般使用数组存储。

3)思路:

a.将无序序列建成一个小顶堆,输出堆顶元素;

b.将堆顶元素与末尾元素交换;

c.重新调整结构,使其满足堆定义。重复bc步骤,直到输出全部元素。

#include <stdio.h>
#include <stdlib.h>

/*
* 功能:从上到下调整堆结构
*/
void adjust_down(int* nums, int k, int len)
{
	nums[0] = nums[k];
	int i;
	for (i = k * 2; i <= len; i *= 2)
	{
		if (i < len && nums[i + 1] < nums[i]) {
			++i;
		}
		if (nums[0] <= nums[i]) {
			break;
		}
		else {
			nums[k] = nums[i];
			k = i;
		}
	}
	nums[k] = nums[0];
}

/*
* 功能:构建最小堆。
*/
void build_min_heap(int *nums, int len)
{
	int i;
	for (i = len / 2; i > 0; --i)
	{
		adjust_down(nums, i, len);
	}
}

/*
* 功能:堆排序,并从小到大依次输出排序后的元素。
*/
void heap_sort(int* nums, int len, int *tmp)
{	
	int i, j;

	for (i = 0; i < len; ++i)
	{
		tmp[i + 1] = nums[i];
	}
	build_min_heap(tmp, len);
	for (i = len; i >= 1; --i)
	{
		printf("%d ", tmp[1]);
		j = tmp[1];
		tmp[1] = tmp[i];
		tmp[i] = j;
		adjust_down(tmp, 1, i - 1);
	}
}

/*
* 功能:依次打印数组每个元素。
* 传入参数:arr,待打印元素的数组;arr_size,待打印元素数组的元素格式。
*/
void print_arr(int* arr, int arr_size)
{
	int i;
	for (i = 0; i < arr_size; ++i)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

int main()
{
	int arr[10] = { 2,5,6,4,3,7,9,8,1,0 };
	int after_sort[11] = { 0 };
	print_arr(arr, 10);
	heap_sort(arr, 10, after_sort);
	return 0;
}

8、基数排序

思路:

a.取得数组中的最大值;

b.根据最大值的位数,从低到高对数组进行计数排序;

#include <stdio.h>
#include <stdlib.h>

int get_max(int* nums, int len)
{
	int i;
	int max = nums[0];
	for (i = 1; i < len; ++i)
	{
		if (nums[i] > max) {
			max = nums[i];
		}
	}
	return max;
}

void count_sort(int* nums, int len, int exp)
{
	int* tmp = (int*)malloc(len * sizeof(int));
	if (NULL == tmp) {
		return;
	}
	int queue[10] = { 0 };
	int i;
	for (i = 0; i < len; ++i)
	{
		++queue[(nums[i] / exp)%10];
	}
	for (i = 1; i < 10; ++i)
	{
		queue[i] += queue[i - 1];
	}
	for (i = len - 1; i >= 0; --i)
	{
		tmp[queue[(nums[i] / exp) % 10] - 1] = nums[i];
		--queue[(nums[i] / exp) % 10];
	}
	for (i = 0; i < len; ++i)
	{
		nums[i] = tmp[i];
	}
}

void radix_sort(int* nums, int len)
{
	int exp;
	int max = get_max(nums, len);
	for (exp = 1; max / exp > 0; exp *= 10)
	{
		count_sort(nums, len, exp);
	}
}

/*
* 功能:依次打印数组每个元素。
* 传入参数:arr,待打印元素的数组;arr_size,待打印元素数组的元素格式。
*/
void print_arr(int* arr, int arr_size)
{
	int i;
	for (i = 0; i < arr_size; ++i)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

int main()
{
	int arr[10] = { 2,5,6,4,3,7,9,8,1,0 };
	print_arr(arr, 10);
	radix_sort(arr, 10);
	print_arr(arr, 10);
	return 0;
}

posted @   调蓝师  阅读(149)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示