C语言排序算法

博客主要是对老师所讲的排序算法进行总结,聚焦于信息技术领域的算法知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

老师讲的一些排序算法的总结

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


//相关的概念和定义
/*
这里涉及到第十章排序的主要内容

内排序和外排序:
内排序:整个表都放在内存中处理,排序时不涉及数据的内、外存交换
外排序:排序过程中要进行数据的内、外层交换

内排序的分类:
插入排序、交换排序、选择排序、归并排序(基于比较的排序算法)
基数排序(不基于比较的排序算法)

基于比较的排序方法:
最好的平均时间复杂度为O(n(log2)n)
最好的情况是排序序列正序,时间复杂度为O(n)

内排序算法的稳定性:
如果待排序的表中存在多个关键字相同的记录,经过排序后,这些具有相同关键字的记录之间的相对次序保持不变,则称这种排序方法是稳定的
反之,如果具有相同关键字的记录之间的相对次序发生变化,则称这种排序方法是不稳定的。

正序和反序:
如果待排序的表中元素已经按照关键字排好序,称此表中元素为正序
反之,若待排序的表中元素的关键字顺序正好和排好序的顺序相反,则称此表中元素为反序。
*/

//定义数据元素的类型
typedef int KeyType;
typedef int InfoType;

typedef struct {
	KeyType key;
	InfoType data;
}Node;

//排序方法汇总

//插入排序
/*
基本思路:将无序区域的元素一个一个的插入有序区域
注意事项:
1.归位:当一个元素在整个排序结束前就已经在其最终位置上,称为归位。
2.每一次产生的有序区不一定是全局有序区,即有序区中的元素不一定放在最终位置上。
3.全局有序区中的元素在后面的排序中不会再发生位置的改变。

主要的插入排序方法有:
1.直接插入排序。
2.折半插入排序。
3.希尔排序。
*/

//直接插入排序
/*
思路:
初始时有序区只有第一个初始元素。
一次插入排序的操作包括:
取出无序区域的第一个元素
依次和有序区域比较大小,找到合适的插入位置
从插入位置的元素开始一次向后挪一个元素的位置
插入该元素
重复以上操作n-1次
*/

void direct_insert_sort_inc(int A[], int n);
void direct_insert_sort_dec(int A[], int n);

//折半插入排序
/*
思路:
折半查找过程只是在查找的过程中进行了优化
一次折半插入排序的操作包括:
1.取出无需区域的第一个元素
2.利用二分查找法,在有序区域中找出合适的插入位置
3.插入改元素
4.重复以上操作n-1次

注意:折半插入排序和直接插入排序的区别仅仅在于查找过程,二分查找法利用有序表的二分查找提高了查找的速度
*/

void bin_insert_sort_inc(int A[], int n);
void bin_insert_sort_dec(int A[], int n);

//希尔排序
/*
思路:
1.取初始值d=n/2
2.将排序序列分为d个组,在各组内进行直接插入排序
3.d=d/2递减,直到d=1

如何保证结果的正确性:
在d=1的时候进行了直接插入排序,因此结果一定是正确的!
希尔排序的时间复杂度约为O(n^1.3)
希尔排序是一种不稳定的排序算法
*/

void shell_insert_sort_inc(int A[], int n);
void shell_insert_sort_dec(int A[], int n);

//交换排序
/*
基本思路:两个元素反序时进行交换

常见的交换排序方法:
1.冒泡排序
2.快速排序
*/

//冒泡排序
/*
思路:
初始时有序区域为空
依次从无序区域通过交换位置找出一个最大或最小的元素,将其放置有序区域,重复该操作n-1次

注意事项:
冒泡排序所得的有序区域总是全局有序的!
*/
void bubble_sort_inc(int A[], int n);
void bubble_sort_dec(int A[], int n);


//快速排序
/*
思路:
每一次将表的第一个元素归为,
将归为后的表分成两张小表,对着两张小表分别进行递归

第一个元素归为的方法:
将待归位的第一个元素拿出
用两个指针分别指向待排序数组的头和尾
对于头指针,如果指向的元素小于等于待归位元素,则将指针后移,否则A[j] = A[i],进行尾指针的比较
对于尾指针,如果指向的元素大于等于待归位元素,则将指针前移,否则A[i] = A[j],进行下一次操作
*/
void quick_sort_inc(int A[], int first, int end);
void quick_sort_dec(int A[], int first, int end);

//选择排序
/*
思路:
类似于冒泡排序,初始时有序区域为空,从无序区域找出一个最小的(不是通过交换,而是直接找),并将其加入有序区
重复上述操作n-1次

常见的选择排序方法:
1.简单选择排序
2.堆排序
*/

//简单选择排序
/*
初始时,全局有序区域为空,从无序区域找出最小或者最大的元素,加入有序区
重复操作n-1次

需要注意的是,与冒泡排序不同的是,简单选择排序没有记录移动,因此在任何情况下都要做n-1次
*/
void easy_select_sort_inc(int A[], int n);
void easy_select_sort_dec(int A[], int n);


//堆排序,nb的算法
/*
基本概念
对于一个队列ki,i=1,2,3,4,5...n
如果满足ki<=k2i,ki<=k2i+1,称满足该情况的堆为小根堆(可以理解为将该序列看成一个完全二叉树之后,所有的孩子节点均小于其对应的父亲节点)
同理可以推出大根堆的定义
以下讨论的仅为大根堆

由大根堆的定义可以知道:最小关键字的记录一定是某一个叶子节点

判断一棵完全二叉树是否为大根堆只需要从n/2的节点开始,所有的分支节点均满足条件即可

推排序的算法设计:
推排序的关键是如何构造堆,这里采用筛选算法来建堆,所谓的筛选,即是指对于一棵左/右子树均为堆的完全二叉树,通过"调整"其根节点,使得整个二叉树也成为一个堆
筛选算法的思路:
如果父亲节点大于孩子节点,则已经构成一个堆
如果父亲节点小于孩子节点,则找出孩子节点中较大的那一个,让该节点和父亲节点交换位置,并继续向下筛选

在操作系统中,将多个进程放在一个队列中,每个进程有一个优先级,总是出队优先级最高的进程执行
其所采用的优先队列,用堆来实现
*/
void filter(int A[], int low, int high);
void big_heap_sort_inc(int A[], int n);

//小根堆的构造过程省略



//归并排序
/*
思路:
多次将相邻的两个或者两个以上的有序表合并成一个新的有序表
最简单的归并是将相邻的两个有序子表合并成一个有序的表,即二路归并排序

算法的实现:
1.一次二路归并,将两个相邻的有序子表合并成一个有序的序列
2.对该顺序表进行一趟二路归并,注意可能会出现子表长度小于length的情况
3.直到length<n,有length=2*length
*/
//实现一次二路归并
void merge(int A[], int low, int mid, int high);
//实现一趟二路归并排序
void merge_pass(int A[], int length, int n);
//实现二路归并算法
void merge_sort(int A[], int n);

//基数排序nb啊
/*
基数r可以理解为进制数

基数排序分为两种,一种是最低位优先LSD、一种是最高位优先MSD,一般而言,对于整数序列的递增排序,选择最低位优先

基数排序的思想如下:
首先构造Q0,Q1,Q2...Qr-1,一共r个队列
对于线性表中的数,从个位开始分配,将所有个位上的值等于k的数进队列Qk,所有的数处理完之后进行收集,即将各个队列中的首尾节点相连得到新的节点
对该线性表中数的所有位上均进行该分配和收集操作,则最后一次收集完毕即得到所要的排列!
*/
//由于基数排列不需要进行关键字的比较,所以基数排列的时间复杂度为O(n)

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

void direct_insert_sort_inc(int A[], int n)
{
	for (int i = 1; i < n; i++)
	{
		int j = i - 1;
		for (j = i-1; j >= 0; j--)
		{
			if (A[i] > A[j]) break;
		}
		int tmp = A[i];
		for (int k = i - 1; k > j; k--) {
			A[k + 1] = A[k];
		}
		A[j + 1] = tmp;
	}
}

void direct_insert_sort_dec(int A[], int n)
{
	for (int i = 1; i < n; i++)
	{
		int j = i - 1;
		for (j = i - 1; j >= 0; j--)
		{
			if (A[i] < A[j]) break;
		}
		int tmp = A[i];
		for (int k = i - 1; k > j; k--)
		{
			A[k + 1] = A[k];
		}
		A[j + 1] = tmp;

	}
}

void bin_insert_sort_inc(int A[], int n)
{
	for (int i = 1; i < n; i++)
	{
		int tmp = A[i];
		//下面是查找插入点的过程
		int first = 0, last = i - 1;
		while (first <= last)
		{
			int mid = (first + last) / 2;
			if (A[i] >= A[mid])
			{
				first = mid + 1;
			}
			else last = mid - 1;
		}
		//找到位置first
		//将全体元素右移
		for (int k = i - 1; k >= first; k--) A[k + 1] = A[k];
		A[first] = tmp;
	}
}

void bin_insert_sort_dec(int A[], int n)
{
	for (int i = 1; i < n; i++)
	{
		int tmp = A[i];
		int first = 0, last = i - 1;
		while (first <= last)
		{
			int mid = (first + last) / 2;
			if (A[i] <= A[mid]) first = mid + 1;
			else last = mid - 1;
		}
		for (int k = i - 1; k >= first; k--) A[k + 1] = A[k];
		A[first] = tmp;
	}
}

void shell_insert_sort_inc(int A[], int n)
{
	//置增量初值
	int d = n / 2;
	while (d > 0)
	{
		for (int i = d; i < n; i++)
		{
			int tmp = A[i];
			int j = i - d;
			for (j = i - d; i >= 0; j = j - d)
			{
				if (A[i] > A[j]) break;
			}
			for (int k = i - d; k > j; k = k - d) A[k + d] = A[k];
			A[j + d] = tmp;
		}
		d = d / 2;
	}
}

void shell_insert_sort_dec(int A[], int n)
{
	//置增量初值
	int d = n / 2;
	while (d > 0)
	{
		for (int i = d; i < n; i++)
		{
			int tmp = A[i];
			int j = i - d;
			for (j = i - d; j >= 0; j = j - d)
			{
				if (A[i] < A[j]) break;
			}
			for (int k = i - d; k > j; k = k - d) A[k + d] = A[k];
			A[j + d] = tmp;
		}
		d = d / 2;
	}
}

void bubble_sort_inc(int A[], int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		//改进冒泡算法:如果不发生交换,则说明已经排好序
		bool exchange = false;
		for (int j = n - 1; j > i; j--)
		{
			if (A[j] < A[j - 1])
			{
				int tmp = A[j-1];
				A[j-1] = A[j];
				A[j] = tmp;
				exchange = true;
			}
		}
		if (!exchange) return;
	}
}

void bubble_sort_dec(int A[], int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		bool exchange = false;
		for (int j = n - 1; j > i; j--)
		{
			if (A[j] > A[j - 1])
			{
				int tmp = A[j - 1];
				A[j-1] = A[j];
				A[j] = tmp;
				exchange = true;
			}
		}
		if (!exchange) return;
	}
}

void quick_sort_inc(int A[], int first, int end)
{
	int i = first, j = end;
	//如果头指针小于尾指针,则继续递归
	if (first < end)
	{
		int tmp = A[first];
		while (i != j)
		{
			//先对j开始,因为第一个元素可以不用
			while (j > i && A[j] >= tmp) j--;
			A[i] = A[j];
			while (j > i && A[i] <= tmp) i++;
			A[j] = A[i];
		}
		//第一个元素归为
		A[i] = tmp;
		//递归排序左区间和右区间
		quick_sort_inc(A, first, i - 1);
		quick_sort_inc(A, i + 1, end);
	}
}

void quick_sort_dec(int A[], int first, int end)
{
	int i = first, j = end;
	if (first < end)
	{
		//对一个元素进行归为
		int tmp = A[first];
		while (i != j)
		{
			while (j > i && A[j] <= tmp) j--;
			A[i] = A[j];
			while (j > i && A[i] >= tmp) i++;
			A[j] = A[i];
		}
		//归为
		A[i] = tmp;
		quick_sort_dec(A, first, i - 1);
		quick_sort_dec(A, i + 1, end);
	}

}

void easy_select_sort_inc(int A[], int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		//用来记录找到的元素
		int k = i;
		for (int j = i; j < n; j++)
		{
			if (A[j] < A[k]) k = j;
		}
		if (k != i)
		{
			int tmp = A[i];
			A[i] = A[k];
			A[k] = tmp;
		}
	}
}

void easy_select_sort_dec(int A[], int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int k = i;
		for (int j = i; j < n; j++)
		{
			if (A[j] > A[k]) k = j;
		}
		if (k != i)
		{
			int tmp = A[i];
			A[i] = A[k];
			A[k] = tmp;
		}
	}
}

void filter(int A[], int low, int high)
{
	//对根节点进行筛选
	//j指向左孩子
	int i = low, j = 2 * i;
	int tmp = A[i];
	//筛选算法
	while (j <= high)
	{
		//总是让j指向那个较大的孩子节点
		if (A[j] < A[j + 1] && j < high) j++;
		//如果父亲节点比较小,则交换
		if (tmp < A[j])
		{
			A[i] = A[j];
			i = j;
			j = 2 * i;
		}
		else break;
	}
	//筛选完毕
	A[i] = tmp;
}

void big_heap_sort_inc(int A[], int n)
{
	//首先开始建堆
	//从n/2开始
	for (int i = n / 2; i >= 0; i--)
	{
		filter(A, i, n - 1);
	}
	//每次取出大根堆的根节点,即最大的那一个进行排序
	for (int i = n - 1; i >= 1; i--)
	{
		int tmp = A[0];
		//将每次筛选出的最大元素调至当前的最后一位
		A[0] = A[i];
		A[i] = tmp;
		//之所以每次的初始值都为0,是因为为了保证一直是只用调整根节点,即除了根节点,其他节点均满足大根堆的条件
		filter(A, 0, i - 1);
	}
}

void merge(int A[], int low, int mid, int high)
{
	int *R = (int *)malloc(sizeof(int)*(high - low + 1));
	int i = low, j = mid + 1, k = 0;
	while (i <= mid && j <= high)
	{
		if (A[i] < A[j])
		{
			R[k] = A[i];
			i++;
			k++;
		}
		else
		{
			R[k] = A[j];
			j++;
			k++;
		}
	}
	//将余下的部分复制进去
	while (i <= mid)
	{
		R[k] = A[i];
		i++;
		k++;
	}
	while (j <= high)
	{
		R[k] = A[j];
		j++;
		k++;
	}
	//将排序好的元素复制回去
	int t = 0;
	for (int s = low; s <= high; s++)
	{
		A[s] = R[t];
		t++;
	}
}

void merge_pass(int A[], int length, int n)
{
	int i = 0;
	for (i = 0; i + 2 * length - 1 < n; i += 2 * length) merge(A, i, i + length - 1, i + 2 * length - 1);
	//如果有余下的子表,则继续进行归并
	if (i + length - 1 < n) merge(A, i, i + length - 1, n - 1);
}

void merge_sort(int A[], int n)
{
	int length = 0;
	for (length = 1; length < n; length = 2 * length) merge_pass(A, length, n);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值