0
点赞
收藏
分享

微信扫一扫

常见七大排序(附动图及W字详解)(C语言)《数据结构与算法》

目录

1. 排序的概念及其运用 🚀

1.1 排序的概念 🚀

1.2 排序运用 🚀

1.3 常见的排序算法 🚀

2. 常见排序算法的实现 🚀

2.1 插入排序 🚀

2.1.1 基本思想 🚀

2.1.2 直接插入排序 🚀

2.1.3 希尔排序( 缩小增量排序 ) 🚀

希尔排序的特性总结🚀

插入和希尔排序的实现 🚀

2.2 选择排序 🚀

2.2.1 基本思想 🚀

2.2.2 直接选择排序 🚀

直接选择排序的特性总结 🚀

2.2.3 堆排序(点我看前面堆详解博客) 🚀

直接选择排序的特性总结 🚀

直接选择和堆排序的实现 🚀

2.3 交换排序 🚀

2.3.1基本思想 🚀

2.3.2冒泡排序(属于老朋友了是哈哈) 🚀

冒泡排序的特性总结 🚀

2.3.3 快速排序(也算是老朋友了,qsort大家应该用挺多的哈) 🚀

1. hoare版本 🚀

2. 挖坑法 🚀

3. 前后指针版本 🚀

4.非递归法 🚀

2.3.3 快速排序优化 🚀

快速排序的特性总结 🚀

2.4 归并排序 🚀

2.4.1 基本思想 🚀

2.4.2 递归实现 🚀

 2.4.3 非递归实现 🚀

归并排序的特性总结 🚀

3.排序算法复杂度及稳定性分析 🚀

4.源代码 🚀

Sort.h 🚀

Stack.h 🚀

Stack.c 🚀

Sort.c 🚀

test.c 🚀

补充内容🚀


1. 排序的概念及其运用 🚀

1.1 排序的概念 🚀

1.2 排序运用 🚀

1.3 常见的排序算法 🚀

2. 常见排序算法的实现 🚀

2.1 插入排序 🚀

插入和希尔排序的实现 🚀

2.2 选择排序 🚀

直接选择和堆排序的实现 🚀

2.3 交换排序 🚀

2.4 归并排序 🚀

3.排序算法复杂度及稳定性分析 🚀

4.源代码 🚀

Sort.h 🚀

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
#include"Stack.h"


void Print(int* arr, int sz);
//2
void InsertSort(int* arr, int sz);
//3
void ShellSort(int* arr, int sz);
//1
void BubbleSort(int* arr, int sz);
//4
void SelectSort(int* arr, int sz);
//6
void HeapSort(int* arr, int sz);
//5
void QuickSort(int* arr, int begin, int end);

void QuickSortNonR(int* arr, int begin, int end);
//7
void MergeSort(int* arr, int begin, int end);

void MergeSortNonR(int* arr, int n);

Stack.h 🚀

#define _CRT_SECURE_NO_WARNINGS 1

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

typedef int STDateType;

typedef struct Stack {
	STDateType* arr;
	int top;
	int capacity;
}Stack;

//初始化
void InitStack(Stack* pst);
//入栈
void PushStack(Stack* pst, STDateType x);
//出栈
void PopStack(Stack* pst);
//取栈顶
STDateType StackTop(Stack* pst);
//判空
bool EmptyStack(Stack* pst);
//求数据个数
int StackSize(Stack* pst);
//销毁
void DestoryStack(Stack* pst);

Stack.c 🚀

#define _CRT_SECURE_NO_WARNINGS 1

#include"Stack.h"


//初始化
void InitStack(Stack* pst)
{
	//int newcapacity = pst->capacity = 0 ? 4 : pst->capacity * 2;
	Stack* new = (Stack*)malloc(sizeof(Stack));
	if (new == NULL)
	{
		perror("InitStack:");
		exit(-1);
	}
	pst->arr = new;
	pst->capacity = 1;
	pst->top = 0;
}

//入栈
void PushStack(Stack* pst, STDateType x)
{
	assert(pst);
	if (pst->capacity == pst->top)
	{
		int newcapacity = pst->capacity * 2;
		STDateType* new = (STDateType*)realloc(pst->arr,sizeof(STDateType) * newcapacity);
		if (new == NULL)
		{
			perror("InitStack:");
			exit(-1);
		}
		pst->arr = new;
		pst->capacity = newcapacity;
	}
	pst->arr[pst->top] = x;
	pst->top++;
}

//出栈
void PopStack(Stack* pst)
{
	assert(pst);
	assert(pst->top > 0);
	pst->top--;
}

//取栈顶
STDateType StackTop(Stack* pst)
{
	assert(pst);
	assert(pst->top > 0);
	return pst->arr[pst->top - 1];
}

//判空
bool EmptyStack(Stack* pst)
{
	assert(pst);
	return pst->top == 0;
}

//求数据个数
int StackSize(Stack* pst)
{
	assert(pst);
	return pst->top;
}

//销毁
void DestoryStack(Stack* pst)
{
	assert(pst);
	free(pst->arr);
	pst->capacity = 0;
	pst->top = 0;
}

Sort.c 🚀

#define _CRT_SECURE_NO_WARNINGS 1

#include"Sort.h"

//后面用的多,直接写成函数方便一点
void Swap(int* p, int* q)
{
	int tmp = *p;
	*p = *q;
	*q = tmp;
}

void Print(int* arr, int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}


//思想:冒泡其实没啥说的,注意第二个循环的-1,因为我们
//这里用的是arr[j]和arr[j+1]小心越界的问题
void BubbleSort(int* arr,int sz)
{
	int i = 0;
	int j = 0;

	for (i = 0; i < sz; i++)
	{
		for (j = 0; j < sz-i-1; j++)
		{
			if (arr[j] > arr[j + 1])
				Swap(&arr[j], &arr[j + 1]);
		}
	}
}

//思想:开始让第一个数有序,然后从后一个数插入,
//如果后一个数小于前面的数,前面的数就往后挪动
//注意不管是哪种情况退出,最后都是把数据放到
//end+1,所有干脆我们就放在外面
void InsertSort(int* arr, int sz)
{
	for(int i = 0; i < sz - 1;i++)
	{
		int end = i;
		int tmp = arr[end + 1];
		while (end >= 0)
		{
			if (arr[end] > tmp)
			{
				arr[end + 1] = arr[end];
				end--;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = tmp;
	}
}

//思想:先对数组进行预排序,预排序是为了
//直接插入更快,越有序,直接插入就越快,这
//也是希尔快的原因,预排序完了就直接插入排序
//预排序:分组(离相同gap)为一组进行插入排序
void ShellSort(int* arr, int sz)
{
// 1、gap > 1  预排序
// 2、gap == 1 直接插入排序
	int gap = sz;
 	while (gap>1)
	{
		//保证最后为直接插入排序
		gap = gap / 3 + 1;
		//这里i++是使多组预排序同时进行
		for (int i = 0; i < sz - gap; i++)
		{
			int end = i;
			int tmp = arr[end + gap];
			while (end >= 0)
			{
				if (tmp < arr[end])
				{
					arr[end + gap] = arr[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			arr[end + gap] = tmp;
		}
	}
}

//思路:选两个值的下标,开始都在左边,然后用一个记录下标的cur去遍历数组,
// 一个记录最大(maxi),一个记录最小(mini),然后遍历数组把最小的放
// 在左边,最大的,放在右边,直到left,right相遇
void SelectSort(int* arr, int sz)
{
	int left = 0;
	int right = sz - 1;

	while (left<right)
	{
		int cur = left+1;
		int mini = left;
		int maxi = left;
		//这里注意要等于哈,不然最后一个值就比掉了
		//作者开始写的时候就画图搞了半天QAQ
		while (cur <= right)
		{
			if (arr[cur] > arr[maxi])
			{
				maxi = cur;
			}
			if (arr[cur] < arr[mini])
			{
				mini = cur;
			}
			cur++;
		}
		Swap(&arr[left], &arr[mini]);
		//如果left==maxi left就会
		//和mini交换,要更新maxi
		if (left == maxi)
		{
			maxi = mini;
		}
		Swap(&arr[right], &arr[maxi]);
		left++;
		right--;
	}
}

//hoare
//思路:左边为key,右边先走,找小,左边再走,找大,然后左右交换
//一直循环直到相遇,因为是右边先走,左边已经是找小
//停住了,所有能保证相遇位置大于等于a[jeyi],然后
//交换a[keyi]和相遇位置的值,这样a[keyi]左边值就
//比他小,右边值就比他大
int PartSort1(int* arr, int left, int right)
{
	int keyi = left;
	while (left < right)
	{
		//右找小 注意:keyi在左边就右边先走
		//要注意left>right的条件,不然可能越界
		while (left < right && arr[right] >= arr[keyi])
		{
			right--;
		}
		//左找大
		while (left < right && arr[left] <= arr[keyi])
		{
			left++;
		}
		Swap(&arr[left], &arr[right]);
	}
	Swap(&arr[left], &arr[keyi]);
	return left;
}

//挖坑法
//思路:左边为key,先存起来,变成坑,左右两边一个变量
//右边先走,找小,然后交换,左边找大,然后交换
//最后把key的值给相遇位置
int PartSort2(int* a, int left, int right)
{
	int pi = left;
	int qi = right;
	int key = a[left];
	while (pi < qi)
	{
		//右找小
		while (a[qi] > key && pi < qi)
		{
			qi--;

		}
		a[pi] = a[qi];
		//左找大
		while (a[pi] < key && pi < qi)
		{
			pi++;
		}
		a[qi] = a[pi];
	}
	a[qi] = key;
	return qi;
}

int GetMidIndex(int* a, int left, int right)
{
	//int mid = (left + right) / 2;
	int mid = left + (right - left) / 2;
	// left mid right
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if (a[left] > a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
	else // a[left] > a[mid]
	{
		if (a[mid] > a[right])
		{
			return mid;
		}
		else if (a[left] < a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
}

//前后指针法
//左边为keyi,用两个变量存储left和left+1,cur去找小,
//找到了prev++再交换然后cur++,没有找到就cur++,
//这样可以保证让prev到cur的值都大于等于a[keyi]
//最后交换a[keyi]和a[prev]
int PartSort3(int* a, int left, int right)
{
	//yysy,自己实现不加也行
	//下面这两句也是优化,处理了有序O(N^2)的情况
	int midi = GetMidIndex(a, left, right);
	Swap(&a[midi], &a[left]);

	int cur = left + 1;
	int prev = left;
	int keyi = left;
	while (cur <= right)
	{
		//这一步很巧哈,可以多理解一下
		//注意是前置++
		if (a[cur] < a[keyi] && a[++prev] != a[cur])
		{
			Swap(&a[cur], &a[prev]);
		}
		cur++;
	}
	Swap(&a[keyi], &a[prev]);
	return prev;
}

//递归
void QuickSort(int* arr, int begin, int end)
{
	// 子区间相等只有一个值或者不存在那么就是递归结束的子问题
	if (begin >= end)
		return;

	//小区间优化
	// 小区间直接插入排序控制有序
	//yysy,自己实现不加也行
	if (end - begin + 1 <= 10)
	{
		InsertSort(arr + begin, end - begin + 1);
	}
	else
	{
		int keyi = PartSort3(arr, begin, end);
		// [begin, keyi-1]keyi[keyi+1, end]
		QuickSort(arr, begin, keyi - 1);
		QuickSort(arr, keyi + 1, end);
	}
}

//非递归
void QuickSortNonR(int* arr, int begin, int end)
{
	Stack st;
	InitStack(&st);
	PushStack(&st, begin);
	PushStack(&st, end);

	while (!EmptyStack(&st))
	{
		int right = StackTop(&st);
		PopStack(&st);
		int left = StackTop(&st);
		PopStack(&st);
		//left keyi-1  keyi+1 right
		int keyi = PartSort1(arr, left, right);

		if (left < keyi-1)
		{
			PushStack(&st, left);
			PushStack(&st, keyi-1);
		}
		if (keyi + 1 < right)
		{
			PushStack(&st, keyi + 1);
			PushStack(&st, right);
		}
	}
}

//升序建大堆
先找左右小的孩子
//再和root的比  比root小就交换
//root等于那个要交换的孩子 孩子再选  然后迭代
void AdjustDown(int* arr,int root,int sz)
{
	//默认左孩子
	int child = root * 2 + 1;
	int parent = root;
	while (child<sz)
	{
		 1、选出左右孩子中大的那个
		if (child + 1 < sz && arr[child + 1] > arr[child])
			child++;
		//2、如果孩子大于父亲,则交换,并继续往下调整
		if (arr[child] > arr[parent])
		{
			Swap(&arr[parent], &arr[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}


//思想:用向下调整算法建大堆(升序),然后把堆顶的数据
//换到最后,然后缩小数据范围,再次向下调整,反复如此
void HeapSort(int* arr, int sz)
{
	//向下调整--建大堆 O(N)
	int curpos = (sz - 1 - 1) / 2;
	while (curpos >= 0)
	{
		AdjustDown(arr, curpos, sz);
		curpos--;
	}
	
	//end为最后一个元素下标
	int end = sz - 1;
	while (end >0)
	{
		Swap(&arr[0], &arr[end]);
		//每次从0开始向下调
		AdjustDown(arr, 0, end);
		end--;
	}
}

void _MergeSort(int* arr, int* tmp, int begin, int end)
{
	if (begin >= end)
		return;
	//先分后合
	//为了防止数据溢出
	int mid = begin + (end - begin) / 2;
	//注意这里我们分成 begin - mid 和 mid+1 - end
	//分成 begin - mid-1 和 mid - end 有bug (1,2)会死循环
	int begin1 = begin; int end1 = mid;
	int begin2 = mid+1; int end2 = end;
	_MergeSort(arr, tmp, begin1, end1);
	_MergeSort(arr, tmp, begin2, end2);

	//printf("begin1:%d end1:%d \nbegin2:%d end2:%d\n", begin1, end1, begin2, end2);
	
	//开始归
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] < arr[begin2])
			tmp[i++] = arr[begin1++];
		else
			tmp[i++] = arr[begin2++];
	}
	while(begin1 <= end1)
		tmp[i++] = arr[begin1++];
	while (begin2 <= end2)
		tmp[i++] = arr[begin2++];

	//注意第三个参数是字节大小.....开始还以为是上面错了,找半天
	//注意要加上begin 因为并不是每次都是初始位置
	memcpy(arr + begin, tmp + begin, (end - begin + 1)*sizeof(int));
}

//思想:先分再合
//注意我们要先把排号的数据先放进tmp数组,
//然后把数组的数据拷回arr
void MergeSort(int* arr, int begin, int end)
{
	int* tmp = (int*)malloc((end-begin+1) * sizeof(int));
	assert(tmp);

	//避免重复开辟空间就用副本函数
	_MergeSort(arr, tmp, begin, end);

	free(tmp);
}


//思想:不分,直接和,注意和快排转非递归不一样,快排是前序,
//所以转非递归用栈和队列都可以,但是后续不好那么搞,
//我们直接暴力求
void MergeSortNonR(int* arr, int n)
{
	int* tmp = (int*)malloc(n * sizeof(int));
	assert(tmp);

	int gap = 1;
	int begin1 = 0; int end1 = 0;
	int begin2 = 0; int end2 = 0;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)
		{
			begin1 = i; end1 = i + gap - 1;
			begin2 = i + gap; end2 = i + 2 * gap - 1;

			//如果 end1 越界 
			if (end1 >= n)
				end1 = n - 1;
			//如果 begin2 越界 end2 越界
			if (begin2 >= n && end2 >= n)
			{
				begin2 = n;
				end2 = n - 1;
			}
			//如果 end2 越界
			if (begin2 < n && end2 >= n)
				end2 = n - 1;

			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] < arr[begin2])
					tmp[index++] = arr[begin1++];
				else
					tmp[index++] = arr[begin2++];
			}
			while (begin1 <= end1)
				tmp[index++] = arr[begin1++];
			while (begin2 <= end2)
				tmp[index++] = arr[begin2++];
		}
		memcpy(arr,tmp,n*sizeof(int));

		gap *= 2;
	}
	free(tmp);
}

test.c 🚀

#define _CRT_SECURE_NO_WARNINGS 1

#include"Sort.h"

void TestOP()
{
	srand((unsigned int)time(0));
	const int N = 10000000;
	int* a1 = (int*)malloc(sizeof(int) * N);
	assert(a1);
	int* a2 = (int*)malloc(sizeof(int) * N);
	assert(a2);
	int* a3 = (int*)malloc(sizeof(int) * N);
	assert(a3);
	int* a4 = (int*)malloc(sizeof(int) * N);
	assert(a4);
	int* a5 = (int*)malloc(sizeof(int) * N);
	assert(a5);
	int* a6 = (int*)malloc(sizeof(int) * N);
	assert(a6);
	int* a7 = (int*)malloc(sizeof(int) * N);
	assert(a7);

	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];
		a7[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, 0, N-1);
	int end6 = clock();

	int begin7 = clock();
	//BubbleSort(a7, N);
	int end7 = clock();

	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);
	printf("BublleSort:%d\n", end7 - begin7);

	printf("SelectSort:%d\n", end3 - begin3);
	printf("HeapSort:%d\n", end4 - begin4);
	printf("QuickSort:%d\n", end5 - begin5);
	printf("MergeSort:%d\n", end6 - begin6);

	printf("MergeSort:%d\n", end7 - begin7);

	free(a1);
	free(a2);
	free(a3);
	free(a4);
	free(a5);
	free(a6);
	free(a7);
}

void test1()
{
	int arr[] = { 1,7,2,5,3,4,9,8 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	//InsertSort(arr, sz);

	//BubbleSort(arr, sz);  //1

	//SelectSort(arr, sz);

	//QuickSort(arr, 0,sz-1);
	//QuickSortNonR(arr, 0, sz - 1);

	//HeapSort(arr, sz);

	//ShellSort(arr, sz);

	//MergeSort(arr, 0, sz-1);
	//MergeSortNonR(arr, sz);
	Print(arr, sz);
}

int main()
{
	//test1();
	TestOP();
	return 0;
}

补充内容🚀

举报

相关推荐

0 条评论