快速排序 递归实现 三种思路 (hoare版本) ( 挖坑法) (前后指针法)

hoare版本

思路 :

        先保存left的下标到keyi  右边先走找小于keyi的值 当找到了right指向小于keyi的值  左边再走 找大于key的值,当找到了 left指向大于keyi的值 若找到比keyi的元素并且也找到比keyi小的元素就交换两个元素的位置    继续 right往前走 找到小  left 往回后找大 直到循环结束(left不小于right)

        循环结束 left 和right 指向同一个元素, 因为右边先走 所以right指向一定是比key小的元素 交换 keyi 和 right 的位置 让keyi到正确的位置  返回新的keyi

        当第一趟排序结束 得到两个条件 1.keyi到达正确的位置 2.keyi左边都是比keyi小的元素  keyi右边都是比keyi大的元素  

int PartSort1(int* a, int left, int right)
{
	//int keyi = a[left];
	int keyi = left;

	while (left < right)
	{
		//1(left<right)防止越界  右边先走 找小于keyi的值
		while ((left<right)&& a[right] >= a[keyi])
		{
			--right;
		}
		//2(left<right)防止越界 左边再走 找大于keyi的值
		while ((left < right) && a[left] <= a[keyi])
		{
			++left;
		}
		//3找到大于keyi的值 并且找到小于keyi的值 就交换它们的位置
		Swap(&a[left], &a[right]);
		//继续 1 2 3 直到不满足循环条件 
	}
	//当循环结束 left == right 
	//因为left先走 所以相遇位置的值一定小于keyi  交换keyi 和right的位置
	Swap(&a[keyi], &a[right]);
	return right; // 返回新的keyi
}

挖坑法

思路:

        保存left的值 到达keyi中  使left成为第一个坑位 然后右边先走 找小于key的值, 放进坑位 自己成为新的坑位  左边再走 找大于key的值, 放进坑位 自己成为新的坑位 

        当循环结束 left 和 right 相遇 因为left先走所以 相遇位置一定是坑位 再把keyi放进坑位返回新的keyi
        当第一趟排序结束 得到两个条件 1.keyi到达正确的位置 2.keyi左边都是比keyi小的元素  keyi右边都是比keyi大的元素  

//挖坑法
int PartSort2(int* a, int left, int right)
{
	//保存left的值 使left成为第一个坑位
	int keyi = a[left];
	int hoult = left;

	while (left < right)
	{
		//右边先走 找小于key的值, 放进坑位 自己成为新的坑位
		while ((left < right) && a[right] >= keyi)
		{
			--right;
		}
		a[hoult] = a[right];
		hoult = right;

		//左边再走 找大于key的值, 放进坑位 自己成为新的坑位
		while ((left < right) && a[left] <= keyi)
		{
			++left;
		}
		a[hoult] = a[left];
		hoult = left;
	}
	//因为left先走所以 相遇位置一定是坑位 再把keyi放进坑位
	a[hoult] = keyi; 
	return hoult;//返回新的keyi
}

前后指针法

思路:

         prev 指向 left  cur 指向left的下一个  cur 小于 keyi ++prev 再交换cur 和prev的值  cur++ 
当 cur指向的元素 大于 keyi  cur就和 prev 拉开了距离 继续 

        当 循环结束 prev 再上一轮已经和cur 交换过了 所以 prev 指向的元素一定是小于 keyi的    再交换 keyi 和 prev 的位置 让keyi达到正确的位置

        当第一趟排序结束 得到两个条件 1.keyi到达正确的位置 2.keyi左边都是比keyi小的元素  keyi右边都是比keyi大的元素

​
int PartSort3(int* a, int left, int right)
{
	int keyi = left;
	int prev = left;
	int cur = prev + 1;

	//[left,right]
	while (cur <= right)
	{
		//防止越界
		if ((cur <= right) && a[cur] < a[keyi]) // cur 小于 keyi ++prev 再交换cur 和prev的值  cur++ 
		{
			Swap(&a[cur], &a[++prev]);
		}
		++cur;//当 cur指向的元素 大于 keyi  cur就和 prev 拉开了距离

	}
	//当 循环结束 prev 再上一轮已经和cur 交换过了 所以 prev 指向的元素一定是小于 keyi的
	Swap(&a[prev], &a[keyi]);
	return prev;
}

​

快速排序

思路:

        第一趟排序后
            1.keyi的左边都是比keyi小的元素 keyi的右边都是比keyi大的元素
             2.keyi到达正确的位置

        

        所以 keyi的位置不用动
                    继续排序 keyi的左边 和keyi的右边 有序 整体有序
                              keyi的左边              keyi的右边
                            [begin,keyi-1] keyi [k
eyi+1,end]

//快速排序
void QuickSort(int* a, int begin, int end)
{
	//若 begin > end 说明 数组不存在  那还排个毛线 直接返回
	// begin == end 说明 剩下一个元素 不用再排序了 直接返回
	if (begin >= end)
		return;

	int keyi = PartSort2(a,begin,end);
	//第一趟排序后 
	//1.keyi的左边都是比keyi小的元素 keyi的右边都是比keyi大的元素
	//2.keyi到达正确的位置
	
	//所以 keyi的位置不用动
	//继续排序 keyi的左边 和keyi的右边 有序 整体有序
	//  keyi的左边         keyi的右边
	//[begin,keyi-1] keyi [keyi+1,end]

	QuickSort(a, begin, keyi - 1); //排序  keyi的左边 使keyi的左边有序
	QuickSort(a, keyi + 1, end); //排序  keyi的右边 使keyi的右边有序

}

三目取中

思路:

        在数组中 起始位置left 结束位置 end  中间位置 mid  获取次大的元素 再和 left交换 把  次大的元素换到 起始位置  直接给快速排序加个 极速  

 最理想的递归情况:

最坏的情况:升序或降序

升序 或降序 快速排序的效率特别菜

再它前面加一个三目取中 会好很多


int GetMid(int* a, int left, int right)
{
	int mid = (left + right) / 2;

	if (a[mid] > a[left])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		//mid 是最大的
		else if (a[left] > a[right])
		{
			//返回次大的
			return left;
		}
		else //a[left] <a[right]
		{
			//返回次大的
			return right;
		}
	}
	//a[mid] <a[left]
	else
	{
		//left是最大的
		if (a[mid] > a[right])
		{
			return mid;
		}
		//mid 是最小的
		else if (a[left] > a[right])
		{
			//返回次大的
			return left;
		}
		else //a[left]<a[right]
		{
			//返回次大的
			return right;
		}
	}
}

      PartSort1  hoare版本中

int mid =  GetMid(a, left, right);
	
	Swap(&a[mid], &a[left]);

.....
其余不变 

小区间优化

        快速排序最好的情况下, 也避免不了递归的深入 每一层 的递归都 是以2倍的形式快速增长

        最好办法加一个判断  小区间优化  当数组剩下的元素个数 小于10 不再递归 需要单独处理,使用InsertSort插入剩下的元素

    

 

        数组剩下的元素可能在 开始  中间 结束 任意 位置  所以InsertSort的参数 不能是(a ,n) 而是

[a+begin] 是 当前 数组剩下的元素的开始位置 ,[end-begin+1] 是当前数组剩下的元素个数

/快速排序
void QuickSort2(int* a, int begin, int end)
{
	//若 begin > end 说明 数组不存在  那还排个毛线 直接返回
	// begin == end 说明 剩下一个元素 不用再排序了 直接返回
	if (begin >= end)
		return;

	if ((end - begin + 1) > 10)
	{
		int keyi = PartSort2(a, begin, end);
		//第一趟排序后 
		//1.keyi的左边都是比keyi小的元素 keyi的右边都是比keyi大的元素
		//2.keyi到达正确的位置

		//所以 keyi的位置不用动
		//继续排序 keyi的左边 和keyi的右边 有序 整体有序
		//  keyi的左边         keyi的右边
		//[begin,keyi-1] keyi [keyi+1,end]

		QuickSort2(a, begin, keyi - 1); //排序  keyi的左边 使keyi的左边有序
		QuickSort2(a, keyi + 1, end); //排序  keyi的右边 使keyi的右边有序
	}
	else//小区间优化 当数组剩下的元素个数 小于10 不再递归 
	{
		InsertSort(a + begin, end - begin + 1);
	}

}

非递归快速排序

思路:

        使用栈 先把R 的下标放进栈再 把 L的下标放进栈 

        栈不为空 获取栈的元素放进 left 和 right的中 再删除栈里面的元素  使用 hoare版本单趟排序
因为栈的特点:后进先出 所以 先把 keyi 右边的放入栈(放进之前需要判断下序列只有一个元素或是不存在) 再把 keyi左边的放入栈 同样(放进之前需要判断下序列只有一个元素或是不存在) 重复执行操作 直到 栈为空


void QuickSortNonR(int* a, int begin, int end)
{
	Stack sl; 

	StackInit(&sl); //初始化栈

	//栈的特点: 后进先出
	//所以 先进 右边 再进 左边
	StackPush(&sl, end); //先把 右边的下标进栈
	StackPush(&sl, begin);//再把 左边的下标进栈

	while (!StackEmpty(&sl))
	{
		int left = StackTop(&sl);//待排的L
		StackPop(&sl); //删除L

		int right = StackTop(&sl);//待排的R
		StackPop(&sl);//删除R

		//         Hoare版本
		int keyi = PartSort1(a, left, right);

		//[begin, keyi-1] keyi [keyi+1, end]
		//1.keyi的左边都是比keyi小的元素 keyi的右边都是比keyi大的元素
		//2.keyi到达正确的位置

		//所以 keyi的位置不用动
		//继续排序 keyi的左边 和keyi的右边 有序 整体有序
		//  keyi的左边         keyi的右边
		//[begin,keyi-1] keyi [keyi+1,end]

		//栈的特点: 后进先出
		//keyi的 右边 先进栈
		if (keyi + 1 < right)
		{
			StackPush(&sl, right); 
			StackPush(&sl, keyi+1);
		}
		//keyi的 左边 再进栈
		if (left < (keyi - 1))
		{
			StackPush(&sl, keyi-1);
			StackPush(&sl, begin);
		}
	}

	//销毁栈
	StackDestory(&sl);
}

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值