针对相同元素的快速排序(算法导论7-2)

当考虑到具有与主元素相同的元素存在时,算法将数组分成三部分,分别小于主元素,等于主元素,大于主元素,然后递归排序第一部分和第三部分,重点是该怎样通过一次循环便可以达到划分目的,因为循环越少,常数因子越小:

代码如下:

#include<iostream>
using namespace std;
void partition(int A[], int p, int r, int &q, int &t)
{
	// A[q..t]中所有元素都等于主元素
	// A[p..q-1]中每个元素都小于主元素
	// A[t+1..r]中每个元素都大于主元素
	int x = A[r];
	q = p-1, t = r;
	for (int i = p; i < r; i++)
	{
		if (i >= t)
			break;
		while (A[i] > x && i < t)
			swap(A[--t], A[i]);
		if (A[i] < x)
			swap(A[++q], A[i]);
	}
	swap(A[t], A[r]);
	q++;
}
void quick_sort(int A[], int p, int r)
{
	if (p < r)
	{
		int q, t;
		partition(A, p, r, q, t);
		quick_sort(A, p, q - 1);
		quick_sort(A, t + 1, r);
	}
}
void main()
{
	while (1)
	{
		cout << "输入数组个数:" << endl;
		int n;
		cin >> n;
		int A[1000];
		cout << "输入数组元素:" << endl;
		for (int i = 0; i < n; i++)
			cin >> A[i];
		quick_sort(A, 0, n - 1);
		cout << "排序后如下:" << endl;
		for (int i = 0; i<n; i++)
		{
			cout << A[i] << " ";
		}
		cout << endl;
	}
	
}

重点是函数partition(),该函数的参数是数组,数组起始索引p,数组的最后一个元素的索引r, 用于划分数组的索引q和t

函数执行完成后有:

1. A[q..t]中所有元素都等于主元素

2. A[p..q-1]中每个元素都小于主元素

3.  A[t+1..r]中每个元素都大于主元素

下面证明其正确性:

其中x是A[r]的值,

构造的循环不变式如下:

1.  当 p <= k <= q, A[k] < x

2. 当 q+1 <= k <= i-1 A[k] = x

3. 当 t <= k <= r-1 A[k] > x

初始化:q = p-1, 所以不变式1中的k取空集,不变式1成立;i=p,不变式2中的k取空集,成立:t = r,不变式3中的k取空集,成立

保持:假设第i次迭代前循环不变式成立,当进行第i次迭代时,经过while循环,A[i]如果大于x,那么t先自减,然后交换A[i]和A[t],如果还是大于,那么t继续自减然后交换,直到A[i]不再大于x。易知while循环结束后,索引t不断左移,循环不变式3始终成立,由于索引i未变,所以循环不变式1和2也成立。

          然后判断A[i]是否小于x,如果小于,则q自增,交换A[i]和A[q], 交换之后A[q]小于x,所以循环不变式1仍然成立。由循环不变式2的假设可知在第i次迭代之前,A[q+1] 等于x, 所以第i次迭代中q自增并且与A[i]交换后,A[i]等于x。因此,在第i次迭代之后,循环不变式2也成立。由于在交换A[i]和A[q]时候索引t不变,所以循环不变式3成立,因此经过第i次迭代后,循环不变式1,2,3始终成立,得证。

一开始写代码时候,函数partition()没有while循环,取而代之的是一个if语句,判断A[i]是否大于x,大于则与A[--t]交换。然后构造了以上循环不变式,发现没法保证循环不变式不变,发现改成了while循环就可以保证循环不变式不变了,可以看出循环不变式的思想真的很强大,不仅仅可以证明已有算法的正确性,甚至可以帮助创造正确的算法。

猜你喜欢

转载自blog.csdn.net/qq_25974431/article/details/81222237
7-2
今日推荐