<排序及模拟实现>——《Data Structure in C Train》

 目录

详细实现链接:

<排序>《数据结构(C语言版)》

<堆及堆排序>《数据结构(C语言版)》

1.分析实现逻辑,学习不同实现思想及写法:

1.1插入排序(直接插入):

1.2 希尔排序:

1.3 堆排序:

1.4 直接选择排序:

1.5 冒泡排序:

 1.6 快速排序:

1.6.1 单趟时间复杂度:O(N)

 1.6.2 快速排序——挖坑法:

 1.6.3 快速排序——挖坑法优化(三数取中)

 1.6.4 快速排序——挖坑法优化(三数取中+小区间优化)

 1.6.5  快速排序——左右指针法

 1.6.6 快速排序——前后指针法

 1.6.7 快速排序——非递归版(排序可复用挖坑法、左右指针法、前后指针法)

 1.7 归并排序

1.7.1   归并排序——递归版:

 1.7.2  归并排序——非递归版:

1.8计数排序

2. 完整源码:

3. 各大排序性能对比分析:

后记:●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                           ——By 作者:新晓·故知


详细实现链接:

<排序>《数据结构(C语言版)》

<堆及堆排序>《数据结构(C语言版)》

1.分析实现逻辑,学习不同实现思想及写法:

1.1插入排序(直接插入):

  • 时间复杂度:O(N^2)
  • 最坏情况:逆序O(N^2)
  • 最好情况:全部顺序 O(N)
  • 空间复杂度:O(1)

  • 稳定

1.2 希尔排序:

  • 时间复杂度:O(N*logN)~ O(N^2)
  • 最好情况:O(N^1.3)
  • 最坏情况:O(N^2)
  • 空间复杂度:O(1)
  • 不稳定

在直接插入排序的基础上优化:

  • 1.先进行预排序(分组排序),让数据接近有序
  • 2.再直接插入排序,

多组间隔为gap的预排序,gap由大变小,

  • gap越大(以升序举例),大的数可以越快的到后面,小的数可以越快的到前面。但预排后越不接近有序
  • gap越小,越接近有序

时间复杂度:O(N*logN)~O(N^2)

  • gap很大时,下面预排序为O(N)
  • gap很小时,数据已接近有序,近似为O(N)

 

1.3 堆排序:

  • 时间复杂度:O(N*logN)
  • 最好情况:O(N*logN)
  • 最坏情况:O(N*logN)
  • 空间复杂度:O(1)
  • 不稳定

给一组数据(假设存储在动态数组里),若要进行堆排序(若打印为升序),且采用向下调整,那么就把这组数据建大堆,再进行调整!然后根据下标遍历数组,即可打印为升序!

  • 大堆:堆顶数据是最大的。
  • 小堆:堆顶元素是最小的。

建堆:

  • 倒数第一个非叶子结点(即叶子父亲的结点)开始调整。
  • 建堆的时间复杂度:O(N)

调整方法:

  • 向上调整法:
  • 向下调整算法:
    • 前提:左右子树必须是小堆。
    • 最多调整高度次:logN次

这里注意:

调整分为:向上调整法、向下调整法。

建堆分为:建大堆、建小堆。

注意:

若将最后的数据按照升序打印,若采用向下调整法,则选择建成大堆(只保证了父结点与子节点的相对大小,相邻没关系,这里只是建堆,而非调整后的最终形态),再从倒数第一个非叶子结点(即叶子父亲的结点)开始调整,最终调整后,再每次输出堆顶元素,打印(因为存储在数组,遍历下标打印,类似于层序遍历的操作)的就是升序!

其实:有4种情况:

(1)建小堆,向上调整,最终为大堆-----用于堆排降序(可以实现,但多此一举!)

(2)建大堆,向上调整,最终为小堆-----用于堆排升序

(3)建小堆,向下调整,最终为大堆-----用于堆排降序(可以实现,但多此一举!)

(4)建大堆,向下调整,最终为小堆-----用于堆排升序

在进行堆排序时,若想数据为升序,则建大堆,采用向下调整法。当把最大的换到最后,就不再将其作为接下来的调整对象。

由于向上调整法的时间复杂度比向下调整法的时间复杂度高,因此一般采用向下调整法。

时间复杂度:

 关于堆的选择题快速判断:

 

(1)建小堆,向上调整,最终为大堆-----用于堆排降序(可以实现,但多此一举!)

(2)建大堆,向上调整,最终为小堆-----用于堆排升序

(3)建小堆,向下调整,最终为大堆-----用于堆排降序(可以实现,但多此一举!)

(4)建大堆,向下调整,最终为小堆-----用于堆排升序

1.4 直接选择排序:

  • 时间复杂度:O(N^2)
  • 最好情况:O(N^2)
  • 最坏情况:O(N^2)
  • 空间复杂度:O(1)
  • 不稳定

1.5 冒泡排序:

  • 时间复杂度:O(N^2)
  • 最好情况:O(N)
  • 最坏情况:O(N^2)
  • 空间复杂度:O(1)
  • 稳定

 1.6 快速排序:

  • 时间复杂度:O(N*logN)
  • 最好情况:O(N*logN)
  • 最坏情况:O(N^2)
  • 空间复杂度:O(logN)~ O(N)
  • 不稳定

1.6.1 单趟时间复杂度:O(N)

 1.6.2 快速排序——挖坑法:

时间复杂度:

 

 1.6.3 快速排序——挖坑法优化(三数取中)

时间复杂度:

 

 1.6.4 快速排序——挖坑法优化(三数取中+小区间优化)

时间复杂度:

 

 1.6.5  快速排序——左右指针法

时间复杂度:

 1.6.6 快速排序——前后指针法

时间复杂度:

 

 1.6.7 快速排序——非递归版(排序可复用挖坑法、左右指针法、前后指针法)

时间复杂度:

 

 1.7 归并排序

  • 时间复杂度:O(N*logN)
  • 最好情况:O(N*logN)
  • 最坏情况:O(N*logN)
  • 空间复杂度:O(N)
  • 稳定

1.7.1   归并排序——递归版:

时间复杂度:

 1.7.2  归并排序——非递归版:

时间复杂度:

 

1.8计数排序

时间复杂度:O(range+N)

空间复杂度:O(range)

 

2. 完整源码:

Sort.h:

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

void PrintArray(int* a, int n);               //打印
void InsertSort(int* a, int n);				  //插入排序
void ShellSort(int* a, int n);                //希尔排序
void HeapSort(int* a, int n);                 //堆排序
void SelectSort(int* a, int n);               //选择排序
void BubbleSort(int* a, int n);               //冒泡排序
//void QuickSort(int* a, int n);              //快速排序——写法1:挖坑法(单趟分析版)
void QuickSort(int* a, int left, int right);  //快速排序——写法1:挖坑法(实现排序版)
void MergeSort(int* a, int n);                //归并排序
void QuickSortNonR(int* a, int n);			  //快速排序(非递归版)
void MergeSortNonR(int* a, int n);            //归并排序(非递归版)
void CountSort(int* a, int n);                //计数排序

Sort.c:

#include"Sort.h"
#include"Stack.h"
//这里采用升序讲解
//打印
void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}
///
//插入排序
//时间复杂度:O(N^2)
//最坏情况:逆序
//最好情况:全部顺序 O(N)
void InsertSort(int* a, int n)
{
	//假设[0,end]有序,将end+1位置的值插入进去,让[0,end+1]有序
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (a[end] > tmp)  //升序
			//if (a[end] < tmp)  //降序
			{
				a[end + 1] = a[end];
				--end;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}
}
//
//希尔排序
//在直接插入排序的基础上优化
//1.先进行预排序(分组排序),让数据接近有序
//2.再直接插入排序,
//多组间隔为gap的预排序,gap由大变小,
//gap越大(以升序举例),大的数可以越快的到后面,小的数可以越快的到前面。但预排后越不接近有序
//gap越小,越接近有序
//时间复杂度:O(logN*N)或者O(log3N*N)
//gap很大时,下面预排序为O(N)
//gap很小时,数据已接近有序,近似为ON)
//平均时间复杂度:O(N^1.3)
void ShellSort(int* a, int n)
{
	int gap = n;  //可以自行设置,但不会给固定的值
	//把间隔为gap的多组数据同时排序(注意:理解这个多组的含义)
	//end最终位置:n-gap-1
	//gap的设置方式之一:
	while(gap > 1)
	{
		//gap=gap/2;        //一定会保证最后一次为1    //运行了logN次
		gap = gap / 3 + 1;  //但要保证最后一次为1      //运行了log3N次
		//gap>1时都是预排序,排序后接近有序
		//gap==1时,就是直接插入排序
		
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}

}

/
//堆排序
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
//(1)建小堆,向上调整,最终为大堆---- - 用于堆排降序
//(2)建大堆,向上调整,最终为小堆---- - 用于堆排升序
//(3)建小堆,向下调整,最终为大堆---- - 用于堆排降序(可以实现,多此一举)
//(4)建大堆,向下调整,最终为小堆---- - 用于堆排升序
//注:由于向上调整法的时间复杂度比向下调整法的时间复杂度高,因此一般采用向下调整法。
//堆排序,采用数组存储,操作下标。但逻辑要是二叉树
//把数据建成堆
//1.堆排序调整-向下调整法
void AdjustDwon(int* a, int n, int root)
{
	int parent = root;
	int child = parent * 2 + 1; // 默认是左孩子
	//1.建大堆,再调整
	while (child < n)
	{
		// 1、选出左右孩子中大的那一个
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child += 1;
		}

		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
	2.建小堆,再调整
	//while (child < n)
	//{
	//	// 1、选出左右孩子中小的那一个
	//	if (child + 1 < n && a[child + 1] < a[child])
	//	{
	//		child += 1;
	//	}

	//	if (a[child] < a[parent])
	//	{
	//		Swap(&a[child], &a[parent]);
	//		parent = child;
	//		child = parent * 2 + 1;
	//	}
	//	else
	//	{
	//		break;
	//	}
	//}
}
//2.堆排序调整-向上调整法
void AdjustUp(int* a, int child)
{
	int parent = (child - 1) / 2;
	//1.建大堆,再调整
	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
	2.建小堆,再调整
	//while (child > 0)
	//{
	//	if (a[child] < a[parent])
	//	{
	//		Swap(&a[child], &a[parent]);
	//		child = parent;
	//		parent = (child - 1) / 2;
	//	}
	//	else
	//	{
	//		break;
	//	}
	//}
}

//堆排序(建堆)
// 升序,建小堆?还是大堆?  -> 大堆
// 建堆只是第一步,并非最终形态,还要进行调整(一般选择向下调整)
// 整体时间复杂度O(N*logN)
void HeapSort(int* a, int n)
{
	// 建堆  时间复杂度:O(N)
	//调整
	1.向上调整法
	//for (int i = 1; i < n; ++i)
	//{
	//	AdjustUp(a, i);
	//}
	//2.向下调整法
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDwon(a, n, i);
	}

	// 排升序,采用向下调整法,那么,建大堆还是小堆? -->建大堆
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDwon(a, end, 0);
		--end;
	}
}
/
//直接选择排序,时间复杂度O(N*N)
// 很差,因为最好情况也是O(N*N)
// N
// N-2
// N-4
// ...
void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int mini = begin, maxi = begin;
		for (int i = begin; i <= end; ++i)
		{
			if (a[i] < a[mini])
			{
				mini = i;
			}

			if (a[i] > a[maxi])
			{
				maxi = i;
			}
		}

		Swap(&a[begin], &a[mini]);
		// 如果begin跟maxi重叠,需要修正一下maxi的位置
		if (begin == maxi)
		{
			maxi = mini;
		}
		Swap(&a[maxi], &a[end]);
		++begin;
		--end;
	}
}
/
//冒泡排序
// 时间复杂度:O(N*N)
// 最好情况:O(N)
// N-1
// N-2
// ...
// 跟直接插入排序相比?谁更好 -> 直接插排序入更好

void BubbleSort(int* a, int n)
{
	//写法1:
	for (int j = 0; j < n; ++j)
	{
		int exchange = 0;
		for (int i = 1; i < n - j; ++i)
		{
			if (a[i - 1] > a[i])
			{
				Swap(&a[i - 1], &a[i]);
				exchange = 1;
			}
		}

		if (exchange == 0)
		{
			break;
		}
	}
	写法2:
	//int end = n;
	//while (end > 0)
	//{
	//	for (int i = 1; i < end; ++i)
	//	{
	//		if (a[i - 1] > a[i])
	//		{
	//			Swap(&a[i - 1], &a[i]);
	//		}
	//	}
	//	--end;
	//}
}
/
快速排序——写法1:挖坑法(单趟分析版)       
 单趟排序时间复杂度:O(N)
//void QuickSort(int* a, int n)    
//{
//	int begin = 0, end = n - 1;
//	int pivot = begin;
//	int key = a[begin];  //指定了第一次关键字为begin
//
//	while (begin < end)
//	{
//		//右边找小,放到左边
//		while (begin < end && a[end] >= key)
//		{
//			--end;
//		}
//		//小的放到左边的坑里,自己形成新的坑位
//		a[pivot] = a[end];
//		pivot = end;
//		//左边找大
//		while (begin < end && a[begin] <= key)
//		{
//			++begin;
//		}
//		//大的放到左边的坑里,自己形成新的坑位
//		a[pivot] = a[begin];
//		pivot = begin;
//
//	}
//	pivot = begin;
//	a[pivot] = key;
//}
快速排序——写法1:挖坑法(实现排序版)
时间复杂度:
最坏情况:有序-->O O(N^2) ,性能相当于插入排序。  解决:“三数取中法”,使其取得key不是最大或最小
//void QuickSort(int* a, int left, int right)
//{
//	if (left >= right)
//		return;
//	int begin = left, end = right;
//	int pivot = begin;
//	int key = a[begin];  //指定了第一次关键字为begin
//
//	while (begin < end)   //单趟排序
//	{
//		//右边找小,放到左边
//		while (begin < end && a[end] >= key)
//		{
//			--end;
//		}
//		//小的放到左边的坑里,自己形成新的坑位
//		a[pivot] = a[end];
//		pivot = end;
//		//左边找大
//		while (begin < end && a[begin] <= key)
//		{
//			++begin;
//		}
//		//大的放到左边的坑里,自己形成新的坑位
//		a[pivot] = a[begin];
//		pivot = begin;
//
//	}
//	pivot = begin;
//	a[pivot] = key;
//
//	// [left, right]
//	// [left, pivot-1] pivot [pivot+1, right]	
//	// 左子区间和右子区间有序,我们就有序了,如果让他们有序呢? 分治递归
//	QuickSort(a, left, pivot - 1);
//	QuickSort(a, pivot + 1, right);
//}
//
快速排序——写法2:挖坑法优化--->(三数取中,解决原始数据有序情况)
 三数取中
//int GetMidIndex(int* a, int left, int right)
//{
//	int mid = (left + right) >> 1;
//	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;
//		}
//	}
//}
//void QuickSort(int* a, int left, int right)
//{
//	if (left >= right)
//		return;
//	int keyIndex = GetMidIndex(a, left, right);  //三数取中,解决原始数据有序的性能下降
//	Swap(&a[left], &a[keyIndex]);
//
//	int begin = left, end = right;
//	int pivot = begin;
//	int key = a[begin];
//
//	while (begin < end)  //单趟排序  O(N)
//	{
//		// 右边找小,放到左边
//		while (begin < end && a[end] >= key)
//			--end;
//
//		// 小的放到左边的坑里,自己形成新的坑位
//		a[pivot] = a[end];
//		pivot = end;
//
//		// 左边找大
//		while (begin < end && a[begin] <= key)
//			++begin;
//
//		// 大的放到左边的坑里,自己形成新的坑位
//		a[pivot] = a[begin];
//		pivot = begin;
//	}
//
//	pivot = begin;
//	a[pivot] = key;
//
//	// [left, right]
//	// [left, pivot-1] pivot [pivot+1, right]	
//	// 左子区间和右子区间有序,我们就有序了,如果让他们有序呢? 分治递归
//	QuickSort(a, left, pivot - 1);
//	QuickSort(a, pivot + 1, right);
//}

快速排序——写法3:挖坑法优化--->(三数取中,解决原始数据有序情况),再根据处理量分情况排序(小区间优化)
 三数取中
//int GetMidIndex(int* a, int left, int right)
//{
//	int mid = (left + right) >> 1;
//	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;
//		}
//	}
//}
//void QuickSort(int* a, int left, int right)
//{
//	if (left >= right)
//		return;
//	int keyIndex = GetMidIndex(a, left, right);
//	Swap(&a[left], &a[keyIndex]);
//
//	int begin = left, end = right;
//	int pivot = begin;
//	int key = a[begin];
//
//	// O(N)
//	while (begin < end)
//	{
//		// 右边找小,放到左边
//		while (begin < end && a[end] >= key)
//			--end;
//
//		// 小的放到左边的坑里,自己形成新的坑位
//		a[pivot] = a[end];
//		pivot = end;
//
//		// 左边找大
//		while (begin < end && a[begin] <= key)
//			++begin;
//
//		// 大的放到左边的坑里,自己形成新的坑位
//		a[pivot] = a[begin];
//		pivot = begin;
//	}
//
//	pivot = begin;
//	a[pivot] = key;
//
//	// 小区间优化
//	//这个值会随原始数据量改变,而官方给13左右
//	if (pivot - 1 - left > 10)   //官方给13左右
//	{
//		QuickSort(a, left, pivot - 1);
//	}
//	else
//	{
//		//InsertSort(int* a, int n)
//		InsertSort(a + left, pivot - 1 - left + 1);  //left不一定从0开始,传数据个数
//	}
//
//	if (right - (pivot + 1) > 10)
//	{
//		QuickSort(a, pivot + 1, right);
//	}
//	else
//	{
//		InsertSort(a + pivot + 1, right - (pivot + 1) + 1);
//	}
//}

快速排序——写法4:左右指针法:找小找大交换
 三数取中
//int GetMidIndex(int* a, int left, int right)
//{
//	int mid = (left + right) >> 1;
//	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;
//		}
//	}
//}
左右指针法:找小找大交换
//void QuickSort(int* a, int left, int right)
//{
//	if (left >= right)
//		return;
//
//	int index = GetMidIndex(a, left, right);
//	Swap(&a[left], &a[index]);
//
//	int begin = left, end = right;
//	int keyi = begin;
//
//	while (begin < end)
//	{
//		// 找小
//		while (begin < end && a[end] >= a[keyi])
//		{
//			--end;
//		}
//
//		// 找大
//		while (begin < end && a[begin] <= a[keyi])
//		{
//			++begin;
//		}
//
//		Swap(&a[begin], &a[end]);
//	}
//
//	Swap(&a[begin], &a[keyi]);
//	int key = begin;
//
//	// 小区间优化
//	//这个值会随原始数据量改变,而官方给13左右
//	if (key - 1 - left > 10)   //官方给13左右
//	{
//		QuickSort(a, left, key - 1);
//	}
//	else
//	{
//		//InsertSort(int* a, int n)
//		InsertSort(a + left, key - 1 - left + 1);  //left不一定从0开始,传数据个数
//	}
//
//	if (right - (key + 1) > 10)
//	{
//		QuickSort(a, key + 1, right);
//	}
//	else
//	{
//		InsertSort(a + key + 1, right - (key + 1) + 1);
//	}
//}

//快速排序——写法5:前后指针法:cur找小,每次找到比keyi小额值,就++prev,然后交换prev与cur的位置交换
//1.会有自己与自己交换的情况,2.也有异步交换,3.最后实现左边全为小于key,右边全为大于key
// 三数取中
int GetMidIndex(int* a, int left, int right)
{
	int mid = (left + right) >> 1;
	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;
		}
	}
}
//前后指针法
void QuickSort(int* a, int left, int right)
{
	if (left >= right)
		return;

	int index = GetMidIndex(a, left, right);
	Swap(&a[left], &a[index]);

	int keyi = left;
	int prev = left, cur = left + 1;
	while (cur <= right)
	{
		if (a[cur] < a[keyi]
			&& ++prev != cur)   //++prev != cur:优化自己与自己交换的情况
		{
			Swap(&a[prev], &a[cur]);
		}

		++cur;
	}

	Swap(&a[keyi], &a[prev]);

	int key = prev;
	// 小区间优化
	//这个值会随原始数据量改变,而官方给13左右
	if (key - 1 - left > 10)   //官方给13左右
	{
		QuickSort(a, left, key - 1);
	}
	else
	{
		//InsertSort(int* a, int n)
		InsertSort(a + left, key - 1 - left + 1);  //left不一定从0开始,传数据个数
	}

	if (right - (key + 1) > 10)
	{
		QuickSort(a, key + 1, right);
	}
	else
	{
		InsertSort(a + key + 1, right - (key + 1) + 1);
	}
}

//递归的缺陷:若递归的深度太深,程序没错,但是栈的空间不够用,会导致溢出
//递归改非递归:方法1:(简单的)可以直接改循环 2.(复杂一点)使用数据结构的栈进行模拟递归过程
//快速排序——非递归版
//(复杂一点)使用数据结构的栈进行模拟递归过程:不会再有栈溢出问题,也会有空间消耗
//但数据结构中的栈是malloc出来的,开辟在堆(操作系统对内存的划分),而堆的空间比栈大
void QuickSortNonR(int* a, int n)
{
	ST st;
	StackInit(&st);
	StackPush(&st, n - 1);
	StackPush(&st, 0);
	while (!StackEmpty(&st))
	{
		int left = StackTop(&st);
		StackPop(&st);
		int right = StackTop(&st);
		StackPop(&st);
		//调用单趟排序
		int index = GetMidIndex(a, left, right);
		//Swap(&a[left], &a[index]);

		int keyi = left;
		int prev = left, cur = left + 1;
		while (cur <= right)
		{
			if (a[cur] < a[keyi]
				&& ++prev != cur)   //++prev != cur:优化自己与自己交换的情况
			{
				Swap(&a[prev], &a[cur]);
			}

			++cur;
		}

		Swap(&a[keyi], &a[prev]);
		int keyIndex = prev;

		//栈里面的就需要单趟分割排序
		//[left,keyIndex-1] keyIndex [keyIndex+1,right]
		if (keyIndex + 1 < right)
		{
			StackPush(&st, right);
			StackPush(&st, keyIndex+1);

		}
		if (left < keyIndex - 1)
		{
			StackPush(&st, keyIndex - 1);
			StackPush(&st, left);
		}
	}
	StackDestory(&st);

}
///
//归并排序
//1.假设左半区间、右半区间有序,依次对比取小的放到新的临时数组
//2.若左半区间、右半区间没有序,则递归进行
void _MergeSort(int* a, int left, int right, int* tmp)
{
	if (left >= right)
		return;

	int mid = (left + right) >> 1;
	//假设 [left, mid] [mid+1, right]有序,那么我们就可以归并了
	_MergeSort(a, left, mid, tmp);
	_MergeSort(a, mid + 1, right, tmp);

	// 归并
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	int index = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			tmp[index++] = a[begin1++];
		}
		else
		{
			tmp[index++] = a[begin2++];
		}
	}

	while (begin1 <= end1)
	{
		tmp[index++] = a[begin1++];
	}

	while (begin2 <= end2)
	{
		tmp[index++] = a[begin2++];
	}

	// 拷贝回去
	for (int i = left; i <= right; ++i)
	{
		a[i] = tmp[i];
	}
}

void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n); //空间复杂度为O(N)
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
}
//归并排序——非递归版
//(复杂一点)使用数据结构的栈进行模拟递归过程
//归并排序的非递归:先相邻两两归并,再四四归并,依次扩大
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	int gap = 1;  //每组数据个数
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)
		{
			//[i,i+gap-1] [i+gap,i+2*gap-1]

			// 归并
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			//归并过程中右半区间可能就不存在
			if (begin2 >= n)
				break;
			//归并过程中右半区间算多了,修正
			if (end2 >= n)
			{
				end2 = n - 1;
			}
			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[index++] = a[begin1++];
				}
				else
				{
					tmp[index++] = a[begin2++];
				}
			}

			while (begin1 <= end1)
			{
				tmp[index++] = a[begin1++];
			}

			while (begin2 <= end2)
			{
				tmp[index++] = a[begin2++];
			}
			// 拷贝回去
			for (int j = i; j < end2; ++j)
			{
				a[j] = tmp[j];
			}
		}
		gap *= 2;
	}
	free(tmp);
}
//归并排序,也叫外排序,还可以对文件中数据进行排序
//假设10G的数据放到硬盘中,要排序可能内存不够,假设有1G内存可以用,如何排序?
//依次读文件,每次读1G到内存中放到一个数组,用快排对其进行排序,再写到一个文件,2G、4G、8G归并需要借助在磁盘归并
//磁盘只能依次读数据

//非比较排序
//
//基数排序(桶排序)实际中运用少,只对整数排序
//
//计数排序
//统计数据个数,不进行比较
//时间复杂度:O(range+N)
//空间复杂度:O(range)
//说明计数排序适用于范围集中的数据
//使用相对映射,也可对负数进行排序
//字符串、浮点数等等不行
void CountSort(int* a, int n)
{
	int min = a[0], max = a[0];
	for (int i = 1; i < n; ++i)
	{
		if (a[i] < min)
			min = a[i];
		if (a[i] > max)
			max = a[i];
	}

	int range = max - min + 1;
	int* countA = (int*)malloc(sizeof(int) * range);
	assert(countA);

	memset(countA, 0, sizeof(int) * range);
	//计数
	for (int i = 0; i < n; ++i)
		countA[a[i] - min]++;
	//排序
	int j = 0;
	for (int i = 0; i < range; ++i)
	{
		while (countA[i]--)
		{
			a[j++] = i + min;
		}
	}
}

Test.c:

#include"Sort.h"
#include"Stack.h"

void TestInsertSort()
{
	int a[] = { 3, 5, 2, 7, 8, 6, 1, 9, 4, 0 };
	PrintArray(a, sizeof(a) / sizeof(int));
	InsertSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}
void TestShellSort()
{
	int a[] = { 3, 5, 2, 7, 8, 6, 1, 9, 4, 0 };
	PrintArray(a, sizeof(a) / sizeof(int));
	ShellSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}
void TestHeapSort()
{
	int a[] = { 3, 5, 2, 7, 8, 6, 1, 9, 4, 0 }; 
	PrintArray(a, sizeof(a) / sizeof(int));
	HeapSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}
void TestSelectSort()
{
	int a[] = { 9, 3, 5, 2, 7, 8, 6, -1, 9, 4, 0 };
	PrintArray(a, sizeof(a) / sizeof(int));
	SelectSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}
void TestBubbleSort()
{
	int a[] = { 9, 3, 5, 2, 7, 8, 6, -1, 9, 4, 0 };
	PrintArray(a, sizeof(a) / sizeof(int));
	BubbleSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}
void TestQuickSort()
{
	int a[] = { 6, 3, 5, 2, 7, 8, 9, 4, 1 };
	//int a[] = { 49, 38, 65, 97, 76, 13, 27, 49};
	//int a[] = { 49, 38, 65, 97, 76, 13, 27, 49, 13, 27, 49 };
	PrintArray(a, sizeof(a) / sizeof(int));
	//QuickSort(a, sizeof(a) / sizeof(int) );    //快速排序——写法1:挖坑法(单趟分析版)
	QuickSort(a, 0, sizeof(a) / sizeof(int)-1);  //快速排序——写法1:挖坑法(实现排序版)
	PrintArray(a, sizeof(a) / sizeof(int));
}
void TestMergeSort()
{
	//int a[] = { 10, 6, 7 ,1, 3, 9, 4, 2 };
	int a[] = { 49, 38, 65, 97, 76, 13, 27, 49, 13, 27, 49 };
	PrintArray(a, sizeof(a) / sizeof(int));
	MergeSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}
void TestQuickSortNonR()
{
	//int a[] = { 6, 3, 5, 2, 7, 8, 9, 4, 1 };
	//int a[] = { 49, 38, 65, 97, 76, 13, 27, 49};
	int a[] = { 49, 38, 65, 97, 76, 13, 27, 49, 13, 27, 49 };
	PrintArray(a, sizeof(a) / sizeof(int));
	QuickSortNonR(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}
void TestMergeSortNonR()
{
	//int a[] = { 10, 6, 7 ,1, 3, 9, 4, 2 };
	int a[] = { 49, 38, 65, 97, 76, 13, 27, 49, 13, 27, 49 };
	PrintArray(a, sizeof(a) / sizeof(int));
	MergeSortNonR(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}
void TestCountSort()
{
	//int a[] = { 10, 6, 7 ,1, 3, 9, 4, 2 };
	int a[] = { 49, 38, 65, 97, 76, 13, 27, 49, 13, 27, 49 };
	PrintArray(a, sizeof(a) / sizeof(int));
	CountSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}
int main()
{
	//TestInsertSort();
	//TestShellSort();
	//TestHeapSort();
	//TestSelectSort();
	//TestBubbleSort();
	//TestQuickSort();
	//TestMergeSort();
	//TestQuickSortNonR();
	//TestMergeSortNonR();
	TestCountSort();
	return 0;
}

 注:

由于这里的快速排序——非递归版,使用数据结构的栈进行模拟递归过程。因此要附用栈的实现代码。这里将栈的代码给出,使用时包含头文件,再复用即可!

Stack.h:

#pragma once

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

typedef char STDataType;

typedef struct Stack
{
	STDataType* a;   //通过数组实现栈的结构
	int top;
	int capacity;
}ST;
//初始化
void StackInit(ST* ps);
//释放内存、销毁空间
void StackDestory(ST* ps);
// 入栈
void StackPush(ST* ps, STDataType x);
// 出栈
void StackPop(ST* ps);
//取栈顶数据
STDataType StackTop(ST* ps);
//栈的大小
int StackSize(ST* ps);
//判空
bool StackEmpty(ST* ps);

Stack.c:

#include"Stack.h"
//初始化
void StackInit(ST* ps)
{
	assert(ps);

	ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
	if (ps->a == NULL)
	{
		printf("malloc fail!\n");
		exit(-1);
	}

	ps->capacity = 4;
	ps->top = 0; //这使得top最终指向的是栈顶的后一个位置。若top=-1,则最终指向的是栈顶。
}
//释放内存、销毁空间
void StackDestory(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}
// 入栈
void StackPush(ST* ps, STDataType x)
{
	assert(ps);

	// 满了->增容
	if (ps->top == ps->capacity)
	{
		STDataType* tmp = (STDataType*)realloc(ps->a, ps->capacity * 2 * sizeof(STDataType));
		if (tmp == NULL)
		{
			printf("realloc fail!\n");
			exit(-1);
		}
		else
		{
			ps->a = tmp;
			ps->capacity *= 2;
		}
	}

	ps->a[ps->top] = x;
	ps->top++;
}

// 出栈
void StackPop(ST* ps)
{
	assert(ps);
	// 栈空了,再调用Pop,就会直接中止程序报错
	assert(ps->top > 0);

	//ps->a[ps->top - 1] = 0; //置为0只考虑了int型等,若为char、double等就不适用了。
	ps->top--;
}
//取栈顶数据
STDataType StackTop(ST* ps)
{
	assert(ps);
	// 栈空了,再调用Top,就会直接中止程序报错
	assert(ps->top > 0);

	return ps->a[ps->top - 1];
}
//求栈大小
int StackSize(ST* ps)
{
	assert(ps);

	return ps->top;
}
//判空
bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}

//判断括号是否匹配算法
bool isValid(char* s) {
	ST st;
	StackInit(&st);
	while (*s != '\0')
	{
		switch (*s)
		{
		case '{':
		case '[':
		case '(':
		{
			StackPush(&st, *s);
			++s;
			break;
		}
		case '}':
		case ']':
		case ')':
		{
			if (StackEmpty(&st))
			{
				StackDestory(&st);
				return false;
			}

			char top = StackTop(&st);
			StackPop(&st);

			// 不匹配
			if ((*s == '}' && top != '{')
				|| (*s == ']' && top != '[')
				|| (*s == ')' && top != '('))
			{
				StackDestory(&st);
				return false;
			}
			else // 匹配
			{
				++s;
			}
			break;
		}
		default:
			break;
		}
	}
	bool ret = StackEmpty(&st);
	StackDestory(&st);
	return ret;
}

3. 各大排序性能对比分析:

 1. 堆排序性能对比测试:

测试代码:

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

/
//堆排序对比测试:
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
//(1)建小堆,向上调整,最终为大堆---- - 用于堆排降序
//1.堆排序调整-向下调整法
void AdjustDwon1(int* a, int n, int root)
{
	int parent = root;
	int child = parent * 2 + 1; // 默认是左孩子
	
	//建小堆,再调整
	while (child < n)
	{
		// 1、选出左右孩子中小的那一个
		if (child + 1 < n && a[child + 1] < a[child])
		{
			child += 1;
		}

		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//2.堆排序调整-向上调整法
void AdjustUp1(int* a, int child)
{
	int parent = (child - 1) / 2;
	//建小堆,再调整
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//堆排序(建堆)
void HeapSort1(int* a, int n)
{
	//建堆 调整
	//向上调整法
	for (int i = 1; i < n; ++i)
	{
		AdjustUp1(a, i);
	}
	
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDwon1(a, end, 0);
		--end;
	}
}

//(2)建大堆,向上调整,最终为小堆---- - 用于堆排升序
//1.堆排序调整-向下调整法
void AdjustDwon2(int* a, int n, int root)
{
	int parent = root;
	int child = parent * 2 + 1; // 默认是左孩子
	//建大堆,再调整
	while (child < n)
	{
		// 1、选出左右孩子中大的那一个
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child += 1;
		}

		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//2.堆排序调整-向上调整法
void AdjustUp2(int* a, int child)
{
	int parent = (child - 1) / 2;
	//1.建大堆,再调整
	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//堆排序(建堆)
void HeapSort2(int* a, int n)
{
	// 建堆  调整
	//向上调整法
	for (int i = 1; i < n; ++i)
	{
		AdjustUp2(a, i);
	}
	
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDwon2(a, end, 0);
		--end;
	}
}

//(3)建小堆,向下调整,最终为大堆---- - 用于堆排降序(可以实现,多此一举)
//1.堆排序调整-向下调整法
void AdjustDwon3(int* a, int n, int root)
{
	int parent = root;
	int child = parent * 2 + 1; // 默认是左孩子
	
	//建小堆,再调整
	while (child < n)
	{
		// 1、选出左右孩子中小的那一个
		if (child + 1 < n && a[child + 1] < a[child])
		{
			child += 1;
		}

		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//2.堆排序调整-向上调整法
void AdjustUp3(int* a, int child)
{
	int parent = (child - 1) / 2;
	
	//建小堆,再调整
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//堆排序(建堆)
void HeapSort3(int* a, int n)
{
	// 建堆 调整
	//向下调整法
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDwon3(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDwon3(a, end, 0);
		--end;
	}
}

//(4)建大堆,向下调整,最终为小堆---- - 用于堆排升序
//1.堆排序调整-向下调整法
void AdjustDwon4(int* a, int n, int root)
{
	int parent = root;
	int child = parent * 2 + 1; // 默认是左孩子
	//建大堆,再调整
	while (child < n)
	{
		// 1、选出左右孩子中大的那一个
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child += 1;
		}

		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//2.堆排序调整-向上调整法
void AdjustUp4(int* a, int child)
{
	int parent = (child - 1) / 2;
	//建大堆,再调整
	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//堆排序(建堆)
void HeapSort4(int* a, int n)
{
	// 建堆 调整
	//向下调整法
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDwon4(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDwon4(a, end, 0);
		--end;
	}
}

void TestOP()
{
	srand(time(0));
	const int N = 100000;
	int* a1 = (int*)malloc(sizeof(int) * N);
	int* a2 = (int*)malloc(sizeof(int) * N);
	int* a3 = (int*)malloc(sizeof(int) * N);
	int* a4 = (int*)malloc(sizeof(int) * N);

	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand();
		//a1[i] = i;
		a2[i] = a1[i];
		a3[i] = a1[i];
		a4[i] = a1[i];

	}

	int begin1 = clock();
	HeapSort1(a1, N);
	int end1 = clock();

	int begin2 = clock();
	HeapSort2(a2, N);
	int end2 = clock();

	int begin3 = clock();
	HeapSort3(a3, N);
	int end3 = clock();

	int begin4 = clock();
	HeapSort4(a4, N);
	int end4 = clock();


	printf("建小堆-向上调整-降序:%d\n", end1 - begin1);
	printf("建大堆-向上调整-升序:%d\n", end2 - begin2);
	printf("建小堆-向下调整-降序:%d\n", end3 - begin3);
	printf("建大堆-向下调整-升序:%d\n", end4 - begin4);

	free(a1);
	free(a2);
	free(a3);
	free(a4);
}

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

 2.快速排序(递归版)性能对比测试:

 3.各大排序测试对比代码

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"Stack.h"
/
//堆排序对比测试:
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
//(1)建小堆,向上调整,最终为大堆---- - 用于堆排降序
//1.堆排序调整-向下调整法
void AdjustDwon1(int* a, int n, int root)
{
	int parent = root;
	int child = parent * 2 + 1; // 默认是左孩子
	
	//建小堆,再调整
	while (child < n)
	{
		// 1、选出左右孩子中小的那一个
		if (child + 1 < n && a[child + 1] < a[child])
		{
			child += 1;
		}

		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//2.堆排序调整-向上调整法
void AdjustUp1(int* a, int child)
{
	int parent = (child - 1) / 2;
	//建小堆,再调整
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//堆排序(建堆)
void HeapSort1(int* a, int n)
{
	//建堆 调整
	//向上调整法
	for (int i = 1; i < n; ++i)
	{
		AdjustUp1(a, i);
	}
	
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDwon1(a, end, 0);
		--end;
	}
}

//(2)建大堆,向上调整,最终为小堆---- - 用于堆排升序
//1.堆排序调整-向下调整法
void AdjustDwon2(int* a, int n, int root)
{
	int parent = root;
	int child = parent * 2 + 1; // 默认是左孩子
	//建大堆,再调整
	while (child < n)
	{
		// 1、选出左右孩子中大的那一个
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child += 1;
		}

		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//2.堆排序调整-向上调整法
void AdjustUp2(int* a, int child)
{
	int parent = (child - 1) / 2;
	//1.建大堆,再调整
	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//堆排序(建堆)
void HeapSort2(int* a, int n)
{
	// 建堆  调整
	//向上调整法
	for (int i = 1; i < n; ++i)
	{
		AdjustUp2(a, i);
	}
	
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDwon2(a, end, 0);
		--end;
	}
}

//(3)建小堆,向下调整,最终为大堆---- - 用于堆排降序(可以实现,多此一举)
//1.堆排序调整-向下调整法
void AdjustDwon3(int* a, int n, int root)
{
	int parent = root;
	int child = parent * 2 + 1; // 默认是左孩子
	
	//建小堆,再调整
	while (child < n)
	{
		// 1、选出左右孩子中小的那一个
		if (child + 1 < n && a[child + 1] < a[child])
		{
			child += 1;
		}

		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//2.堆排序调整-向上调整法
void AdjustUp3(int* a, int child)
{
	int parent = (child - 1) / 2;
	
	//建小堆,再调整
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//堆排序(建堆)
void HeapSort3(int* a, int n)
{
	// 建堆 调整
	//向下调整法
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDwon3(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDwon3(a, end, 0);
		--end;
	}
}

//(4)建大堆,向下调整,最终为小堆---- - 用于堆排升序
//1.堆排序调整-向下调整法
void AdjustDwon4(int* a, int n, int root)
{
	int parent = root;
	int child = parent * 2 + 1; // 默认是左孩子
	//建大堆,再调整
	while (child < n)
	{
		// 1、选出左右孩子中大的那一个
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child += 1;
		}

		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//2.堆排序调整-向上调整法
void AdjustUp4(int* a, int child)
{
	int parent = (child - 1) / 2;
	//建大堆,再调整
	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//堆排序(建堆)
void HeapSort4(int* a, int n)
{
	// 建堆 调整
	//向下调整法
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDwon4(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDwon4(a, end, 0);
		--end;
	}
}

//插入排序
//时间复杂度:O(N^2)
//最坏情况:逆序
//最好情况:全部顺序 O(N)
void InsertSort(int* a, int n)
{
	//假设[0,end]有序,将end+1位置的值插入进去,让[0,end+1]有序
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (a[end] > tmp)  //升序
			//if (a[end] < tmp)  //降序
			{
				a[end + 1] = a[end];
				--end;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}
}
//快速排序——写法1:挖坑法(实现排序版)
void QuickSort1(int* a, int left, int right)
{
	if (left >= right)
		return;
	int begin = left, end = right;
	int pivot = begin;
	int key = a[begin];  //指定了第一次关键字为begin

	while (begin < end)   //单趟排序
	{
		//右边找小,放到左边
		while (begin < end && a[end] >= key)
		{
			--end;
		}
		//小的放到左边的坑里,自己形成新的坑位
		a[pivot] = a[end];
		pivot = end;
		//左边找大
		while (begin < end && a[begin] <= key)
		{
			++begin;
		}
		//大的放到左边的坑里,自己形成新的坑位
		a[pivot] = a[begin];
		pivot = begin;

	}
	pivot = begin;
	a[pivot] = key;

	// [left, right]
	// [left, pivot-1] pivot [pivot+1, right]	
	// 左子区间和右子区间有序,我们就有序了,如果让他们有序呢? 分治递归
	QuickSort1(a, left, pivot - 1);
	QuickSort1(a, pivot + 1, right);
}


//快速排序——写法2:挖坑法优化--->(三数取中,解决原始数据有序情况),再根据处理量分情况排序(小区间优化)
// 三数取中
int GetMidIndex(int* a, int left, int right)
{
	int mid = (left + right) >> 1;
	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;
		}
	}
}
void QuickSort2(int* a, int left, int right)
{
	if (left >= right)
		return;
	int keyIndex = GetMidIndex(a, left, right);
	Swap(&a[left], &a[keyIndex]);

	int begin = left, end = right;
	int pivot = begin;
	int key = a[begin];

	// O(N)
	while (begin < end)
	{
		// 右边找小,放到左边
		while (begin < end && a[end] >= key)
			--end;

		// 小的放到左边的坑里,自己形成新的坑位
		a[pivot] = a[end];
		pivot = end;

		// 左边找大
		while (begin < end && a[begin] <= key)
			++begin;

		// 大的放到左边的坑里,自己形成新的坑位
		a[pivot] = a[begin];
		pivot = begin;
	}

	pivot = begin;
	a[pivot] = key;

	// 小区间优化
	//这个值会随原始数据量改变,而官方给13左右
	if (pivot - 1 - left > 10)   //官方给13左右
	{
		QuickSort2(a, left, pivot - 1);
	}
	else
	{
		//InsertSort(int* a, int n)
		InsertSort(a + left, pivot - 1 - left + 1);  //left不一定从0开始,传数据个数
	}

	if (right - (pivot + 1) > 10)
	{
		QuickSort2(a, pivot + 1, right);
	}
	else
	{
		InsertSort(a + pivot + 1, right - (pivot + 1) + 1);
	}
}

//快速排序——写法3:左右指针法:找小找大交换
void QuickSort3(int* a, int left, int right)
{
	if (left >= right)
		return;

	int index = GetMidIndex(a, left, right);
	Swap(&a[left], &a[index]);

	int begin = left, end = right;
	int keyi = begin;

	while (begin < end)
	{
		// 找小
		while (begin < end && a[end] >= a[keyi])
		{
			--end;
		}

		// 找大
		while (begin < end && a[begin] <= a[keyi])
		{
			++begin;
		}

		Swap(&a[begin], &a[end]);
	}

	Swap(&a[begin], &a[keyi]);
	int key = begin;

	// 小区间优化
	//这个值会随原始数据量改变,而官方给13左右
	if (key - 1 - left > 10)   //官方给13左右
	{
		QuickSort3(a, left, key - 1);
	}
	else
	{
		//InsertSort(int* a, int n)
		InsertSort(a + left, key - 1 - left + 1);  //left不一定从0开始,传数据个数
	}

	if (right - (key + 1) > 10)
	{
		QuickSort3(a, key + 1, right);
	}
	else
	{
		InsertSort(a + key + 1, right - (key + 1) + 1);
	}
}

//快速排序——写法4:前后指针法:cur找小,每次找到比keyi小额值,就++prev,然后交换prev与cur的位置交换
//1.会有自己与自己交换的情况,2.也有异步交换,3.最后实现左边全为小于key,右边全为大于key

void QuickSort4(int* a, int left, int right)
{
	if (left >= right)
		return;

	int index = GetMidIndex(a, left, right);
	Swap(&a[left], &a[index]);

	int keyi = left;
	int prev = left, cur = left + 1;
	while (cur <= right)
	{
		if (a[cur] < a[keyi]
			&& ++prev != cur)   //++prev != cur:优化自己与自己交换的情况
		{
			Swap(&a[prev], &a[cur]);
		}

		++cur;
	}

	Swap(&a[keyi], &a[prev]);

	int key = prev;
	// 小区间优化
	//这个值会随原始数据量改变,而官方给13左右
	if (key - 1 - left > 10)   //官方给13左右
	{
		QuickSort4(a, left, key - 1);
	}
	else
	{
		//InsertSort(int* a, int n)
		InsertSort(a + left, key - 1 - left + 1);  //left不一定从0开始,传数据个数
	}

	if (right - (key + 1) > 10)
	{
		QuickSort4(a, key + 1, right);
	}
	else
	{
		InsertSort(a + key + 1, right - (key + 1) + 1);
	}
}
//快速排序——非递归版
//(复杂一点)使用数据结构的栈进行模拟递归过程:不会再有栈溢出问题,也会有空间消耗
//但数据结构中的栈是malloc出来的,开辟在堆(操作系统对内存的划分),而堆的空间比栈大
void QuickSortNonR(int* a, int n)
{
	ST st;
	StackInit(&st);
	StackPush(&st, n - 1);
	StackPush(&st, 0);
	while (!StackEmpty(&st))
	{
		int left = StackTop(&st);
		StackPop(&st);
		int right = StackTop(&st);
		StackPop(&st);
		//调用单趟排序
		int index = GetMidIndex(a, left, right);
		//Swap(&a[left], &a[index]);

		int keyi = left;
		int prev = left, cur = left + 1;
		while (cur <= right)
		{
			if (a[cur] < a[keyi]
				&& ++prev != cur)   //++prev != cur:优化自己与自己交换的情况
			{
				Swap(&a[prev], &a[cur]);
			}

			++cur;
		}

		Swap(&a[keyi], &a[prev]);
		int keyIndex = prev;

		//栈里面的就需要单趟分割排序
		//[left,keyIndex-1] keyIndex [keyIndex+1,right]
		if (keyIndex + 1 < right)
		{
			StackPush(&st, right);
			StackPush(&st, keyIndex + 1);

		}
		if (left < keyIndex - 1)
		{
			StackPush(&st, keyIndex - 1);
			StackPush(&st, left);
		}
	}
	StackDestory(&st);

}
void ShellSort(int* a, int n)
{
	int gap = n;  //可以自行设置,但不会给固定的值
	//把间隔为gap的多组数据同时排序(注意:理解这个多组的含义)
	//end最终位置:n-gap-1
	//gap的设置方式之一:
	while (gap > 1)
	{
		//gap=gap/2;        //一定会保证最后一次为1    //运行了logN次
		gap = gap / 3 + 1;  //但要保证最后一次为1      //运行了log3N次
		//gap>1时都是预排序,排序后接近有序
		//gap==1时,就是直接插入排序

		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}

}
void BubbleSort(int* a, int n)
{
	//写法1:
	for (int j = 0; j < n; ++j)
	{
		int exchange = 0;
		for (int i = 1; i < n - j; ++i)
		{
			if (a[i - 1] > a[i])
			{
				Swap(&a[i - 1], &a[i]);
				exchange = 1;
			}
		}

		if (exchange == 0)
		{
			break;
		}
	}
	写法2:
	//int end = n;
	//while (end > 0)
	//{
	//	for (int i = 1; i < end; ++i)
	//	{
	//		if (a[i - 1] > a[i])
	//		{
	//			Swap(&a[i - 1], &a[i]);
	//		}
	//	}
	//	--end;
	//}
}
void _MergeSort(int* a, int left, int right, int* tmp)
{
	if (left >= right)
		return;

	int mid = (left + right) >> 1;
	//假设 [left, mid] [mid+1, right]有序,那么我们就可以归并了
	_MergeSort(a, left, mid, tmp);
	_MergeSort(a, mid + 1, right, tmp);

	// 归并
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	int index = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			tmp[index++] = a[begin1++];
		}
		else
		{
			tmp[index++] = a[begin2++];
		}
	}

	while (begin1 <= end1)
	{
		tmp[index++] = a[begin1++];
	}

	while (begin2 <= end2)
	{
		tmp[index++] = a[begin2++];
	}

	// 拷贝回去
	for (int i = left; i <= right; ++i)
	{
		a[i] = tmp[i];
	}
}

void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n); //空间复杂度为O(N)
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
}
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	int gap = 1;  //每组数据个数
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)
		{
			//[i,i+gap-1] [i+gap,i+2*gap-1]

			// 归并
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			//归并过程中右半区间可能就不存在
			if (begin2 >= n)
				break;
			//归并过程中右半区间算多了,修正
			if (end2 >= n)
			{
				end2 = n - 1;
			}
			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[index++] = a[begin1++];
				}
				else
				{
					tmp[index++] = a[begin2++];
				}
			}

			while (begin1 <= end1)
			{
				tmp[index++] = a[begin1++];
			}

			while (begin2 <= end2)
			{
				tmp[index++] = a[begin2++];
			}
			// 拷贝回去
			for (int j = i; j < end2; ++j)
			{
				a[j] = tmp[j];
			}
		}
		gap *= 2;
	}
	free(tmp);
}
void CountSort(int* a, int n)
{
	int min = a[0], max = a[0];
	for (int i = 1; i < n; ++i)
	{
		if (a[i] < min)
			min = a[i];
		if (a[i] > max)
			max = a[i];
	}

	int range = max - min + 1;
	int* countA = (int*)malloc(sizeof(int) * range);
	assert(countA);

	memset(countA, 0, sizeof(int) * range);
	//计数
	for (int i = 0; i < n; ++i)
		countA[a[i] - min]++;
	//排序
	int j = 0;
	for (int i = 0; i < range; ++i)
	{
		while (countA[i]--)
		{
			a[j++] = i + min;
		}
	}
}

void TestOP()
{
	srand(time(0));
	const int N = 1000000;
	int* a1 = (int*)malloc(sizeof(int) * N);
	int* a2 = (int*)malloc(sizeof(int) * N);
	int* a3 = (int*)malloc(sizeof(int) * N);
	int* a4 = (int*)malloc(sizeof(int) * N);
	int* a5 = (int*)malloc(sizeof(int) * N);
	int* a6 = (int*)malloc(sizeof(int) * N);
	int* a7 = (int*)malloc(sizeof(int) * N);
	int* a8 = (int*)malloc(sizeof(int) * N);
	int* a9 = (int*)malloc(sizeof(int) * N);
	int* a10 = (int*)malloc(sizeof(int) * N);
	int* a11 = (int*)malloc(sizeof(int) * N);
	int* a12 = (int*)malloc(sizeof(int) * N);
	int* a13 = (int*)malloc(sizeof(int) * N);
	int* a14 = (int*)malloc(sizeof(int) * N);

	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand();
		//a1[i] = i;
		a2[i] = a1[i];
		a3[i] = a1[i];
		a4[i] = a1[i];
		a5[i] = a1[i];
		a6[i] = a1[i];
		a7[i] = a1[i];
		a8[i] = a1[i];
		a9[i] = a1[i];
		a10[i] = a1[i];
		a11[i] = a1[i];
		a12[i] = a1[i];
		a13[i] = a1[i];
		a14[i] = a1[i];

	}

	int begin1 = clock();
	//HeapSort1(a1, N);
	int end1 = clock();

	int begin2 = clock();
	//HeapSort2(a2, N);
	int end2 = clock();

	int begin3 = clock();
	//HeapSort3(a3, N);
	int end3 = clock();

	int begin4 = clock();
	//HeapSort4(a4, N);
	int end4 = clock();

	int begin5 = clock();
	QuickSort1(a5, 0, N - 1);
	int end5 = clock();

	int begin6 = clock();
	QuickSort2(a6, 0, N - 1);
	int end6 = clock();

	int begin7 = clock();
	QuickSort3(a7, 0, N - 1);
	int end7 = clock();

	int begin8 = clock();
	QuickSort4(a8, 0, N - 1);
	int end8 = clock();

	int begin9 = clock();
	QuickSortNonR(a9, N);
	int end9 = clock();

	int begin10 = clock();
	//ShellSort(a10, N);
	int end10 = clock();	

	int begin11 = clock();
	//BubbleSort(a11, N);
	int end11 = clock();	

	int begin12 = clock();
	//MergeSort(a12, N);
	int end12 = clock();	

	int begin13= clock();
	//MergeSortNonR(a13, N);
	int end13 = clock();	

	int begin14 = clock();
	//CountSort(a14, N);
	int end14 = clock();

	printf("给定%d个随机数进行排序,测试对比各大排序性能(单位:ms):\n", N);
	printf("建小堆-向上调整-降序:\t%d\n", end1 - begin1);
	printf("建大堆-向上调整-升序:\t%d\n", end2 - begin2);
	printf("建小堆-向下调整-降序:\t%d\n", end3 - begin3);
	printf("建大堆-向下调整-升序:\t%d\n", end4 - begin4);
	printf("快速排序-挖坑法:    \t%d\n", end5 - begin5);
	printf("快速排序-挖坑法优化:\t%d\n", end6 - begin6);
	printf("快速排序-左右指针法:\t%d\n", end7 - begin7);
	printf("快速排序-前后指针法:\t%d\n", end8 - begin8);
	printf("快速排序-非递归:    \t%d\n", end9 - begin9);
	printf("希尔排序:          \t%d\n", end10 - begin10);
	printf("冒泡排序:          \t%d\n", end11 - begin11);
	printf("归并排序:          \t%d\n", end12 - begin12);
	printf("归并排序-非递归:    \t%d\n", end13 - begin13);
	printf("计数排序:          \t%d\n", end14 - begin14);



	free(a1);
	free(a2);
	free(a3);
	free(a4);
	free(a5);
	free(a6);
	free(a7);
	free(a8);
	free(a9);
	free(a10);
	free(a11);
	free(a12);
	free(a13);
	free(a14);
}

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

后记:
●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                           ——By 作者:新晓·故知

评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值