快速排序实现

目录

1.基本思想

2.主框架实现

2.划分方式

 2.1hoare版本

 2.2挖坑法  

 2.3前后指针 

3.快速排序优化 

4.快速排序非递归 

5.快速排序特性总结:


1.基本思想

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右 子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。(一般基准值取最左侧或左右侧元素) 

2.主框架实现

void QuickSort(int array[],int left,int right)
{
	if ( right-left <= 1)
		return;
	//按照基准值将对数组array的[left,right)区间元素进行划分
	int div = Partion2(array, left, right);
	//划分成功后,数组形成[left,div)和[div+1.right)两部分
	//递归排[left,div)
	QuickSort(array, left, div);
	//递归排[div+1.right)
	QuickSort(array, div + 1, right);
}

2.划分方式

 将区间按照基准值划分为左右两半部分的常见方式有:

 2.1hoare版本

重复以下步骤,直到划分成功:(当begin=end时循环结束) 

 a.先让begin从前往后找,找比基准值大的元素,找到之后停止

 b.再让end从后往前找,找比基准值小的元素,找到之后停止

 c.让后将begin位置上大于基准值的元素与end位置上小于基准值的元素进行交换

 当循环结束后,将begin位置上的元素和区间最右侧的元素交换

 最后返回begin或者end即可 

 具体实现:

//1.hoare版本
int Partion1(int array[], int left,int right)
{
	int keyIndex = GetMiddleIndex(array, left, right);
	Swap(&array[keyIndex], &array[right - 1]);

	int begin = left;//begin不能从0开始
	int end = right-1;
	int key = array[end];//选取最右边的元素为基准值
	while (begin < end)
	{
		//让begin从前往后找,找比基准值大的元素
		while (begin < end && array[begin] <= key)
			begin++;
		//让end从后往前找,找比基准值小的元素
		while (begin < end && array[end] >= key)
			end--;
		//找到后交换两个元素的值
		if (begin < end)
		{
			Swap(&array[begin], &array[end]);
		}
		
	}
	//交换begin(或者end)位置元素与基准值array[rigt-1]的值
	if (begin == end)
	{
		Swap(&array[begin], &array[right - 1]);
	}
	

 2.2挖坑法  

 循环以下操作:(当begin=end时循环结束) 

        将基准值挖走,end位置形成一个坑

        1.让begin从前往后找,找比基准值大的元素,然后用begin位置上的元素填end位置上的坑,begin位置又形成了新坑

        2.让end从后往前找,找比基准值小的元素,然后用end位置上的元素填begin位置上的坑,end位置又形成了新坑

当循环结束之后,用key填充最后一个坑

最后返回begin

int Partion2(int array[], int left, int right)
{
	int keyIndex = GetMiddleIndex(array, left, right);
	Swap(&array[keyIndex], &array[right - 1]);

	int begin = left;//begin不能从0开始
	int end = right - 1;
	int key = array[end];//end位置现在就是一个坑
	while (begin < end)
	{
		//让begin从前往后找比基准值大的元素
		while (begin < end && array[begin] <= key)
			begin++;
		//找到比基准值大的元素后,把该元素赋值给end位置的元素,填掉end位置的坑
		//end-- 往前找比基准值小的元素
		if (begin < end)
		{
			array[end] = array[begin];
			end--;
		}
		//begin位置为一个新的坑位
		//end从后往前找比基准值小的元素,找到后将该元素赋值给begin位置的元素,填begin的坑
		while (begin < end && array[end] >= key)
			end--;
		if (begin < end)
		{
			//填掉begin位置的坑
			//begin++继续往后找比基准值大的
			array[begin] = array[end];
			begin++;
		}	
		//end现在又是一个新坑
	}
	//最后需要用基准值填最后一个坑位
	array[begin] = key; //begin 与 end相等
	return begin;
}

 2.3前后指针 

 

循环以下操作:

        1.让cur从左至右找比基准值小的元素

        2.找到之后如果prev的下一个位置和cur的位置不相等,则交换两个位置的元素,若相同cur继续往后找,不交换

找完整个数组后,若prev的下一个位置不在基准值的位置,则将prev下个位置的元素与基准值交换

最后返回prev

//3.前后指针
int Partion3(int array[], int left, int right)
{
	int cur = left;
	int prev = cur - 1;

	int keyIndex = GetMiddleIndex(array, left, right);
	Swap(&array[keyIndex], &array[right - 1]);
	int key = array[right - 1];//选最右边元素为基准值
	while (cur < right)
	{
		//让cur从前往后找,找比及准准小的元素
		//若没找到,cur则继续往后找
		//找到之后,如果prev的下一个位置和cur的位置不相等则交换两个位置的元素,若相同,cur继续往后找,不交换
		if (array[cur] < key && ++prev != cur)
		{
			Swap(&array[cur], &array[prev]);
		}
		++cur;
	}
	//找完整个数组后,若prev的下一个位置不在基准值的位置,则将prev下个位置的元素与基准值交换
	if (++prev != right - 1)
	{
		Swap(&array[prev], &array[right - 1]);
	}
	return prev;//返回基准值的下标
}

排序结果展示:

3.快速排序优化 

   1.三数取中法选key(array[left]、array[mid]、array[right-1]中取中间值)

int GetMiddleIndex(int array[], int left, int right)
{
	int mid = left + ((right - left) >> 1);
	if (array[left] < array[right-1])
	{
		if (array[mid] < array[left])
			return left;
		else if (array[mid] > array[right-1])
			return right-1;
		else
			return mid;
	}
	else
	{
		if (array[mid] < array[right - 1])
			return right - 1;
		else if (array[mid]>array[left])
			return left;
		else
			return mid;
	}
}

   2.递归到小的子区间时,可以考虑插入排序

4.快速排序非递归 

void QuickSortNor(int array[], int size)
{
	Stack s;
	StackInit(&s);

	StackPush(&s, size);
	StackPush(&s, 0);

	while (!StackEmpty(&s))
	{
		// 获取区间的右边界
		int left = StackTop(&s);
		StackPop(&s);

		// 获取区间的左边界
		int right = StackTop(&s);
		StackPop(&s);

		// [left, right)中的区间进行划分
		if (right - left <= 1)
			continue;

		int div = Partion(array, left, right);

		// 基准值的左侧[left, div)
		// 基准值的右侧[div+1,right)

		// 将基准值的右侧压栈
		// 先压右侧然后再压区间的左侧
		StackPush(&s, right);
		StackPush(&s, div + 1);

		// 将基准值的左侧压栈
		// 先压右侧然后再压区间的左侧
		StackPush(&s, div);
		StackPush(&s, left);
	}

	StackDestroy(&s);
}

 

5.快速排序特性总结:

  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

  2. 时间复杂度:O(N*logN)

  3. 空间复杂度:O(logN)

          最坏情况:当每次取到的基准值是最小或者最大元素时(或者接近最大或最小),时间复杂度为O(N^2)

  4. 稳定性:不稳定

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值