十大经典排序算法

目录

0.排序的概念及其运用

1.直接插入排序

2.希尔排序

3.堆排序

4.冒泡排序

5.直接选择排序

6.快速排序--分治递归实现(挖坑法,前后指针法,左右指针法)

7.快速排序--数据结构栈模拟实现

8.归并排序--分治递归实现

9.归并排序--循环模拟实现

10.基数排序(使用桶排序辅助)

11.计数排序

12.总结​

 13.源代码(包含栈的相关函数)


0.排序的概念及其运用

排序 :所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起
来的操作。
稳定性:排序中的稳定性指的是当排序算法对相等的元素进行排序时,能够保持它们在原始序列中的相对位置不变。换句话说,如果原始序列中有两个相等的元素,经过排序后,它们的相对顺序仍然保持不变,那么这个排序算法就是稳定的。稳定性对于某些应用场景非常重要,比如对于对象的多重排序,如果排序算法是稳定的,可以确保每次排序后的结果是一致的。
内部排序 :数据元素全部放在内存中的排序。
外部排序 外部排序是指一种排序算法,用于对大规模数据进行排序,因为数据量太大无法一次性全部加载到内存中进行排序,因此需要借助外部存储设备(如硬盘)来存储部分数据,并在内存和外部存储设备之间进行数据交换和排序操作。外部排序算法通常包括多轮数据读取、排序和写入操作,以确保对整个数据集进行排序。常见的外部排序算法包括归并排序和快速排序等。

1.直接插入排序

思想:直接插入排序是一种简单的插入排序法,其基本思想是把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。

实现过程:当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移。

具体实现:

//插入排序--升序
void InsertSort1(int* arr,int num)
{
	//[0,end]是有序的,end+1 位置的数是要插入的
	for (int i = 0; i < num - 1; i++)
	{	//end是用来比较的位置,end+1是替换的位置
		int end = i;
		//起始end+1是需要插入有序序列的值的位置
		int temp = arr[end + 1];
		while (end >= 0)
		{
			if (arr[end] > temp)
			{
				arr[end + 1] = arr[end];
				end--;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = temp;
	}
}
//插入排序--降序
void InsertSort2(int* arr, int num)
{
	for (int i = 0; i < num - 1; i++)
	{
		int end = i;
		int temp = arr[end + 1];
		while (end >= 0)
		{
			if (arr[end] < temp)
			{
				arr[end + 1] = arr[end];
				end--;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = temp;
	}
}
最优情况:顺序O(n)
最坏情况:逆序O(n^2)
1. 元素集合越接近有序,直接插入排序算法的时间效率越高
2. 时间复杂度: O(N^2)
3. 空间复杂度: O(1) ,它是一种稳定的排序算法
4. 稳定性:稳定

2.希尔排序

思想:希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成几个组,所有距离相等(gap一致)的记录分在同一组内,并对每一组内的记录进行排序,使较大的记录能快速排到后面去,较小的记录能较快排到前面来。然后gap不断减小重复上述分组和排序的工作。当到达gap=1时(就是插入排序了),所有记录在统一组内排好序。

 实现过程:

具体实现:

//希尔排序--升序
void ShellSort1(int* arr, int num)
{
	int gap = num;
	while (gap > 1)
	{
		//gap一般两种取法,除gap==1,其余排序都预排序;gap==1,为插入排序
		//gap = gap / 2;
		gap = gap / 3 + 1;
		for (int i = 0; i < num - gap; i++)
		{
			int end = i;
			int temp = arr[end + gap];
			while (end >= 0)
			{
				if (arr[end] > temp)
				{
					arr[end + gap] = arr[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			arr[end + gap] = temp;
		}
	}
	
}

//希尔排序--降序
void ShellSort2(int* arr, int num)
{
	int gap = num;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < num - gap; i++)
		{
			int end = i;
			int temp = arr[end + gap];
			while (end >= 0)
			{
				if (arr[end] < temp)
				{
					arr[end + gap] = arr[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			arr[end + gap] = temp;
		}
	}
}
1. 希尔排序是对直接插入排序的优化。
2. gap > 1 时都是预排序,目的是让数组更接近于有序。当 gap == 1 时,数组已经接近有序的
了,这样就会很快。这样整体而言,可以达到优化的效果。
3. 希尔排序的时间复杂度不好计算,需要进行推导,推导出来平均时间复杂度: O(N^1.3~
N^2
4. 稳定性:不稳定

3.堆排序

思想:堆排序的思想是利用堆这种数据结构来进行排序。堆是一种特殊的树形数据结构,它满足堆的性质:对于任意节点i,其父节点和子节点之间有特定的大小关系。

堆排序的过程包括两个步骤:
1. 建立堆:将待排序的数组看作是一个完全二叉树,通过调整节点的位置,使得整个树满足堆的性质。建立堆的过程可以采用自下而上的方式,从最后一个非叶子节点开始,依次向前调整节点的位置,使得整个树都满足堆的性质。

//堆排序的向下调整算法--建大顶堆,算法前提是左右子树为大顶堆
void AdjustDown_Big(int* arr, int num, int root)
{
	int parent = root;
	//默认孩子是左孩子
	int child = parent * 2 + 1;
	while (child < num)
	{
		//选出较大的孩子
		if (child + 1 < num && arr[child + 1] > arr[child])
		{
			child += 1;
		}
		if (arr[child] > arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;//默认左孩子
		}
		else//已经是大顶堆了
		{
			break;
		}
	}
}

//堆排序的向下调整算法--建小顶堆,算法前提是左右子树为小顶堆
void AdjustDown_Small(int* arr, int num, int root)
{
	int parent = root;
	int child = parent * 2 + 1;
	while (child < num)
	{
		if (child + 1 < num && arr[child] > arr[child + 1])
		{
			child += 1;
		}
		if (arr[child] < arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}


2. 排序:将堆顶元素(即数组的第一个元素)与最后一个元素交换位置,然后将剩余元素重新调整为堆,再次取出堆顶元素与倒数第二个元素交换位置,以此类推,直到整个数组有序。

(要注意:升序建大顶堆,降序建小顶堆)

//堆排序--升序
void HeapSort1(int* arr, int num)
{
	//建堆--升序建大顶堆--从下到上建堆--从最后一个非叶子节点开始建堆
	for (int i = (num - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown_Big(arr, num, i);
	}
	//排序升序
	int end = num - 1;
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDown_Big(arr, end, 0);
		end--;
	}
}

//堆排序--降序
void HeapSort2(int* arr, int num)
{
	//建堆--升序建小顶堆--从下到上建堆--从最后一个非叶子节点开始建堆
	for (int i = (num - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown_Small(arr, num, i);
	}
	//排序降序
	int end = num - 1;
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDown_Small(arr, end, 0);
		end--;
	}
}

 具体实现:

//堆排序的向下调整算法--建大顶堆,算法前提是左右子树为大顶堆
void AdjustDown_Big(int* arr, int num, int root)
{
	int parent = root;
	//默认孩子是左孩子
	int child = parent * 2 + 1;
	while (child < num)
	{
		//选出较大的孩子
		if (child + 1 < num && arr[child + 1] > arr[child])
		{
			child += 1;
		}
		if (arr[child] > arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;//默认左孩子
		}
		else//已经是大顶堆了
		{
			break;
		}
	}
}

//堆排序的向下调整算法--建小顶堆,算法前提是左右子树为小顶堆
void AdjustDown_Small(int* arr, int num, int root)
{
	int parent = root;
	int child = parent * 2 + 1;
	while (child < num)
	{
		if (child + 1 < num && arr[child] > arr[child + 1])
		{
			child += 1;
		}
		if (arr[child] < arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//堆排序--升序
void HeapSort1(int* arr, int num)
{
	//建堆--升序建大顶堆--从下到上建堆--从最后一个非叶子节点开始建堆
	for (int i = (num - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown_Big(arr, num, i);
	}
	//排序升序
	int end = num - 1;
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDown_Big(arr, end, 0);
		end--;
	}
}

//堆排序--降序
void HeapSort2(int* arr, int num)
{
	//建堆--升序建小顶堆--从下到上建堆--从最后一个非叶子节点开始建堆
	for (int i = (num - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown_Small(arr, num, i);
	}
	//排序降序
	int end = num - 1;
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDown_Small(arr, end, 0);
		end--;
	}
}

堆排序的时间复杂度为O(nlogn),其中建堆的时间复杂度为O(n),排序的时间复杂度为O(nlogn)。堆排序是一种原地排序算法,不需要额外的空间,因此在空间复杂度上是比较优秀的。

1. 堆排序使用堆来选数,效率就高了很多。
2. 时间复杂度: O(N*logN)
3. 空间复杂度: O(1)
4. 稳定性:不稳定

4.冒泡排序

思想:就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

实现过程:

具体实现(要注意边界的控制):

//冒泡排序--升序
void BubbleSort1(int* arr, int num)
{
	for (int i = 1; i < num; i++)
	{
		//flag进行优化,如果已经有序就不用继续排序了
		int flag = 1;
		for (int j = 0; j < num - i; j++)
		{
			if (arr[j + 1] < arr[j])
			{
				Swap(&arr[j + 1], &arr[j]);
				flag = 0;
			}
		}
		if (flag)
		{
			return;
		}
	}
}
//冒泡排序--降序
void BubbleSort2(int* arr, int num)
{
	for (int i = 1; i < num; i++)
	{
		//flag进行优化,如果已经有序就不用继续排序了
		int flag = 1;
		for (int j = 0; j < num - i; j++)
		{
			if (arr[j + 1] > arr[j])
			{
				Swap(&arr[j + 1], &arr[j]);
				flag = 0;
			}
		}
		//优化
		if (flag)
		{
			return;
		}
	}
}
1. 时间复杂度: O(N^2)
2. 空间复杂度: O(1)
3. 稳定性:稳定

5.直接选择排序

思想:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。

实现过程:在元素集合array[i]--array[n-1]中选择关键码最大()的数据元素

若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个) 元素交换

在剩余的array[i]--array[n-2]array[i+1]--array[n-1])集合中,重复上述步骤,直到集合剩余1个元素

具体实现(也可以一次遍历找出区间最大值和最小值,并按照升降序安置区间最前后位置):

//选择排序-升序
void SelectSort(int* arr, int num)
{
	//起始左右下标
	int left = 0;
	int right = num - 1;
	while (left < right)
	{
		int maxi = left;
		int mini = left;
		for (int i = left; i <= right; i++)
		{
			if (arr[maxi] < arr[i])
			{
				maxi = i;
			}
			if (arr[mini] > arr[i])
			{
				mini = i;
			}
		}
		Swap(&arr[maxi], &arr[right]);
		//特殊情况修正
		if (mini == right)
		{
			mini = maxi;
		}
		Swap(&arr[mini], &arr[left]);
		left++;
		right--;
	}
}

1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用

2. 时间复杂度:O(N^2)

3. 空间复杂度: O(1)
4. 稳定性:不稳定

6.快速排序--分治递归实现(挖坑法,前后指针法,左右指针法)

 思想:快速排序是Hoare1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值(一般取最左边的数,或者最右边,下面实现过程取最左边),按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值(此时基准值就在排完序后的位置了),然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止

实现过程:

 具体实现(三种方法):

1.挖坑法(优化:三数取中,小区间优化):挖坑法是一种递归实现的快速排序方法,它的思想是选择一个基准值,然后通过一趟排序将序列分割成两部分,使得左边的元素都小于基准值,右边的元素都大于基准值(升序)。接着,再分别对左右两部分进行递归排序。

//三数取中
int GetMidIndex(int* arr, int left, int right)
{
	int mid = (left + right) >> 1;
	if (arr[left] < arr[right])
	{
		if (arr[mid] < arr[left])
			return left;
		else if (arr[mid] > arr[right])
			return right;
		else
			return mid;
	}
	else //arr[left] >= arr[right]
	{
		if (arr[mid] > arr[left])
			return left;
		else if (arr[mid] < arr[right])
			return right;
		else
			return mid;
	}
}

//快速排序--挖坑法--升序//分治递归
void QuickSort1(int* arr, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	//优化:三数取中(避免极端恶劣情况)
	int midindex = GetMidIndex(arr, left, right);
	Swap(&arr[midindex], &arr[left]);

	int begin = left;
	int end = right;
	int Pivot = begin;
	int key = arr[Pivot];
	while (begin < end)
	{
		while (begin < end && arr[end] >= key)
		{
			end--;
		}
		arr[Pivot] = arr[end];
		Pivot = end;

		while (begin < end && arr[begin] <= key)
		{
			begin++;
		}
		arr[Pivot] = arr[begin];
		Pivot = begin;
	}
	arr[Pivot] = key;

	//QuickSort1(arr,left,Pivot - 1);
	//QuickSort1(arr, Pivot + 1, right);

	//优化:小区间优化,区间数据个数一般为13或10个,剩下用希尔排序,可以减少递归次数
	if (Pivot - 1 - left >= 13)
	{
		QuickSort1(arr, left, Pivot - 1);
	}
	else
	{
		InsertSort1(arr + left, Pivot - 1 - left + 1);
	}
	if (right - (Pivot + 1) >= 13)
	{
		QuickSort1(arr, Pivot + 1, right);
	}
	else
	{
		InsertSort1(arr + Pivot + 1, right - (Pivot + 1) + 1);
	}
}
//快速排序--挖坑法--降序//分治递归
void QuickSort2(int* arr, int left, int right)
{
	if (left >= right)
	{
		return;
	}

	//优化:三数取中(避免极端恶劣情况)
	int midindex = GetMidIndex(arr, left, right);
	Swap(&arr[midindex], &arr[left]);

	int begin = left;
	int end = right;
	int Pivot = begin;
	int key = arr[Pivot];
	while (begin < end)
	{
		while (begin < end && arr[end] <= key)
		{
			end--;
		}
		arr[Pivot] = arr[end];
		Pivot = end;

		while (begin < end && arr[begin] >= key)
		{
			begin++;
		}
		arr[Pivot] = arr[begin];
		Pivot = begin;
	}
	arr[Pivot] = key;

	//QuickSort2(arr,left,Pivot - 1);
	//QuickSort2(arr, Pivot + 1, right);

	//优化:小区间优化,区间数据个数一般为13或10个,剩下用希尔排序,可以减少递归次数
	if (Pivot - 1 - left >= 13)
	{
		QuickSort2(arr, left, Pivot - 1);
	}
	else
	{
		InsertSort2(arr + left, Pivot - 1 - left + 1);
	}
	if (right - (Pivot + 1) >= 13)
	{
		QuickSort2(arr, Pivot + 1, right);
	}
	else
	{
		InsertSort2(arr + Pivot + 1, right - (Pivot + 1) + 1);
	}
}

 2.左右指针法:左右指针法是一种迭代实现的快速排序方法,它的思想是通过两个指针分别从序列的两端向中间移动,找到需要交换的元素,然后交换它们的位置,直到两个指针相遇。最后,再分别对左右两部分进行递归排序。

//快速排序--左右指针法--升序
void QuickSort_LR1(int* arr, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int begin = left;
	int end = right;
	int keyi = left;//默认去左边

	while (begin < end)
	{
		if (arr[end] > arr[keyi])
		{
			end--;
		}
		if (arr[begin] < arr[keyi])
		{
			begin++;
		}
		Swap(&arr[end], &arr[begin]);
	}
	Swap(&arr[keyi], &arr[begin]);

	QuickSort_LR1(arr, left, begin - 1);
	QuickSort_LR1(arr, begin + 1, right);
}

//快速排序--左右指针法--降序
void QuickSort_LR2(int* arr, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int begin = left;
	int end = right;
	int keyi = left;
	while (begin < end)
	{
		if (arr[end] < arr[keyi])
		{
			end--;
		}
		if (arr[begin] > arr[keyi])
		{
			begin++;
		}
		Swap(&arr[begin], &arr[end]);
	}
	Swap(&arr[begin], &arr[keyi]);

	QuickSort_LR2(arr, left, keyi - 1);
	QuickSort_LR2(arr, keyi + 1, right);
}

 3.前后指针法:prev前驱指针和cur指针,cur遍历数组,(升序)cur遇到比keyi对应位置大的数就继续前进,遇到比keyi对应位置小的数就停下来,prev先加一,然后cur与prev对应位置进行交换,接着继续重复操作,直到cur超出边界。

//快速排序--前后指针法--升序
void QuickSort_BE1(int* arr, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int cur = left + 1;
	int prev = left;
	int keyi = left;
	while (cur <= right)
	{
		if (arr[cur] < arr[keyi] && ++prev != cur)//prev进行加一的同时,判断是否在同一位置,同一位置就不用交换了
		{
			Swap(&arr[cur], &arr[prev]);
		}
		cur++;
	}
	Swap(&arr[prev], &arr[keyi]);

	QuickSort_BE1(arr, left, prev - 1);
	QuickSort_BE1(arr, prev + 1, right);
}

//快速排序--前后指针法--降序
void QuickSort_BE2(int* arr, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int cur = left + 1;
	int prev = left;
	int keyi = left;
	while (cur <= right)
	{
		if (arr[cur] > arr[keyi] && ++prev != cur)//prev进行加一的同时,判断是否在同一位置,同一位置就不用交换了
		{
			Swap(&arr[prev], &arr[cur]);
		}
		cur++;
	}
	Swap(&arr[prev], &arr[keyi]);

	QuickSort_BE2(arr, left, prev - 1);
	QuickSort_BE2(arr, prev + 1, right);
}

 1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

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

3. 空间复杂度:O(logN) 

4. 稳定性:不稳定

7.快速排序--数据结构栈模拟实现

递归方式存在如果递归过深,栈空间不够的问题,可以通过数据结构的栈来模拟实现,通过区间的入栈出栈来实现快速排序:

//快速排序(非递归)--  升序-- 用数据结构的栈模拟递归
void QuickSortNonR1(int* arr, int num)
{
	Stack st;
	StackInit(&st);
	StackPush(&st, num - 1);
	StackPush(&st, 0);

	while (!StackEmpty(&st))
	{
		int left = StackTop(&st);
		StackPop(&st);
		int right = StackTop(&st);
		StackPop(&st);

		//单层快排--这里用挖坑法,也可用其他方法
		int begin = left;
		int end = right;
		int Pivot = begin;
		int key = arr[Pivot];
		while (begin < end)
		{
			while (begin < end && arr[end] >= key)
			{
				end--;
			}
			arr[Pivot] = arr[end];
			Pivot = end;

			while (begin < end && arr[begin] <= key)
			{
				begin++;
			}
			arr[Pivot] = arr[begin];
			Pivot = begin;
		}
		arr[Pivot] = key;
		//继续入栈
		if (Pivot + 1 < right)
		{
			StackPush(&st, right);
			StackPush(&st, Pivot + 1);
		}
		if (left < Pivot - 1)
		{
			StackPush(&st, Pivot - 1);
			StackPush(&st, left);
		}
	}
	//销毁
	StackDestroy(&st);
}

//快速排序(非递归)-- 降序 -- 用数据结构的栈模拟递归
void QuickSortNonR2(int* arr, int num)
{
	Stack st;
	StackInit(&st);
	StackPush(&st, num - 1);
	StackPush(&st, 0);

	while (!StackEmpty(&st))
	{
		int left = StackTop(&st);
		StackPop(&st);
		int right = StackTop(&st);
		StackPop(&st);

		int begin = left;
		int end = right;
		int Privot = left;
		int key = arr[Privot];
		while (begin < end)
		{
			while (begin < end && arr[end] <= key)
			{
				end--;
			}
			arr[Privot] = arr[end];
			Privot = end;
			while (begin < end && arr[begin] >= key)
			{
				begin++;
			}
			arr[Privot] = arr[begin];
			Privot = begin;
		}
		arr[Privot] = key;

		if (Privot + 1 < right)
		{
			StackPush(&st, right);
			StackPush(&st, Privot + 1);
		}
		if (left < Privot - 1)
		{
			StackPush(&st, Privot - 1);
			StackPush(&st, left);
		}
	}

	StackDestroy(&st);
}

8.归并排序--分治递归实现

思想:归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序 列;即先使每个子序列,有序再使子序列段间有序。若将两个有序表合并成一个有序表,称为二 路归并。

实现过程:

 具体实现:

//归并排序(分治递归)--升序//递归缺陷:递归深度太深,栈空间可能不够用。
void MergeSort1(int* arr, int left, int right, int* temp)
{
	if (left >= right)
	{
		return;
	}
	//使左右两边有序
	int mid = (left + right) >> 1;
	MergeSort1(arr, left, mid, temp);
	MergeSort1(arr, mid + 1, right, temp);

	//归并
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	int index = left;//拷贝数组的下标
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] < arr[begin2])
		{
			temp[index++] = arr[begin1++];
		}
		else
		{
			temp[index++] = arr[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		temp[index++] = arr[begin1++];
	}
	while (begin2 <= end2)
	{
		temp[index++] = arr[begin2++];
	}
	//拷贝回去
	for (int i = left; i <= right; i++)
	{
		arr[i] = temp[i];
	}
}

//归并排序(分治递归)--降序
void MergeSort2(int* arr, int left, int right, int* temp)
{
	if (left >= right)
	{
		return;
	}
	int mid = (left + right) >> 1;
	MergeSort2(arr, left, mid, temp);
	MergeSort2(arr, mid + 1, right, temp);
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	int index = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] > arr[begin2])
		{
			temp[index++] = arr[begin1++];
		}
		else
		{
			temp[index++] = arr[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		temp[index++] = arr[begin1++];
	}
	while (begin2 <= end2)
	{
		temp[index++] = arr[begin2++];
	}
	for (int i = left; i <= right; i++)
	{
		arr[i] = temp[i];
	}
}
1. 归并的缺点在于需要 O(N) 的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问
题。
2. 时间复杂度: O(N*logN)
3. 空间复杂度: O(N)
4. 稳定性:稳定 

9.归并排序--循环模拟实现

用循环模拟实现,先将1个和1个合并,再2个和2个,再4个与4个合并……

要注意特殊情况:

1.最后左区间存在(满值或者缺值),右区间不存在--直接向temp拷贝,再拷贝回来,如果没有拷贝给temp,temp拷贝回来可能会有随机值;

2.最后左区间存在,右区间也存在但是缺值,那么就要修改下标边界值;

具体实现:

//归并排序(非递归)-- 升序 -- 用循环模拟递归
void MergeSortNonR1(int* arr, int num)
{
	int* temp = (int*)malloc(sizeof(int) * num);
	//gap表示每组数据个数
	int gap = 1;//刚开始一个一个归
	while (gap < num)
	{
		for (int i = 0; i < num; 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;
			//特殊情况:右半区间不存在,最后一组数据才会出现这种情况,所以可以直接break;
			if (begin2 >= num)
			{
				break;
			}
			//特殊情况:归并过程中右半区间算多了,需要修正
			if (end2 >= num)
			{
				end2 = num - 1;
			}
			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] < arr[begin2])
				{
					temp[index++] = arr[begin1++];
				}
				else
				{
					temp[index++] = arr[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				temp[index++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				temp[index++] = arr[begin2++];
			}
			//每两组合并就拷贝回去
			for (int j = i; j <= end2; j++)
			{
				arr[j] = temp[j];
			}
		}
		gap *= 2;
	}
	free(temp);
}

//归并排序(非递归)-- 降序 -- 用循环模拟递归
void MergeSortNonR2(int* arr, int num)
{
	int* temp = (int*)malloc(sizeof(int) * num);
	int gap = 1;
	while (gap < num)
	{
		for (int i = 0; i < num; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			if (begin2 >= num)
			{
				break;
			}
			if (end2 >= num)
			{
				end2 = num - 1;
			}
			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] < arr[begin2])
				{
					temp[index++] = arr[begin2++];
				}
				else
				{
					temp[index++] = arr[begin1++];
				}
			}
			while (begin1 <= end1)
			{
				temp[index++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				temp[index++] = arr[begin2++];
			}
			//拷贝
			for (int j = 0; j <= end2; j++)
			{
				arr[j] = temp[j];
			}
		}

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

10.基数排序(使用桶排序辅助)

思想:基数排序的思想是将待排序的数字按照各个位数的大小进行排序。首先根据个位数进行排序,然后根据十位数进行排序,依次类推,直到最高位数。这样可以确保排序的稳定性,即相同位数的数字在排序后仍然保持相对顺序不变。基数排序通常使用桶排序或计数排序作为辅助排序算法,以实现对各个位数的排序。基数排序适用于待排序数字的位数不太多的情况,时间复杂度为O(nk),其中n为待排序数字的个数,k为数字的最大位数。

缺陷:Temp大小不定,并且需要初始值填充数组(下面用0填充),也就是说要排序的数据不能有填充数组的数据。

具体实现:

//基数排序 -- 依次从小到大的位排序
#define N 1000
int Temp[10][N];//0为填充值的桶数组

void RedixSort(int* arr, int num)
{
	int i = 0, j = 0;
	int pos;
	for (int k = 10; k < 1001; k *= 10)
	{
		for (i = 0; i < num; i++)
		{
			j = 0;
			pos = (arr[i] % k) / (k / 10);
			while (Temp[pos][j] != 0)
			{
				j++;
			}
			Temp[pos][j] = arr[i];
		}
		pos = 0;
		for (i = 0; i < 10; i++)
		{
			for (j = 0; j < num && Temp[i][j] != 0; j++)
			{
				arr[pos++] = Temp[i][j];
				//还原桶数组
				Temp[i][j] = 0;
			}
		}
	}
}

基数排序的时间复杂度取决于待排序元素的位数和基数。假设待排序的数有n个,k表示最大数的位数,基数排序的时间复杂度为O(nk)。

基数排序是一种稳定的排序算法,即相同元素的相对位置在排序后不会改变。

11.计数排序

计数排序的思想是通过统计数组中每个元素出现的次数,然后根据统计结果将元素按照顺序放入新的数组中。具体步骤如下:

1. 找出待排序数组中的最大值和最小值,确定计数数组的大小。
2. 创建一个计数数组,大小为最大值和最小值之间的差值加一。
3. 遍历待排序数组,统计每个元素出现的次数并存储在计数数组中。(相对映射在数较大时可以减少数组开辟空间)
4. 遍历计数数组,根据统计结果将元素按照顺序放入新的数组中。
5. 将新数组复制回原数组,完成排序。

具体实现:

//计数排序--映射--绝对映射位置和相对映射位置(可以减少空间开辟)
//时间O(N+range)
//空间O(range)
void CountSort(int* arr, int num)
{
	//计算区间大小
	int max = arr[0], min = arr[0];
	for (int i = 0; i < num; i++)
	{
		if (arr[i] > max)
		{
			max = arr[i];
		}
		if (arr[i] < min)
		{
			min = arr[i];
		}
	}
	int range = max - min + 1;//(注意要+1,因为要算上被减的数)
	int* count = (int*)malloc(sizeof(int) * range);
	memset(count, 0, sizeof(int) * range);
	//统计次数
	for (int i = 0; i < num; i++)
	{
		//相对映射位置
		count[arr[i] - min]++;
	}
	//排序
	int j = 0;
	for (int i = 0; i < range; i++)
	{
		while (count[i]--)
		{
			//相对映射,要+min回去
			arr[j++] = i + min;
		}
	}
}

计数排序的时间复杂度为O(n+k),其中n为待排序数组的大小,k为计数数组的大小。计数排序适用于待排序数组元素范围不大且比较集中的情况,例如非负整数。

12.总结

 13.源代码(包含栈的相关函数)

//Stack.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1

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

typedef int StackDataType;
//栈(stack)
typedef struct Stack
{
	StackDataType* arr;//数据
	int capacity;//容量
	int top;//栈顶,top初始化为0,则top为最后一个有效数据的下一位下标;top初始化为-1,则为最后一个有效数据下标
}Stack;

//初始化
void StackInit(Stack* ps);
//销毁
void StackDestroy(Stack* ps);
//入栈
void StackPush(Stack* ps, StackDataType x);
//出栈
void StackPop(Stack* ps);
//调用栈顶
StackDataType StackTop(Stack* ps);
//返回个数
int StackSize(Stack* ps);
//判断是否为空栈
bool StackEmpty(Stack* ps);
//Stack.c

#include "Stack.h"

//初始化
void StackInit(Stack* ps)
{
	assert(ps);
	ps->arr = (StackDataType*)malloc(sizeof(StackDataType) * 4);//初始开辟容量4个
	if (ps->arr == NULL)//开辟失败
	{
		perror("Malloc Fail!");
		exit(1);
	}
	ps->capacity = 4; // 初始开辟容量4个
	ps->top = 0;//top初始化为0,则top为最后一个有效数据的下一位下标
}
//销毁
void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->arr);
	ps->arr = NULL;
	ps->capacity = ps->top = 0;
}
//入栈
void StackPush(Stack* ps, StackDataType x)
{
	assert(ps);
	//需要扩容
	if (ps->top == ps->capacity)
	{
		//扩容为原来2倍
		StackDataType* temp = (StackDataType*)realloc(ps->arr, ps->capacity * 2 * sizeof(StackDataType));
		if (temp == NULL)//扩容失败
		{
			perror("Realloc Fail!");
			exit(1);
		}
		ps->arr = temp;
		ps->capacity = ps->capacity * 2;
	}
	//加入数据
	ps->arr[ps->top] = x;
	ps->top++;
}
//出栈
void StackPop(Stack* ps)
{
	assert(ps);
	//栈为空不能删
	assert(ps->top > 0);
	ps->top--;
}
//调用栈顶
StackDataType StackTop(Stack* ps)
{
	assert(ps);
	//栈为空不能调用
	assert(ps->top > 0);

	return ps->arr[ps->top - 1];
}
//返回个数
int StackSize(Stack* ps)
{
	assert(ps);

	return ps->top;
}
//判断是否为空栈
bool StackEmpty(Stack* ps)
{
	assert(ps);
	//真为空,假不为空
	return ps->top == 0;
}

//Sort.c

#define _CRT_SECURE_NO_WARNINGS 1

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

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

//生成随机数
int* CreatRand(int num)
{
	srand((unsigned int)time(NULL));
	int* arr = (int*)malloc(sizeof(int) * num);
	for (int i = 0; i < num; i++)
	{
		arr[i] = rand();
	}
	return arr;
}

//插入排序--升序
void InsertSort1(int* arr,int num)
{
	//[0,end]是有序的,end+1 位置的数是要插入的
	for (int i = 0; i < num - 1; i++)
	{	//end是用来比较的位置,end+1是替换的位置
		int end = i;
		//起始end+1是需要插入有序序列的值的位置
		int temp = arr[end + 1];
		while (end >= 0)
		{
			if (arr[end] > temp)
			{
				arr[end + 1] = arr[end];
				end--;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = temp;
	}
}
//插入排序--降序
void InsertSort2(int* arr, int num)
{
	for (int i = 0; i < num - 1; i++)
	{
		int end = i;
		int temp = arr[end + 1];
		while (end >= 0)
		{
			if (arr[end] < temp)
			{
				arr[end + 1] = arr[end];
				end--;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = temp;
	}
}

//插入排序测试
void TestInsert(int num)
{
	int* arr = CreatRand(num);
	int clock1 = clock();
	InsertSort1(arr, num);
	int clock2 = clock();
	//Print(arr, num);
	printf("%d\n", clock2 - clock1);

	//int clock5 = clock();
	//InsertSort1(arr, num);
	//int clock6 = clock();
	Print(arr, num);
	//printf("%d\n", clock6 - clock5);

	//int clock3 = clock();
	//InsertSort2(arr, num);
	//int clock4 = clock();
	Print(arr, num);
	//printf("%d\n", clock4 - clock3);
	free(arr);
}

//希尔排序--升序
void ShellSort1(int* arr, int num)
{
	int gap = num;
	while (gap > 1)
	{
		//gap一般两种取法,除gap==1,其余排序都预排序;gap==1,为插入排序
		//gap = gap / 2;
		gap = gap / 3 + 1;
		for (int i = 0; i < num - gap; i++)
		{
			int end = i;
			int temp = arr[end + gap];
			while (end >= 0)
			{
				if (arr[end] > temp)
				{
					arr[end + gap] = arr[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			arr[end + gap] = temp;
		}
	}
	
}

//希尔排序--降序
void ShellSort2(int* arr, int num)
{
	int gap = num;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < num - gap; i++)
		{
			int end = i;
			int temp = arr[end + gap];
			while (end >= 0)
			{
				if (arr[end] < temp)
				{
					arr[end + gap] = arr[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			arr[end + gap] = temp;
		}
	}
}

//希尔排序测试
void TestShell(int num)
{
	int* arr = CreatRand(num);
	int clock1 = clock();
	ShellSort1(arr, num);
	int clock2 = clock();
	//Print(arr, num);
	printf("%d\n", clock2 - clock1);

	//int clock3 = clock();
	//ShellSort2(arr, num);
	//int clock4 = clock();
	Print(arr, num);
	//printf("%d\n", clock4 - clock3);
}

//Swap交换
void Swap(int* a, int* b)
{
	int temp = *a;
	*a = *b;
	*b = temp;
}

//堆排序的向下调整算法--建大顶堆,算法前提是左右子树为大顶堆
void AdjustDown_Big(int* arr, int num, int root)
{
	int parent = root;
	//默认孩子是左孩子
	int child = parent * 2 + 1;
	while (child < num)
	{
		//选出较大的孩子
		if (child + 1 < num && arr[child + 1] > arr[child])
		{
			child += 1;
		}
		if (arr[child] > arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;//默认左孩子
		}
		else//已经是大顶堆了
		{
			break;
		}
	}
}

//堆排序的向下调整算法--建小顶堆,算法前提是左右子树为小顶堆
void AdjustDown_Small(int* arr, int num, int root)
{
	int parent = root;
	int child = parent * 2 + 1;
	while (child < num)
	{
		if (child + 1 < num && arr[child] > arr[child + 1])
		{
			child += 1;
		}
		if (arr[child] < arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

//堆排序--升序
void HeapSort1(int* arr, int num)
{
	//建堆--升序建大顶堆--从下到上建堆--从最后一个非叶子节点开始建堆
	for (int i = (num - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown_Big(arr, num, i);
	}
	//排序升序
	int end = num - 1;
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDown_Big(arr, end, 0);
		end--;
	}
}

//堆排序--降序
void HeapSort2(int* arr, int num)
{
	//建堆--升序建小顶堆--从下到上建堆--从最后一个非叶子节点开始建堆
	for (int i = (num - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown_Small(arr, num, i);
	}
	//排序降序
	int end = num - 1;
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDown_Small(arr, end, 0);
		end--;
	}
}

//堆排序测试
void TestHeap(int num)
{
	int* arr1 = CreatRand(num);
	int clock1 = clock();
	HeapSort1(arr1, num);
	int clock2 = clock();
	//Print(arr1, num);
	printf("%d\n", clock2 - clock1);

	//int* arr2 = CreatRand(num);
	//int clock3 = clock();
	//HeapSort2(arr2, num);
	//int clock4 = clock();
	Print(arr2, num);
	//printf("%d\n", clock4 - clock3);
}

//冒泡排序--升序
void BubbleSort1(int* arr, int num)
{
	for (int i = 1; i < num; i++)
	{
		//flag进行优化,如果已经有序就不用继续排序了
		int flag = 1;
		for (int j = 0; j < num - i; j++)
		{
			if (arr[j + 1] < arr[j])
			{
				Swap(&arr[j + 1], &arr[j]);
				flag = 0;
			}
		}
		if (flag)
		{
			return;
		}
	}
}
//冒泡排序--降序
void BubbleSort2(int* arr, int num)
{
	for (int i = 1; i < num; i++)
	{
		//flag进行优化,如果已经有序就不用继续排序了
		int flag = 1;
		for (int j = 0; j < num - i; j++)
		{
			if (arr[j + 1] > arr[j])
			{
				Swap(&arr[j + 1], &arr[j]);
				flag = 0;
			}
		}
		//优化
		if (flag)
		{
			return;
		}
	}
}

//冒泡排序测试
void TestBubble(int num)
{
	int* arr = CreatRand(num);
	BubbleSort1(arr, num);
	Print(arr, num);
	BubbleSort2(arr, num);
	Print(arr, num);
}

//选择排序-升序
void SelectSort(int* arr, int num)
{
	//起始左右下标
	int left = 0;
	int right = num - 1;
	while (left < right)
	{
		int maxi = left;
		int mini = left;
		for (int i = left; i <= right; i++)
		{
			if (arr[maxi] < arr[i])
			{
				maxi = i;
			}
			if (arr[mini] > arr[i])
			{
				mini = i;
			}
		}
		Swap(&arr[maxi], &arr[right]);
		//特殊情况修正
		if (mini == right)
		{
			mini = maxi;
		}
		Swap(&arr[mini], &arr[left]);
		left++;
		right--;
	}
}

//选择排序测试
void TestSelect(int num)
{
	int* arr = CreatRand(num);
	SelectSort(arr, num);
	Print(arr, num);
}

//三数取中
int GetMidIndex(int* arr, int left, int right)
{
	int mid = (left + right) >> 1;
	if (arr[left] < arr[right])
	{
		if (arr[mid] < arr[left])
			return left;
		else if (arr[mid] > arr[right])
			return right;
		else
			return mid;
	}
	else //arr[left] >= arr[right]
	{
		if (arr[mid] > arr[left])
			return left;
		else if (arr[mid] < arr[right])
			return right;
		else
			return mid;
	}
}

//快速排序--挖坑法--升序//分治递归
void QuickSort1(int* arr, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	//优化:三数取中(避免极端恶劣情况)
	int midindex = GetMidIndex(arr, left, right);
	Swap(&arr[midindex], &arr[left]);

	int begin = left;
	int end = right;
	int Pivot = begin;
	int key = arr[Pivot];
	while (begin < end)
	{
		while (begin < end && arr[end] >= key)
		{
			end--;
		}
		arr[Pivot] = arr[end];
		Pivot = end;

		while (begin < end && arr[begin] <= key)
		{
			begin++;
		}
		arr[Pivot] = arr[begin];
		Pivot = begin;
	}
	arr[Pivot] = key;

	//QuickSort1(arr,left,Pivot - 1);
	//QuickSort1(arr, Pivot + 1, right);

	//优化:小区间优化,区间数据个数一般为13或10个,剩下用希尔排序,可以减少递归次数
	if (Pivot - 1 - left >= 13)
	{
		QuickSort1(arr, left, Pivot - 1);
	}
	else
	{
		InsertSort1(arr + left, Pivot - 1 - left + 1);
	}
	if (right - (Pivot + 1) >= 13)
	{
		QuickSort1(arr, Pivot + 1, right);
	}
	else
	{
		InsertSort1(arr + Pivot + 1, right - (Pivot + 1) + 1);
	}
}
//快速排序--挖坑法--降序//分治递归
void QuickSort2(int* arr, int left, int right)
{
	if (left >= right)
	{
		return;
	}

	//优化:三数取中(避免极端恶劣情况)
	int midindex = GetMidIndex(arr, left, right);
	Swap(&arr[midindex], &arr[left]);

	int begin = left;
	int end = right;
	int Pivot = begin;
	int key = arr[Pivot];
	while (begin < end)
	{
		while (begin < end && arr[end] <= key)
		{
			end--;
		}
		arr[Pivot] = arr[end];
		Pivot = end;

		while (begin < end && arr[begin] >= key)
		{
			begin++;
		}
		arr[Pivot] = arr[begin];
		Pivot = begin;
	}
	arr[Pivot] = key;

	//QuickSort2(arr,left,Pivot - 1);
	//QuickSort2(arr, Pivot + 1, right);

	//优化:小区间优化,区间数据个数一般为13或10个,剩下用希尔排序,可以减少递归次数
	if (Pivot - 1 - left >= 13)
	{
		QuickSort2(arr, left, Pivot - 1);
	}
	else
	{
		InsertSort2(arr + left, Pivot - 1 - left + 1);
	}
	if (right - (Pivot + 1) >= 13)
	{
		QuickSort2(arr, Pivot + 1, right);
	}
	else
	{
		InsertSort2(arr + Pivot + 1, right - (Pivot + 1) + 1);
	}
}

//快速排序测试(挖坑法)
void TestQuick(int num)
{
	int* arr = CreatRand(num);
	QuickSort1(arr, 0, num - 1);
	Print(arr, num);

	QuickSort2(arr, 0, num - 1);
	Print(arr, num);
}

//快速排序--左右指针法--升序
void QuickSort_LR1(int* arr, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int begin = left;
	int end = right;
	int keyi = left;//默认去左边

	while (begin < end)
	{
		if (arr[end] > arr[keyi])
		{
			end--;
		}
		if (arr[begin] < arr[keyi])
		{
			begin++;
		}
		Swap(&arr[end], &arr[begin]);
	}
	Swap(&arr[keyi], &arr[begin]);

	QuickSort_LR1(arr, left, begin - 1);
	QuickSort_LR1(arr, begin + 1, right);
}

//快速排序--左右指针法--降序
void QuickSort_LR2(int* arr, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int begin = left;
	int end = right;
	int keyi = left;
	while (begin < end)
	{
		if (arr[end] < arr[keyi])
		{
			end--;
		}
		if (arr[begin] > arr[keyi])
		{
			begin++;
		}
		Swap(&arr[begin], &arr[end]);
	}
	Swap(&arr[begin], &arr[keyi]);

	QuickSort_LR2(arr, left, keyi - 1);
	QuickSort_LR2(arr, keyi + 1, right);
}

//快速排序测试(左右指针法)
void QuickTest_LR(int num)
{
	int* arr = CreatRand(num);
	QuickSort_LR1(arr, 0, num - 1);
	Print(arr, num);
	QuickSort_LR2(arr, 0, num - 1);
	Print(arr, num);
}

//快速排序--前后指针法--升序
void QuickSort_BE1(int* arr, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int cur = left + 1;
	int prev = left;
	int keyi = left;
	while (cur <= right)
	{
		if (arr[cur] < arr[keyi] && ++prev != cur)//prev进行加一的同时,判断是否在同一位置,同一位置就不用交换了
		{
			Swap(&arr[cur], &arr[prev]);
		}
		cur++;
	}
	Swap(&arr[prev], &arr[keyi]);

	QuickSort_BE1(arr, left, prev - 1);
	QuickSort_BE1(arr, prev + 1, right);
}

//快速排序--前后指针法--降序
void QuickSort_BE2(int* arr, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int cur = left + 1;
	int prev = left;
	int keyi = left;
	while (cur <= right)
	{
		if (arr[cur] > arr[keyi] && ++prev != cur)//prev进行加一的同时,判断是否在同一位置,同一位置就不用交换了
		{
			Swap(&arr[prev], &arr[cur]);
		}
		cur++;
	}
	Swap(&arr[prev], &arr[keyi]);

	QuickSort_BE2(arr, left, prev - 1);
	QuickSort_BE2(arr, prev + 1, right);
}

//快速排序测试(前后指针法)
void QuickTest_BE(int num)
{
	int* arr = CreatRand(num);
	QuickSort_BE1(arr, 0, num - 1);
	Print(arr, num);
	QuickSort_BE2(arr, 0, num - 1);
	Print(arr, num);
}

//归并排序(分治递归)--升序//递归缺陷:递归深度太深,栈空间可能不够用。
void MergeSort1(int* arr, int left, int right, int* temp)
{
	if (left >= right)
	{
		return;
	}
	//使左右两边有序
	int mid = (left + right) >> 1;
	MergeSort1(arr, left, mid, temp);
	MergeSort1(arr, mid + 1, right, temp);

	//归并
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	int index = left;//拷贝数组的下标
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] < arr[begin2])
		{
			temp[index++] = arr[begin1++];
		}
		else
		{
			temp[index++] = arr[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		temp[index++] = arr[begin1++];
	}
	while (begin2 <= end2)
	{
		temp[index++] = arr[begin2++];
	}
	//拷贝回去
	for (int i = left; i <= right; i++)
	{
		arr[i] = temp[i];
	}
}

//归并排序(分治递归)--降序
void MergeSort2(int* arr, int left, int right, int* temp)
{
	if (left >= right)
	{
		return;
	}
	int mid = (left + right) >> 1;
	MergeSort2(arr, left, mid, temp);
	MergeSort2(arr, mid + 1, right, temp);
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	int index = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] > arr[begin2])
		{
			temp[index++] = arr[begin1++];
		}
		else
		{
			temp[index++] = arr[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		temp[index++] = arr[begin1++];
	}
	while (begin2 <= end2)
	{
		temp[index++] = arr[begin2++];
	}
	for (int i = left; i <= right; i++)
	{
		arr[i] = temp[i];
	}
}

//归并排序测试
void MergeTest(int num)
{
	int* arr = CreatRand(num);
	int* temp = (int*)malloc(sizeof(int) * num);
	MergeSort1(arr, 0, num - 1, temp);
	Print(arr, num);
	MergeSort2(arr, 0, num - 1, temp);
	Print(arr, num);
	free(temp);
}

//快速排序(非递归)--  升序-- 用数据结构的栈模拟递归
void QuickSortNonR1(int* arr, int num)
{
	Stack st;
	StackInit(&st);
	StackPush(&st, num - 1);
	StackPush(&st, 0);

	while (!StackEmpty(&st))
	{
		int left = StackTop(&st);
		StackPop(&st);
		int right = StackTop(&st);
		StackPop(&st);

		//单层快排--这里用挖坑法,也可用其他方法
		int begin = left;
		int end = right;
		int Pivot = begin;
		int key = arr[Pivot];
		while (begin < end)
		{
			while (begin < end && arr[end] >= key)
			{
				end--;
			}
			arr[Pivot] = arr[end];
			Pivot = end;

			while (begin < end && arr[begin] <= key)
			{
				begin++;
			}
			arr[Pivot] = arr[begin];
			Pivot = begin;
		}
		arr[Pivot] = key;
		//继续入栈
		if (Pivot + 1 < right)
		{
			StackPush(&st, right);
			StackPush(&st, Pivot + 1);
		}
		if (left < Pivot - 1)
		{
			StackPush(&st, Pivot - 1);
			StackPush(&st, left);
		}
	}
	//销毁
	StackDestroy(&st);
}

//快速排序(非递归)-- 降序 -- 用数据结构的栈模拟递归
void QuickSortNonR2(int* arr, int num)
{
	Stack st;
	StackInit(&st);
	StackPush(&st, num - 1);
	StackPush(&st, 0);

	while (!StackEmpty(&st))
	{
		int left = StackTop(&st);
		StackPop(&st);
		int right = StackTop(&st);
		StackPop(&st);

		int begin = left;
		int end = right;
		int Privot = left;
		int key = arr[Privot];
		while (begin < end)
		{
			while (begin < end && arr[end] <= key)
			{
				end--;
			}
			arr[Privot] = arr[end];
			Privot = end;
			while (begin < end && arr[begin] >= key)
			{
				begin++;
			}
			arr[Privot] = arr[begin];
			Privot = begin;
		}
		arr[Privot] = key;

		if (Privot + 1 < right)
		{
			StackPush(&st, right);
			StackPush(&st, Privot + 1);
		}
		if (left < Privot - 1)
		{
			StackPush(&st, Privot - 1);
			StackPush(&st, left);
		}
	}

	StackDestroy(&st);
}

//快速排序测试(非递归)
void QuickSortNonRTest(int num)
{
	int* arr = CreatRand(num);
	QuickSortNonR1(arr, num);
	Print(arr, num);
	QuickSortNonR2(arr, num);
	Print(arr, num);
	free(arr);
}

//归并排序(非递归)-- 升序 -- 用循环模拟递归
void MergeSortNonR1(int* arr, int num)
{
	int* temp = (int*)malloc(sizeof(int) * num);
	//gap表示每组数据个数
	int gap = 1;//刚开始一个一个归
	while (gap < num)
	{
		for (int i = 0; i < num; 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;
			//特殊情况:右半区间不存在,最后一组数据才会出现这种情况,所以可以直接break;
			if (begin2 >= num)
			{
				break;
			}
			//特殊情况:归并过程中右半区间算多了,需要修正
			if (end2 >= num)
			{
				end2 = num - 1;
			}
			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] < arr[begin2])
				{
					temp[index++] = arr[begin1++];
				}
				else
				{
					temp[index++] = arr[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				temp[index++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				temp[index++] = arr[begin2++];
			}
			//每两组合并就拷贝回去
			for (int j = i; j <= end2; j++)
			{
				arr[j] = temp[j];
			}
		}
		gap *= 2;
	}
	free(temp);
}

//归并排序(非递归)-- 降序 -- 用循环模拟递归
void MergeSortNonR2(int* arr, int num)
{
	int* temp = (int*)malloc(sizeof(int) * num);
	int gap = 1;
	while (gap < num)
	{
		for (int i = 0; i < num; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			if (begin2 >= num)
			{
				break;
			}
			if (end2 >= num)
			{
				end2 = num - 1;
			}
			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] < arr[begin2])
				{
					temp[index++] = arr[begin2++];
				}
				else
				{
					temp[index++] = arr[begin1++];
				}
			}
			while (begin1 <= end1)
			{
				temp[index++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				temp[index++] = arr[begin2++];
			}
			//拷贝
			for (int j = 0; j <= end2; j++)
			{
				arr[j] = temp[j];
			}
		}

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

//归并排序测试(非递归)
void MergeSortNonRTest(int num)
{
	int* arr = CreatRand(num);
	MergeSortNonR1(arr, num);
	Print(arr, num);
	MergeSortNonR2(arr, num);
	Print(arr, num);
	free(arr);
}

//基数排序 -- 依次从小到大的位排序
#define N 1000
int Temp[10][N];//0为填充值的桶数组

void RedixSort(int* arr, int num)
{
	int i = 0, j = 0;
	int pos;
	for (int k = 10; k < 1001; k *= 10)
	{
		for (i = 0; i < num; i++)
		{
			j = 0;
			pos = (arr[i] % k) / (k / 10);
			while (Temp[pos][j] != 0)
			{
				j++;
			}
			Temp[pos][j] = arr[i];
		}
		pos = 0;
		for (i = 0; i < 10; i++)
		{
			for (j = 0; j < num && Temp[i][j] != 0; j++)
			{
				arr[pos++] = Temp[i][j];
				//还原桶数组
				Temp[i][j] = 0;
			}
		}
	}
}

//基数排序测试
void RedixTest(int num)
{
	int* arr = CreatRand(100);
	RedixSort(arr, num);
	Print(arr, num);
	free(arr);
}

//计数排序--映射--绝对映射位置和相对映射位置(可以减少空间开辟)
//时间O(N+range)
//空间O(range)
void CountSort(int* arr, int num)
{
	//计算区间大小
	int max = arr[0], min = arr[0];
	for (int i = 0; i < num; i++)
	{
		if (arr[i] > max)
		{
			max = arr[i];
		}
		if (arr[i] < min)
		{
			min = arr[i];
		}
	}
	int range = max - min + 1;//(注意要+1,因为要算上被减的数)
	int* count = (int*)malloc(sizeof(int) * range);
	memset(count, 0, sizeof(int) * range);
	//统计次数
	for (int i = 0; i < num; i++)
	{
		//相对映射位置
		count[arr[i] - min]++;
	}
	//排序
	int j = 0;
	for (int i = 0; i < range; i++)
	{
		while (count[i]--)
		{
			//相对映射,要+min回去
			arr[j++] = i + min;
		}
	}
}

//计数排序测试
void CountTest(int num)
{
	int* arr = CreatRand(num);
	CountSort(arr, num);
	Print(arr, num);
	free(arr);
}

int main()
{
	//TestInsert(1000000);
	//TestShell(1000000);
	//TestHeap(1000000);
	//TestBubble(10);
	//TestSelect(10);
	//TestQuick(20);
	//QuickTest_LR(10);
	//QuickTest_BE(100);
	//MergeTest(10);
	//QuickSortNonRTest(1000);
	//MergeSortNonRTest(5);
	//RedixTest(10);
	//CountTest(100);

	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值