归并排序与计数排序

目录

一、归并排序

        1、递归实现:

2、非递归实现:

二、计数排序:

三、完整代码:


一、归并排序

        1、递归实现:

思路:

首先定义tmp指针指向一个与原数组大小一样的空间,作用是存放拷贝过去的值。

然后再将要分解的序列的中间值给算出来,就知道区间了,就可以进行递归分解了,

最后在返回时进行重新排序合并,再将虚拟数组的值拷贝回去就可以了。

void _MergeSort(int* a, int begin ,int end ,int* tmp)
{
	if (begin >= end)
		return;

	int mid = (begin + end) / 2;
	_MergeSort(a, begin, mid, tmp);
	_MergeSort(a, mid + 1, end, tmp);

	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] <= a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}
	while (begin1 <= end1)
	{	
		tmp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}

	memcpy(a+begin, tmp+begin, sizeof(int)*(end - begin + 1));
}

void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	if (tmp == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	_MergeSort(a, 0, n - 1, tmp);
	
	free(tmp);
}

解析:

1、有一个MergeSort函数,创建一个tmp指针指向一个与原数组大小一样的空间,作用是存放拷贝过去的值。

2、_MergeSort是归并排序的核心,分割后的区间[begin,mid][mid+1,end]所以将上述两个进行递归即可。

3、具体合并操作:

首先定义begin1,end1为一个的区间,begin2,end2为另一个区间,然后进行判断大小,越小的那个放在tmp中,最后如果二者只要有一个走完了,就可以进行判断,将未走完的那个数组剩下的元素放在tmp的后面。

4、最后将tmp中的值拷贝回数组a中即可。

整体思路如下:

2、非递归实现:

非递归实现的时候,定义一个gap(这个是归并过程中,每一组数据的个数)

依然需要嵌套循环来完成这个非递归的实现

所以就需要将上述的begin1,end1,begin2,end2改为

因为是取左闭右闭的区间,所以end1需要i+gap后-1(跳过每组数据的个数)

begin2比end1大1,end就需要跳过两个gap所以乘以2。

特别注意!!!!!!!!

这里因为end1,begin2,end2,是加上gap的,当gap较大时就会发生越界访问,所以要对它们进行越界处理。

三种情况的越界:

1、end1,begin2,end2越界,解决方法:不归并了(修正为一个不存在的区间)

2、begin2,end2越界,解决方法:不归并了

3、end2越界,解决方法:修正end2继续归并

上述为修改方法,begin2 = n,end2 = n-1就会使得这个区间不存在

void MergeSortRenN(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	
	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			int j = i;
			if (end1 >= n)
			{
				end1 = n - 1;
				begin2 = n;
				end2 = n - 1;
			}
			else if (begin2 >= n)
			{
				begin2 = n;
				end2 = n - 1;
			}
			else if (end2 >= n)
			{
				end2 = n - 1;
			}
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] <= a[begin2])
					tmp[j++] = a[begin1++];
				else
					tmp[j++] = a[begin2++];
			}
			while (begin1 <= end1)
				tmp[j++] = a[begin1++];
			while (begin2 <= end2)
				tmp[j++] = a[begin2++];
		}

		memcpy(a , tmp , sizeof(int) * n);
		gap *= 2;
	}
	free(tmp);
}

二、计数排序:

思路:

遍历一遍原数组,然后统计每一个元素出现的个数放在计数数组中(计数数组的大小通过原数组的end-begin+1来计算)(计数数组的初始化用calloc来初始化,这样可以将数组中的元素全员设置为0方便计数),在通过遍历计数数组中每一个元素出现的次数情况进行排序。

比如:2出现了3次,3出现了2次,5出现了2次......

而有:2,2,2,3,3,5,5......

注意:要使用相对位置映射计数,这样的话可以避免待排序数组较大时,在前面开辟过大的空间造成空间浪费。

局限性:只适合范围集中且范围不大的整形数组,不适合范围较大或者非整型的排序。

 

优化:计数时,将所记的数减去这个数组中最小的数,来达到相对位置映射,记得在最后加回那个最小的数字即可

void CountSort(int* a, int n)
{	
	//初始化
	int max = a[0];
	int min = a[0];
	for (int i = 0; i < n ; i++)
	{
		if (a[i] > max)
		{
			max = a[i];
		}
		if (a[i] < min)
		{
			min = a[i];
		}
	}
	int range = max - min + 1;
	int* countiA = (int*)calloc(range, sizeof(int));
	if (countiA == NULL)
	{
		perror("calloc fail");
		return;
	}
	//计数
	for (int i = 0; i < n; i++)
	{
		countiA[a[i] - min]++;
	}
	//排序
	int j = 0;
	for (int i = 0; i < range; i++)
	{
		while (countiA[i]--)
			a[j++] = i + min;
	}
	free(countiA);
}	

三、完整代码:

代码的实现:

void _MergeSort(int* a, int begin ,int end ,int* tmp)
{
	if (begin >= end)
		return;

	int mid = (begin + end) / 2;
	_MergeSort(a, begin, mid, tmp);
	_MergeSort(a, mid + 1, end, tmp);

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

	memcpy(a+begin, tmp+begin, sizeof(int)*(end - begin + 1));
}

void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	if (tmp == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	_MergeSort(a, 0, n - 1, tmp);
	
	free(tmp);
}

void MergeSortRenN(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	
	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			int j = i;
			if (end1 >= n)
			{
				end1 = n - 1;
				begin2 = n;
				end2 = n - 1;
			}
			else if (begin2 >= n)
			{
				begin2 = n;
				end2 = n - 1;
			}
			else if (end2 >= n)
			{
				end2 = n - 1;
			}
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] <= a[begin2])
					tmp[j++] = a[begin1++];
				else
					tmp[j++] = a[begin2++];
			}
			while (begin1 <= end1)
				tmp[j++] = a[begin1++];
			while (begin2 <= end2)
				tmp[j++] = a[begin2++];
			//memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
		}

		memcpy(a , tmp , sizeof(int) * n);
		gap *= 2;
	}
	free(tmp);
}

void CountSort(int* a, int n)
{	
	//初始化
	int max = a[0];
	int min = a[0];
	for (int i = 0; i < n ; i++)
	{
		if (a[i] > max)
		{
			max = a[i];
		}
		if (a[i] < min)
		{
			min = a[i];
		}
	}
	int range = max - min + 1;
	int* countiA = (int*)calloc(range, sizeof(int));
	if (countiA == NULL)
	{
		perror("calloc fail");
		return;
	}
	//计数
	for (int i = 0; i < n; i++)
	{
		countiA[a[i] - min]++;
	}
	//排序
	int j = 0;
	for (int i = 0; i < range; i++)
	{
		while (countiA[i]--)
			a[j++] = i + min;
	}
	free(countiA);

测试:

test.c

void MergeSortTest()
{
	int a[] = { 10,6,7,1,3,9,4,2,0 ,11,16,14,13};
	int n = sizeof(a) / sizeof(a[0]);
	Print(a, n);
	MergeSort(a, n);
	Print(a, n);
}


void MergeSortRenNTest()
{
	int a[] = { 10,6,7,1,3,9,4,2,0 ,11,16,14,13 };
	int n = sizeof(a) / sizeof(a[0]);
	Print(a, n);
	MergeSortRenN(a, n);
	Print(a, n);
}


void CountSortTest()
{
	int a[] = { 10,6,7,1,3,9,4,2,0 ,11,16,14,-13,12,-7,-5 };
	int n = sizeof(a) / sizeof(a[0]);
	Print(a, n);
	CountSort(a, n);
	Print(a, n);
}

int main()
{
	MergeSortTest();
	MergeSortRenNTest();
	CountSortTest();
	return 0;
}

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值