Ⅰ. 归并排序
1. 基本思想
归并排序( MERGE-SORT )是建立在归并操作上的一种有效的排序算法 , 该算法是采用分治法( Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
图示如下
 
  
2. 递归代码实现
//打印数组内容
void ArrPrint(int* a, int n)
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}
void _MergeSort(int* a, int begin, int end, int* tmp)
{
	//当右边的下标小于左边时,结束递归
	if (begin >= end)
	{
		return;
	}
	//取中间数mid,将当前内容二分为[begin, mid],[mid+1, end]
	int mid = (begin + end) / 2;
	//如果需要排序后半的话就必须重置一个新的begin来保证是从后半的位置开始(而非0处)
	int begin1 = begin;
	//如果需要排序前半的话就必须重置一个新的end来保证是在前半的位置结束(而非n-1处)
	int end1 = mid;
	//保证形式的统一
	int begin2 = mid + 1;
	int end2 = end;
	_MergeSort(a, begin1, end1, tmp);
	_MergeSort(a, begin2, end2, tmp);
	//在从最后的递归返回后,返回的是前后两个有序的数组
	//起点与终点分别是arr1:[begin1, end1] arr2:[begin2, end2]
	//单趟归并
	int begin3 = begin1;//记录前数组begin1的位置,作为tmp的起始位置
	//当遍历完两个数组中的其中一个时,结束循环
	while (begin1 <= end1 && begin2 <= end2)
	{
		//两数组中较小的数取出并存放在tmp对应位置上
		if (a[begin1] < a[begin2])
		{
			tmp[begin3++] = a[begin1++];
		}
		else
		{
			tmp[begin3++] = a[begin2++];
		}
	}
	//将剩下那个数组的值接在tmp上
	//两个数组都进行判断,因为有一个数组已经完成排序不会进入循环
	while (begin1 <= end1)
	{
		tmp[begin3++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[begin3++] = a[begin2++];
	}
	//将tmp数组值拷贝回a
	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}
void MergeSort(int* a, int n)
{
	//创建一个大小与a相同的数组,归并返回数据
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
	//传入数组左右下标进行排序
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
	tmp = NULL;
}我们用以下代码来验证
void test()
{
	int a[] = { 5,6,7,4,1,9,2,8,3,0 };
	int sz = sizeof(a) / sizeof(a[0]);
	MergeSort(a, sz);
	ArrPrint(a, sz);
}
int main()
{
	test();
	return 0;
}有
3. 非递归形式
对于有2的n次方个数据,如下的一组数据归并有

观察可以发现,第一次归并的间距为2(即2-0),第二次的间距为4(即4-0),由此我们可以得到如下代码
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
	// 间隙为1,也代表2个一组进行归并
	int gap = 1;
	// 间隙为n/2时,代表n个一组归并
	while (gap < n)
	{
		// 保证每次刚好归并排序对应2组的数据
		for (int i = 0; i < n; i += gap * 2)
		{
			// 确定前后两组的头与尾
			int begin1 = i, end1 = begin1 + gap - 1;
			int begin2 = begin1 + gap, end2 = begin2 + gap - 1;
			// 初始化tmp中开始储存数据的位置为begin1
			int begin3 = begin1;
			// 归并数据
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] <= a[begin2])
				{
					tmp[begin3++] = a[begin1++];
				}
				else
				{
					tmp[begin3++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[begin3++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[begin3++] = a[begin2++];
			}
			// 从a+i的位置将有效数据从tmp全部拷贝回a
			memcpy(a + i, tmp + i, sizeof(int)*(begin3 - i));
		}
		gap *= 2;
	}
}而在大多数时候,数据个数并不总是2的n次方个数,因此需要我们作出修改,一般来说有如下几种情况

因此,有两种方案
一、将每次边界在数组外的部分存放在tmp中,最后一次性拷贝回a中
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
	// 间隙为1,也代表2个一组进行归并
	int gap = 1;
	// 间隙为n/2时,代表n个一组归并
	while (gap < n)
	{
		// 保证每次刚好归并排序对应2组的数据
		for (int i = 0; i < n; i += gap * 2)
		{
			// 确定前后两组的头与尾
			int begin1 = i, end1 = begin1 + gap - 1;
			int begin2 = begin1 + gap, end2 = begin2 + gap - 1;
			// 初始化tmp中开始储存数据的位置为begin1
			int begin3 = begin1;
			//第1种情况
			if (end1 >= n)
			{
				// 将[begin1,end1]这一部分调整为end1截止,后面部分为不存在的区间
				end1 = n - 1;
				begin2 = n;
				end2 = n - 1;
			}
			else if(begin2 >= n)// 第2种
			{
				// 将[begin1,end1]这一部分调整为end1截止,后面部分为不存在的区间
				begin2 = n;
				end2 = n - 1;
			}
			else if (end2 >= n)// 第3种
			{
				// 将[begin2,end2]这一部分调整为end2截止
				end2 = n - 1;
			}
			// 手动打印边界查看范围
			printf("[%d,%d][%d,%d]", begin1, end1, begin2, end2);
			// 归并数据
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] <= a[begin2])
				{
					tmp[begin3++] = a[begin1++];
				}
				else
				{
					tmp[begin3++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[begin3++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[begin3++] = a[begin2++];
			}
		}
		// 将tmp的所有数据拷贝回a
		memcpy(a, tmp, sizeof(int) * n);
		printf("\n");
		gap *= 2;
	}
}验证有

二、将每次边界在数组外的部分放弃存放在tmp中,每次拷贝tmp的一部分回a中
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
	// 间隙为1,也代表2个一组进行归并
	int gap = 1;
	// 间隙为n/2时,代表n个一组归并
	while (gap < n)
	{
		// 保证每次刚好归并排序对应2组的数据
		for (int i = 0; i < n; i += gap * 2)
		{
			// 确定前后两组的头与尾
			int begin1 = i, end1 = begin1 + gap - 1;
			int begin2 = begin1 + gap, end2 = begin2 + gap - 1;
			// 初始化tmp中开始储存数据的位置为begin1
			int begin3 = begin1;
			// 第1,2种情况
			if (end1 >= n || begin2 >= n)
			{
				// 只要end1或begin2越界,就放弃本次[begin1,end1]的排序
				break;
			}
			else if(end2 >= n)// 第三种
			{
				// begin2未越界end2越界,将end2调整为最后一个元素进行排序
				end2 = n - 1;
			}
			// 手动打印边界查看范围
			printf("[%d,%d][%d,%d]", begin1, end1, begin2, end2);
			// 归并数据
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] <= a[begin2])
				{
					tmp[begin3++] = a[begin1++];
				}
				else
				{
					tmp[begin3++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[begin3++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[begin3++] = a[begin2++];
			}
			// 将tmp中对应数据数据拷贝回a
			memcpy(a + i, tmp + i, sizeof(int) * (begin3 - i));
		}
		printf("\n");
		gap *= 2;
	}
}验证有

Ⅱ. 计数排序
1. 基本思想
计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。 操作步骤:1. 统计相同元素出现次数2. 根据统计的结果将序列回收到原来的序列中
即 
  
2. 代码实现
//打印数组内容
void ArrPrint(int* a, int n)
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}
void CountSort(int* a, int n)
{
	// 选出相对区间内的最大值与最小值
	int min, max;
	max = min = a[0];
	for (int i = 0; i < n; i++)
	{
		if (max < a[i])
		{
			max = a[i];
		}
		if (min > a[i])
		{
			min = a[i];
		}
	}
	// 创建一个大小数值在[min,max]内的临时数组
	int* count = (int*)calloc((max - min + 1), sizeof(int));
	if (count == NULL)
	{
		perror("calloc fail");
		return;
	}
	for (int i = 0; i < n; i++)
	{
		// a[i] ∈ [min,max]
		// a[i]-min ∈ [0,max-min]
		// 因此对应下标++就代表对应数值+1
		count[a[i] - min]++;
	}
	int j = 0;
	// 将数值不为0的依次取出复制到原数组中
	for (int i = 0; i < max - min + 1; i++)
	{
		// 对应计数数组为0时表明不含该元素
		while (count[i]-- != 0)
		{
			a[j++] = i + min;
		}
	}
}在此,我们用下面的代码来验证
void test()
{
	int a[] = { 100,105,104,102,103,104,107,100,105,106 };
	int sz = sizeof(a) / sizeof(a[0]);
	CountSort(a, sz);
	ArrPrint(a, sz);
}
int main()
{
	test();
	return 0;
}有



















