C语言实现五大排序算法(插入、交换、选择、归并、基数)

/*实现内容
*
* 一、插入排序			平均	最坏	辅助存储
* 	 ①直接插入排序		 n²		 n²       1
* 	 ②折半插入排序		 n²		 n²       1
* 	 ③希尔排序			 和所取增量函数delt有关
* 二、交换排序
* 	 ①冒泡排序			 n²      n²       1
* 	 ②快速排序			nlogn    n²      logn(栈空间)
* 三、选择排序
* 	 ①简单选择排序		 n²		 n²       1
* 	 ②堆排序			nlogn	nlogn	  1
* 四、归并排序			nlogn	nlogn	  n
* 五、基数排序			  dn     dn       rd
*
* 	堆、快速、希尔 不稳定
* 	注意基数排序需要关键字的个数相同,也就是说 数字位数要一样 比如都是个位数、两位数、三位数
* 
*	VS2019编译通过 王大花 2019.8.21
* 
*/

/*实现内容
*
* 一、插入排序			平均	最坏	辅助存储
* 	 ①直接插入排序		 n²		 n²       1
* 	 ②折半插入排序		 n²		 n²       1
* 	 ③希尔排序			 和所取增量函数delt有关
* 二、交换排序
* 	 ①冒泡排序			 n²      n²       1
* 	 ②快速排序			nlogn    n²      logn(栈空间)
* 三、选择排序
* 	 ①简单选择排序		 n²		 n²       1
* 	 ②堆排序			nlogn	nlogn	  1
* 四、归并排序			nlogn	nlogn	  n
* 五、基数排序			  dn     dn       rd
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define Number_Num 12
#define Max_Stack_Size 100
#define Radix 10 
#define Max_Key_Num 2

//非递归需要用到的栈以及函数
typedef struct _StackNode {
	int head;
	int tail;
}StackNode;

typedef struct _Stack {
	StackNode* SNode;
	int tail;
}Stack;

void InitStack(Stack* S) {
	S->SNode = (StackNode*)malloc(sizeof(StackNode) * Max_Stack_Size);
	S->tail = 0;
}

void Push(Stack* S, int head, int tail) {
	S->SNode[S->tail].head = head;
	S->SNode[S->tail++].tail = tail;
}

StackNode Pop(Stack* S) {
	return S->SNode[--S->tail];
}

//基数排序需要的结构
typedef struct _SLNode {
	int key;
	int next;
}SLNode;

typedef struct _SLList {
	SLNode* r;
	//关键字个数
	int KeyNum;

}SLList;

int* Get_Rand_Num() {
	int* tmp = (int*)malloc(sizeof(int) * (Number_Num + 1));
	if (NULL == tmp)
		return NULL;

	for (int i = 1; i <= Number_Num; i++)
		tmp[i] = rand() % 100;
	return tmp;
}

void Print(int* Num) {
	for (int i = 1; i <= Number_Num; i++)
		printf("%d ", Num[i]);
	printf("\n");
}

void Swap(int* a, int* b) {
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

// 一、插入排序(0号为哨兵)

// (1)直接插入排序
void Insert_Sort(int* Num) {

	//1号自己肯定有序 然后把后面的元素往前面有序队列里面插入
	for (int i = 2; i <= Number_Num; i++) {

		//如果新数字比上一个数字小 就把新的数字赋给哨兵 然后把上一个数字移到这个数字的位置
		if (Num[i] < Num[i - 1]) {
			Num[0] = Num[i];

			int pos;
			//寻找应该插入的位置
			for (pos = i - 1; Num[pos] > Num[0]; pos--)
				Num[pos + 1] = Num[pos];

			Num[pos + 1] = Num[0];
		}
	}
}

// (2)折半插入排序(注意看的是high)
void Binary_Insert_Sort(int* Num) {

	for (int i = 2; i <= Number_Num; i++) {
		if (Num[i] < Num[i - 1]) {
			Num[0] = Num[i];
			int high = i - 1, low = 1, mid;

			while (high >= low) {
				mid = (high + low) / 2;
				if (Num[mid] > Num[0])
					high = mid - 1;
				else if (Num[mid] < Num[0])
					low = mid + 1;
				else break;
			}
			for (int j = i; j > high + 1; j--)
				Num[j] = Num[j - 1];

			Num[high + 1] = Num[0];
		}
	}
}

// (3)①希尔排序辅助函数(间距为n的直接插入排序)
void Sup_Shell_Sort(int* Num, int n) {

	for (int i = 1; i <= Number_Num; i++) {
		for (int j = i + n; j <= Number_Num; j += n) {
			if (Num[j] < Num[j - n]) {
				int tmp = Num[j], pos;

				for (pos = j - n; Num[pos] > tmp && pos >= i; pos -= n)
					Num[pos + n] = Num[pos];

				Num[pos + n] = tmp;
			}
		}
	}
}

// (3)②希尔排序
void Shell_Insert_Sort(int* Num) {

	//创建▲序列
	int delt[4] = { 5,3,2,1 };

	for (int i = 0; i < 4; i++)
		Sup_Shell_Sort(Num, delt[i]);
}

//二、交换排序

//(1)冒泡排序
void Bubble_Sort(int* Num) {

	for (int i = 0; i < Number_Num; i++) {
		int pos, min = INT_MAX, flag = 0;
		for (int j = i; j < Number_Num; j++) {
			if (Num[j] < min) {
				flag = 1;
				min = Num[j];
				pos = j;
			}
		}
		if (!flag)
			break;
		Swap(Num + pos, Num + i);
	}
}

// (2)快速排序

// ①快速排序(递归)
void Quick_Sort(int* Num, int head, int tail) {

	if (head >= tail)
		return;

	int i = head, j = tail;

	while (i < j) {
		while (i < j && Num[head] <= Num[j])
			j--;
		while (i < j && Num[head] >= Num[i])
			i++;
		Swap(Num + i, Num + j);
	}
	Swap(Num + head, Num + i);

	Quick_Sort(Num, head, i - 1);
	Quick_Sort(Num, i + 1, tail);
}

// ②快速排序(非递归)
void Quick_Sort2(int* Num) {

	Stack S;
	InitStack(&S);
	Push(&S, 1, Number_Num);

	while (0 != S.tail) {

		StackNode tmp = Pop(&S);

		int mid = (tmp.head + tmp.tail) / 2;
		int i = tmp.head, j = tmp.tail;

		while (i < j) {
			while (i < j && Num[tmp.head] <= Num[j])
				j--;
			while (i < j && Num[tmp.head] >= Num[i])
				i++;
			Swap(Num + i, Num + j);
		}
		Swap(Num + tmp.head, Num + i);

		if (tmp.head < i - 1)
			Push(&S, tmp.head, i - 1);
		if (i + 1 < tmp.tail)
			Push(&S, i + 1, tmp.tail);
	}
}

//三、选择排序
// (1)直接选择排序
void Select_Sort(int* Num) {

	for (int i = 1; i <= Number_Num; i++) {
		int min = INT_MAX, pos;
		for (int j = i; j <= Number_Num; j++) {
			if (Num[j] < min) {
				min = Num[j];
				pos = j;
			}
		}
		Swap(Num + pos, Num + i);
	}
}

// (2)堆排序
//一开始建堆的时候 从n/2 -> 1 之后调整的时候自上而下

// ①调整成小根堆
void Heap_Adjust(int* Num, int n, int max) {

	while (2 * n <= max) {

		//只有左儿子的特殊情况
		if (2 * n + 1 > max) {
			if (Num[2 * n] < Num[n])
				Swap(Num + 2 * n, Num + n);
			break;
		}
		if (Num[n] > Num[2 * n] || Num[n] > Num[2 * n + 1]) {
			if (Num[2 * n] < Num[2 * n + 1]) {
				Swap(Num + 2 * n, Num + n);
				n *= 2;
			}
			else {
				Swap(Num + 2 * n + 1, Num + n);
				n = 2 * n + 1;
			}
		}
		else
			break;
	}
}

// ②堆排序
void Heap_Sort(int* Num) {

	//建立小根堆
	for (int i = Number_Num / 2; i > 0; i--)
		Heap_Adjust(Num, i, Number_Num);

	printf("堆排序后:\t\t");

	//输出堆顶,再继续调整堆
	for (int i = Number_Num; i > 0; i--) {
		printf("%d ", Num[1]);
		Swap(Num + 1, Num + i);
		Heap_Adjust(Num, 1, i - 1);
	}
	printf("\n\n");
}

// 四、归并排序

// 归并(把Num数组上的两组数[head,mid],[mid+1,tail]合并到一个数组pos上)
void Merge(int* Num, int* pos, int head, int mid, int tail) {

	int i = head, j = mid + 1, k = head;
	while (i < mid + 1 && j <= tail) {
		if (Num[i] < Num[j])
			pos[k++] = Num[i++];
		else
			pos[k++] = Num[j++];
	}
	while (i < mid + 1)
		pos[k++] = Num[i++];
	while (k <= tail)
		pos[k++] = Num[j++];
}

//归并排序
void Merge_Sort(int* Num, int* pos, int head, int tail) {

	int tmp[Number_Num + 1];

	if (head == tail) {
		pos[head] = Num[head];
		return;
	}
	Merge_Sort(Num, tmp, head, (head + tail) / 2);
	Merge_Sort(Num, tmp, (head + tail) / 2 + 1, tail);
	Merge(tmp, pos, head, (head + tail) / 2, tail);
}

// 五、基数排序

//分配函数 把串好的静态链表按照关键字大小重新分配(注意还有f、e数组所指也要更新) n代表第几个关键字
void Distribute(SLList S, int n, int* f, int* e) {

	//初始化头尾指针
	for (int i = 0; i < Radix; i++) {
		f[i] = e[i] = 0;
	}

	//S.r[0].next类似于链表的头指针
	for (int p = S.r[0].next; 0 != p; p = S.r[p].next) {
		int tmp = S.r[p].key;
		for (int i = 1; i < n; i++)
			tmp /= 10;
		tmp %= 10;

		//如果此位没有数字
		if (0 == f[tmp]) {
			f[tmp] = p;
			e[tmp] = p;
		}
		else {
			S.r[e[tmp]].next = p;
			e[tmp] = p;
		}
	}
}

//Collect辅助函数 找到n后方第一个非空结点
int Find_Next_Node(int n, int* f) {
	for (int i = n + 1; i < Radix; i++)
		if (0 != f[i])
			return i;
	return 0;
}

//把分散的各个结点重新组合 收集成一个链表
void Collect(SLList S, int* f, int* e) {

	//找到第一个非空结点
	int pre;
	for (pre = 0; pre < Radix; pre++) {
		if (0 != f[pre])
			break;
	}

	//更改头指针
	S.r[0].next = f[pre];
	int cur;

	for (cur = Find_Next_Node(pre, f); 0 != cur; pre = cur, cur = Find_Next_Node(pre, f))
		S.r[e[pre]].next = f[cur];

	//更新尾结点
	S.r[e[pre]].next = 0;

}
//基数排序 注意数字位数要一样 比如都是两位数 都是三位数
void Radix_Sort(int* Num) {

	//建立、初始化静态链表 f、e指针组
	int* f = (int*)malloc(sizeof(int) * Radix), * e = (int*)malloc(sizeof(int) * Radix);
	SLList S;
	S.r = (SLNode*)malloc(sizeof(SLNode) * (1 + Number_Num));

	for (int i = 0; i <= Number_Num; i++) {
		S.r[i].key = Num[i];
		S.r[i].next = i + 1;
	}
	S.r[Number_Num].next = 0;
	S.KeyNum = Number_Num;

	//有几个关键字 就分配收集几次
	for (int i = 1; i <= Max_Key_Num; i++) {
		Distribute(S, i, f, e);
		Collect(S, f, e);
	}
	//更新Num数组
	int tmp = S.r[0].next;
	for (int i = 1; i <= Number_Num; i++) {
		Num[i] = S.r[tmp].key;
		tmp = S.r[tmp].next;
	}
}

int main() {

	srand((unsigned)time(NULL));
	int* Num = Get_Rand_Num();
	printf("数组内的元素为:\t");
	Print(Num);
	printf("插入排序后:\t\t");
	Insert_Sort(Num);
	Print(Num);

	Num = Get_Rand_Num();
	printf("\n数组内的元素为:\t");
	Print(Num);
	Binary_Insert_Sort(Num);
	printf("折半插入排序后:\t");
	Print(Num);

	Num = Get_Rand_Num();
	printf("\n数组内的元素为:\t");
	Print(Num);
	Shell_Insert_Sort(Num);
	printf("希尔排序后:\t\t");
	Print(Num);

	Num = Get_Rand_Num();
	printf("\n数组内的元素为:\t");
	Print(Num);
	Bubble_Sort(Num);
	printf("冒泡排序后:\t\t");
	Print(Num);
	
	Num = Get_Rand_Num();
	printf("\n数组内的元素为:\t");
	Print(Num);
	Quick_Sort(Num, 1, Number_Num);
	printf("快速排序(递归)后:\t");
	Print(Num);

	Num = Get_Rand_Num();
	printf("\n数组内的元素为:\t");
	Print(Num);
	Quick_Sort2(Num);
	printf("快速排序(非递归)后:\t");
	Print(Num);

	Num = Get_Rand_Num();
	printf("\n数组内的元素为:\t");
	Print(Num);
	Select_Sort(Num);
	printf("选择排序后:\t\t");
	Print(Num);

	Num = Get_Rand_Num();
	printf("\n数组内的元素为:\t");
	Print(Num);
	Heap_Sort(Num);
	

	Num = Get_Rand_Num();
	printf("数组内的元素为:\t");
	Print(Num);
	Merge_Sort(Num, Num, 1, Number_Num);
	printf("归并排序后:\t\t");
	Print(Num);

	Num = Get_Rand_Num();
	printf("\n数组内的元素为:\t");
	Print(Num);
	Radix_Sort(Num);
	printf("基数排序后:\t\t");
	Print(Num);

	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值