快速排序和归并排序的非递归形式

        快速排序和归并排序都需要用递归的形式展开,那么有没有什么方法不需要递归就能实现归并和快速排序,有的!

 1.快速排序

        我们可以借助栈来模拟递归。

        递归的主要思想就是大事化小,小事化了。我们借助栈的 目的是将需要排序的“头” 和 “尾”找到,进而排序,然后找到合适的keyi并放入合适的位置,再将keyi两边的“头” 和 “尾”入栈,找到合适的keyi......如此重复下去.......

        keyi需要入栈吗?keyi不需要入栈,因为keyi已经在合适的位置了

快速排序和归并排序的非递归形式_第1张图片

快速排序和归并排序的非递归形式_第2张图片

快速排序和归并排序的非递归形式_第3张图片

前后指针法,结合上一篇博客来看, 

int PatrSort3(int* a, int begin, int end)
{

	int prev = begin, cur = begin+1;

	int keyi = begin;

	int midi = GetMidIndex(a , begin ,end);
	Swap(&a[keyi],&a[midi]);

	while (cur <= end)
	{
		//cur位置小于keyi发生交换
		if (a[cur] < a[keyi] && ++prev != cur)
		{
			//++prev;
			Swap(&a[prev], &a[cur]);
		}

		cur++;
	}

	Swap(&a[keyi],&a[prev]);

	keyi = prev;
	return keyi;

}
void QuickSortNonR(int* a, int begin, int end)
{
	ST st;
	STInit(&st);
	STPush(&st,end);
	STPush(&st, begin);

	while (!STEmpty(&st))
	{
		int left = STTop(&st);
		STPop(&st);

		int right = STTop(&st);
		STPop(&st);

		int keyi = PatrSort3(a,left,right);

		if (keyi + 1 < right)
		{
			STPush(&st,right);
			STPush(&st,keyi+1);
		}

		if (left < keyi - 1)
		{
			STPush(&st, keyi - 1);
			STPush(&st, left);
		}

	}

	STDestroy(&st);
}

2.归并排序

        归并排序的基本思想在上一篇已经讲过,快速排序是先对整个数组中选出适中的的keyi放到合适的位置,然后切分子块,层层递归,进行排序,快速排序类似于前序遍历。归并排序是先找到最小的子块,然后层层排序,层层归并,归并排序类似于后序遍历。

        归并排序还存在越界的问题:每个子区间应该怎么样确定,能给出明确的边界吗?

        边界是怎么确定的:

快速排序和归并排序的非递归形式_第4张图片

 

        gap每次增加是上一次的2倍,当gap = 4 时, 数组有10个元素,我们控制循环是按照一下的方式来控制的。

        当 i = 0 的时候 begin1 end1 ,begin2 end2 。[0 ,3]  [4 ,7]  。

        进入下一轮循环: i = 8 , begin1 end1 ,begin2 end2 。[8,11]  [12 ,15] 。

 

        此时问题来了,数组只有十个元素,下标为 10-15  已经越界访问了 ,这是我们要处理越界访问问题。

        

快速排序和归并排序的非递归形式_第5张图片

快速排序和归并排序的非递归形式_第6张图片

void MergeSortNonR(int* a, int n)
{

	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
	}

	int gap = 1;
	while (gap < n)
	{
		printf("gap:%d->", gap);
		for (int i = 0; i < n; i += 2*gap)
		{

		
			//[i,i+gap-1] [i+gap ,i + 2*gap-1] 控制边界
			int begin1 = i;
			int end1 = i + gap - 1;
			int begin2 = i + gap;
			int end2 = i + 2 * gap - 1;


			//越界,修正边界
			if (end1 >= n)
			{
				end1 = n - 1;

				// [begin2, end2]修正为不存在区间
				begin2 = n;
				end2 = n - 1;
			}
			else if (begin2 >=n)
			{
			// [begin2, end2]修正为不存在区间
				begin2 = n;
				end2 = n - 1;
			}
			else if (end2 >= n)
			{
				end2 = n - 1;
			}

			printf("[%d,%d] [%d, %d]--", begin1, end1, begin2, end2);

			int m = end2 - begin1+1;
			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) * m);


		}
	//	memcpy(a, tmp, sizeof(int) * m);
	
		printf("\n");
		
		gap *= 2;
 	}

	free(tmp);

}
void TestMergeSort()
{
	int a[] = { 9, 1, 2, 5, 7, 4, 8, 6, 3, 5 };
	//MergeSort(a, sizeof(a) / sizeof(int) );
	MergeSortNonR(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}

归并排序的特性总结:
        1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
        2. 时间复杂度:O(N*logN)
        3. 空间复杂度:O(N)
        4. 稳定性:稳定

快速排序和归并排序的非递归形式_第7张图片

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