排序 -- 快速排序

排序 – 快速排序


一、快速排序(Hoare版本)

1.排序思想

快速排序Hoare版本的思想是:选择待排序序列中某元素为基准值key,进行排序,将序列分成两个子序列,要求左子序列的元素全部小于key,右边子序列的元素全部大于key,然后在左右子序列重复该过程,直到整个序列的元素都在相应的位置上。
请添加图片描述
单躺排序如上图所示,先选出一个key,一般选最左边或最右边的元素,然后左右两个指针对剩下的元素进行选择,右边选小,左边选大,然后左右两元素交换,就进行下一次选择,直到left和right两指针相遇,此时交换key和left的值,结束单躺排序。
单躺排序完成后,key就在正确的位置上,key就将原序列分割为两个子序列,如果左右两序列都有序,则整个序列有序,可以利用递归,分治左右区间。

左边做key,右边先走;
原因:因为要保证相遇位置的值小于key,right先走,right停下来后,left去遇right,相遇位置就是right停下来的位置,一定小于key。

2.代码示例

代码如下:

//交换
void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

//(Hoare版本)
//begin是序列的开头下标,end是序列的结尾下标

int PartSort1(int* a, int begin, int end)
{
	//单躺
	int left = begin;
	int right = end;
	int keyi = left;
	while (left < right)
	{
		//右边先走,找小
		while (left < right && a[right] >= a[keyi])
		{
			right--;
		}
		//左边再走,找大
		while (left < right && a[left] <= a[keyi])
		{
			left++;
		}
		//交换
		Swap(&a[left], &a[right]);
	}
	Swap(&a[keyi], &a[left]);//交换key和最左边的数据,key就在正确的位置上了
	keyi = left;//更新keyi

	return keyi;
}


void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)//如果区间不存在或者只有一个数据是,直接返回
	{
		return;
	}

	int keyi = PartSort1(a, begin, end);

	//递归调用[begin, keyi - 1], [keyi + 1, end]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

3.特性总结

1.快排的综和性能和使用场景都是比较好的;
2.时间复杂度:O(N * logN);
3.空间复杂度:O(logN);
4.稳定性:不稳定。

二、快速排序(挖坑法)

1.排序思想

快排挖坑法的基本思想是:与Hoare法相似,选取最左边或最右边的数作为key,将其取出来形成一个坑位,然后L和R分别找大和找小,R先走,找到小后,将其填入坑位,R所在位置形成新的坑位,此时L找大,找到后再填入坑位,如此往复,直到L和R相遇,将key放到此时的坑位中,key就到了正确的位置,单躺排序结束。
请添加图片描述
如上图所示,挖坑法思想与Hoare法大致相同,只是数据交换的细节有所差别。

2.代码示例

代码如下:

//挖坑法
int PartSort2(int* a, int begin, int end)
{
	int key = a[begin];
	int piti = begin;
	int left = begin;
	int right = end;
	
	while (left < right)
	{
		//右边先走,找小
		while (left < right && a[right] >= key)
		{
			right--;
		}
		//把right选到的数放到坑里,更新piti
		a[piti] = a[right];
		piti = right;

		//左边再走,找大
		while (left < right && a[left] <= key)
		{
			left++;
		}
		//把left选到的数放到坑里,更新piti
		a[piti] = a[left];
		piti = left;
	}
	//将key放入坑位中
	a[piti] = key;
	return piti;
}


void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)//如果区间不存在或者只有一个数据是,直接返回
	{
		return;
	}

	int keyi = PartSort2(a, begin, end);

	//递归调用[begin, keyi - 1], [keyi + 1, end]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

三、快速排序(前后指针法)

1.排序思想

快排前后指针法的基本思想:与Hoare法相似,选取最左边或最右边的数作为key。创建两个指针:prev和cur,初始时,prev指向序列开头,cur指向prev的下一个元素,让cur找小与key的元素,若cur指向的元素小于key,先将prev++,再交换cur与prev所指向的元素,然后cur++;若cur找到的元素大于key,则直接cur++。
请添加图片描述

此番操作,前面在遇到小于key的元素时,cur与prev都指向同一个元素,相当于自己交换,位置不变,而当遇到大于key的元素时,直接cur++,prev不动,此时prev与cur拉开了差距,prev的下一个元素就是大于key的元素,当cur再找到小于key的元素时,就会将两者交换。最终,当cur遍历完数组元素后,将key与prev的位置交换,单躺排序结束。排序的结果是小于key的都在左边,大于key的都在右边,key到了正确的位置。

2.代码示例

代码如下:

//交换
void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

//前后指针法
int PartSort3(int* a, int begin, int end)
{
	int prev = begin;
	int cur = prev + 1;
	int keyi = begin;

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

		cur++;
	}

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


void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)//如果区间不存在或者只有一个数据是,直接返回
	{
		return;
	}

	int keyi = PartSort3(a, begin, end);

	//递归调用[begin, keyi - 1], [keyi + 1, end]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

四、快速排序(非递归)

1.排序思想

快排非递归的基本思想:在快速排序的基础上,将递归改为非递归,其算法思想是一致的,只是实现方式不同。
非递归法的实现方式为:借助栈这一先入后出的数据结构,在每次单躺排序后,都将分割好的子区间入栈,然后进行循环,下次单躺排序时,再从栈中将子区间取出来,不断循环。

2.代码示例

代码如下:

void QuickSortNonR(int* a, int begin, int end)
{
	ST st;
	StackInit(&st);
	StackPush(&st, end);//先把区间入栈
	StackPush(&st, begin);

	while (!StackEmpty(&st))//当栈不为空时,循环
	{
		int left = StackTop(&st);//取出左边界
		StackPop(&st);

		int right = StackTop(&st);//取出右边界
		StackPop(&st);

		int keyi = PartSort3(a, left, right);//单躺排序

		if (keyi + 1 < right)//入栈右区间
		{
			StackPush(&st, right);
			StackPush(&st, keyi + 1);
		}

		if (keyi - 1 > left)//入栈左区间
		{
			StackPush(&st, keyi - 1);
			StackPush(&st, left);
		}

	}
	StackDestory(&st);
}

总结

快速排序是一种效率很高的排序算法,本文主要介绍了快速排序的基本写法及挖坑法、前后指针法、非递归法等多种方式的实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值