欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

排序 - 归一化 - 非递归归一化

最编程 2024-03-04 14:17:11
...

进行非递归归并, 归并的过程也是一种分治, 不过利用了一个gap值控制每次归并的分治区间大小。直接上代码


//归并非递归
void MergeSortRno(int* a, int sz) 
{
	int* tmp = (int*)malloc(sizeof(int) * sz);
	//
	int gap = 1;
	while (gap < sz) 
	{
		
		gap *= 2;
	}
}

着段代码的意思是, 从区间为1开始, 归并之后。 区间乘以二。然后再进行归并。 然后再乘以二, 再进行归并。 最后一次归并可能很巧合gap就是sz / 2,这个时候不需要进行其他操作。 但是如果最后一次归并gap 比 sz / 2大的话。 那么就需要额外的操作。 这个问题会放到后面说, 这里先不讨论。 先将归并的内容实现。

//归并非递归
void MergeSortRno(int* a, int sz) 
{
	int* tmp = (int*)malloc(sizeof(int) * sz);
	//
	int gap = 1;
	while (gap < sz) 
	{
		for (int i = 0; i < sz; i += 2 * gap) 
		{
            
		}

		gap *= 2;
	}
}

归并的时候每gap是一个区间, 每两个区间进行归并。 当gap很小的时候, 很显然这个数组一定不是只有一两个区间。 这些区间进行归并的时候, 应该将所有的区间两两归并。 从下标为0, 向后依次进行。 

这里for循环就是来完成这个操作的,如图为gap为1的时候进行的归并, 每两个区间进行归并,一共五组, 从左到右依次进行:

然后具体的递归过程就和递归归并的归并过程大概是一样的了。

//归并非递归
void MergeSortRno(int* a, int sz) 
{
	int* tmp = (int*)malloc(sizeof(int) * sz);
	//
	int gap = 1;
	while (gap < sz) 
	{
		for (int i = 0; i < sz; i += 2 * gap) 
		{

            //归并过程
			int begin1 = i;
			int end1 = i + gap - 1;
			int begin2 = i + gap;
			int end2 = i + 2 * gap - 1;
			
			if (end1 > sz || begin2 > sz) 
			{
				break;
			}
			if (end2 > sz) 
			{
				end2 = sz - 1;
			}

			int j = i;
			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;
	}
}

这里之所以说是大概, 就是因为这里有个超出范围的坑。 我们进行非递归归并的过程过程中可能遇到这种情况。

就比如我们这个十个大小的数组,当gap为2的时候范围就出问题了。如图:

当gap为二的时候, 第一组进行归并的是0-1区间和2-3区间归并。 第二组区间分别是4-5和6-7归并。 第三组本来应该是8-9和10-11, 但是10-11不在数组里面。我们是没有访问权限的。

所以这就出问题了。 那么解决问题的方式就是对着种越界情况进行判断。

我们分析这种越界情况有几种?

第一种:第一个区间的右区间超出范围, 这种显然不成立。 

第二种:第二个区间的左区间超出范围, 当第一个区间的右区间超出时这种就一定成立。

第三种:第二个区间的左区间没有超范围, 也就是第一种和第二种情况都不符合的情况下右区间超出了范围。 

那么解决方法就是这几行代码:

	if (end1 > sz || begin2 > sz) 
	{
		break;
	}
	if (end2 > sz) 
	{
		end2 = sz - 1;
	}

首先第一个if的意思就是第一种情况或者第二种情况。 这个时候break就行了。 最后一组的第二个区间不存在, 不需要进行归并, 直接跳出循环就行了。 

第二个if就是在第一种情况合第二种情况都不符合的情况下可能符合。 这个时候最后一组的第二个区间内有数据, 但是和其他的区间数据个数不同, 虽然不同,但它也是可以进行归并的。 所以我们只要修改右区间的范围就行了。