数据结构【初阶】--排序(归并排序和基数排序)

目录

一.归并排序的非递归写法

1.思想应用 

2.代码基本实现 

(1)单趟归并逻辑 

(2)多趟(循环)的控制条件

① 迭代条件:i+=2*gap

② 结束条件:i(或i<=n-2*gap)<>

(3)代码展示 

① 单趟逻辑

②整体逻辑 

3.优化代码 

(1)end1和begin2越界 

(2)begin2不越界而end2越界

 二.计数排序

1.思想应用 

2.(直接映射)逻辑图示 

3.优点以及局限性 

4.针对分散的数据进行优化 

(1)(相对映射)图示解析 

(2)代码实现 


一.归并排序的非递归写法

1.思想应用 

采用思想:一个数肯定是有序的,就每一个数和另一个数归并成一个含有两个数的有序序列,然后一个含有两个数的有序序列在和另一个含有两个数的有序序列归并成一个含有四个数的有序序列,依次类推,最后整体有序。 

  • 图示 

 

数据结构【初阶】--排序(归并排序和基数排序)_第1张图片

2.代码基本实现 

(1)单趟归并逻辑 

  • 图示 

数据结构【初阶】--排序(归并排序和基数排序)_第2张图片 

(2)多趟(循环)的控制条件

① 迭代条件:i+=2*gap
  • 图示 
数据结构【初阶】--排序(归并排序和基数排序)_第3张图片
② 结束条件:i

数据结构【初阶】--排序(归并排序和基数排序)_第4张图片 

(3)代码展示 

① 单趟逻辑
    int* tmp = (int*)malloc(sizeof(int) * n);//开辟一个新数组
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
    int gap = 1;
	for (size_t i = 0;i
  • 效果 

数据结构【初阶】--排序(归并排序和基数排序)_第5张图片 

②整体逻辑 
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
	int gap = 1;
	while (gap < n)//控制多趟
	{
		for (size_t i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			//[begin1,end1][begin2,end2]--归并区间
			int j = begin1;

			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) * 2 * gap);//每归并两个数就拷贝回去
		}
		gap *= 2;
	}
	free(tmp);
}
  • 效果演示 

数据结构【初阶】--排序(归并排序和基数排序)_第6张图片 

  •  存在问题 

此样例给的数组数据个数恰好可以通过测试,但是代码本身存在问题导致其并不适用于所有样例,例如:

数据结构【初阶】--排序(归并排序和基数排序)_第7张图片

 我们观察一下分割的区间来看一看问题所在:

下面对代码进行改进。

3.优化代码 

(1)end1和begin2越界 

由于begin1=i,所以可以判断begin1是不可能越界的,那么越界的就可能是end1和begin2。(这里先不考虑begin2不越界而end2越界的情况。)

  • 解决方式 :发生越界时直接跳出循环
	if (end1 >= n || begin2 >= n)//begin2或end1越界
	{
		break;
	}

(2)begin2不越界而end2越界

  • 解决方式:修改end2的值,并且调整拷贝数据的大小 
//优化部分
if (end1 >= n || begin2 >= n)//begin2或end1越界
{
	break;
}
if (end2 >= n)
{
	end2 = n - 1;
}

memcpy(a + i, tmp + i, sizeof(int) * (end2-i+1));
  • 完整代码 
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
	int gap = 1;
	while (gap < n)
	{
		for (size_t i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			//[begin1,end1][begin2,end2]--归并区间
			int j = begin1;
			if (end1 >= n || begin2 >= n)//begin2或end1越界
			{
				break;
			}
			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));//每归并两个数就拷贝回去
		}
		gap *= 2;
	}
	free(tmp);
}
  • 效果 

数据结构【初阶】--排序(归并排序和基数排序)_第8张图片 

 二.计数排序

1.思想应用 

思想:计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。

操作步骤

  1. 统计相同元素出现次数
  2. 根据统计的结果将序列回收到原来的序列中 

2.(直接映射)逻辑图示 

 数据结构【初阶】--排序(归并排序和基数排序)_第9张图片

 

3.优点以及局限性 

优点

  1. 效率极高
  2. 时间复杂度为O(aN+countN(有一个范围))

局限性

1.不适合分散的数据,更适合集中的数据

2.不适合浮点数、字符串、结构体数据排序,只适合整数

4.针对分散的数据进行优化 

(1)(相对映射)图示解析 

数据结构【初阶】--排序(归并排序和基数排序)_第10张图片 

(2)代码实现 

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* count = (int*)calloc(range, sizeof(int));
	if (count == NULL)
	{
		printf("calloc fail\n");
		return;
	}
	//统计次数
	for (int i = 0; i < n; i++)
	{
		count[a[i] - min]++;
	}
	//排序
	int i = 0;
	for (int j = 0; j < range; j++)
	{
		while (count[j]--)
		{
			a[i++] = j + min;//把相对映射还原回去
		}
	}
}
  • 结果 

数据结构【初阶】--排序(归并排序和基数排序)_第11张图片 

你可能感兴趣的:(数据结构,排序算法)