快速排序算法

文章详细介绍了快速排序算法的基本思想,包括选择基准值将数组分为小于等于和大于基准值两部分,以及递归实现排序的过程。讨论了快速排序在数组完全有序时的退化情况,提出了随机选择和三位取中法来优化基准值选取。此外,还介绍了一种非递归的快速排序实现,利用队列进行迭代排序。
摘要由CSDN通过智能技术生成


文章目录

  • 一、快速排序算法的基本思想
  • 二    快速排序的代码实现
  • 三    快速排序的退化
  • 四    非递归快速排序

一、快速排序算法的基本思想

快速排序算法是基于分治策略的一种排序算法。其基本思想是对数组nums[left:right]进行如下操作:

1 选取一个基准值mid(通常选为nums[left])将数组分为三个子数组nums[left:mid-1],nums[mid]和nums[mid+1:right] ,将原数组中所有小于等于nums[mid]的放到nums[left:mid-1]中,将原数组中所有大于nums[mid]的放到nums[mid+1:right]中。

2 通过递归调用快速排序算法,分别对nums[left:mid-1]和nums[mid+1:right]进行排序。

3 由于所有排序都在原数组中就地就行,所以在对nums[left:mid-1]和nums[mid+1:right]排序完成后,原数组已经有序。

如图所示,以ar[] = { 9,10,8,6,7,9,5,2 }为例

将数组首元素设置为基准值,定义两个指针分别指向数组首元素和尾元素,然后我们用j所指向的值和tmp进行比较,如果j所指向的值比tmp大,j继续向前移动,否则,我们交换j和i所指向的值,接着,我们操作i,如果i指向的值小于等于tmp,i继续向前移动,否则,我们交换i和j所指向的值,直到i和j相遇。

第一趟排序,由于j所指向的值小于tmp,所以我们交换i和j所指向的值,让i指针向后移动

 第二趟排序,由于i所指向的值大于tmp,交换i和j所指向的值,让j指针向前移动


 

第三趟排序,由于j所指向的值小于tmp,所以我们交换i和j所指向的值,让i指针向后移动

 第四趟排序,i在移动过程中,指向的值均小于等于tmp,所以i指针一直走,直到i和j相遇。此时

i和j所指向的位置即为基准值所在位置,此时上面所说的第一点完成,第二步同理,只需对基准值左右各进行如此操作,排序完成。

 二、快速排序的代码实现

//划分函数,功能将是小于等于tmp的元素放到数组左边,大于tmp的放右边
int Partition(int* nums, int left, int right)
{
	int i = left, j = right;
	int tmp = nums[i];
	while (i < j)
	{
		while (i < j && tmp < nums[j])  --j;
		if (i < j) nums[i] = nums[j];
		while (i < j && nums[i] <= tmp) ++i;
		if (i < j) nums[j] = nums[i];
	}
	nums[i] = tmp;
	return i;
}
void QuickSort(int* nums, int left, int right)
{
	if (left < right)
	{
	    int mid = Partition(nums, left, right);
		QuickSort(nums, left, mid - 1);
		QuickSort(nums, mid + 1, right);
	}
}

int main()
{
	int ar[] = { 9,10,8,6,7,9,5,2 };
	int n = sizeof(ar) / sizeof(ar[0]);
	QuickSort(ar, 0, n-1);
	for (auto x : ar)
	{
		cout <<x << " ";
	}
	cout << endl;
	return 0;
}

代码运行结果如下

 三、快速排序的退化

通过上面例子可以看出来,快速排序的运行时间与划分是否对称有关。数组越无序,快排速度越快。最坏情况元素全部有序时间复杂度为O(n2)  最好情况基值恰好为中值时间复杂度为O(nlog2n),所以基准值的选取十分重要,有两种解决办法1 随机选择策略 2 三位取中法。

1 随机选择策略,通过修改算法 Partition,可以设计出采用随机选择策略的快速排序算法。在快速排序算法的每一步中,当数组还没有被划分时,可以在nums[left:right]中随机选出一个元素作为划分基准,这样可以使划分基准的选择是随机的,从而可以期望划分是比较对称的。

//随机选择策略
int RandomPartition(int* nums, int left, int right)
{
	srand(time(nullptr));
	int rapos = rand() % ((left + right) / 2);
	std::swap(nums[left], nums[rapos]);
	return Partition(nums, left, right);
}

2 三位取中法我们只需在首中尾三个数据中,选择中间的值作为基准值进行排序,即可提高排序效率。

//三位取中法
int MedionThree(int* nums, int left, int right)
{
	static int mid = (left + right) / 2;
	if (nums[left] > nums[mid])
	{
		if (nums[mid] > nums[right])
			return mid;
		else
		{
			if (nums[right] > nums[left])
				return left;
			else
				return right;
		}
	}
	else
	{
		if (nums[left] > nums[right])
			return left;
		else
		{
			if (nums[right] > nums[mid])
				return mid;
			else
				return right;
		}
	}
}
int MedionPartition(int* nums, int left, int right)
{
	MedionThree(nums, left, right);
	return Partition(nums, left, right);
}

四、非递归快排

我们可以采用队列或栈来进行

void queue_QuickSort(int* nums, int left, int right)
{
	queue<int> qu;
	qu.push(left);
	qu.push(right);
	while (!qu.empty())
	{
		int sleft = qu.front();
		qu.pop();
		int sright = qu.front();
		qu.pop();
		int mid = Partition(nums, sleft, sright);
		if (sleft < mid - 1)
		{
			qu.push(sleft);
			qu.push(mid - 1);
		}
		if (mid + 1 < sright)
		{
			qu.push(mid + 1);
			qu.push(sright);
		}
	}
}

感谢观看!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值