模拟实现模拟实现快速排序递归和非递归

快速排序

快速排序是一种二叉树结构交换排序算法,在日常的使用当中较为频繁的使用;
交换的思想就是 :就是将序列中的两个值值进行比较,然后根据结果交换两个值在序列中的位置,以升序为例,较大值就想序列尾部移动,较小值向序列前部移动;
快排的思想就是:在待排序的元素当中选择一个元素为基准值,按照该排序要求将序列分为两个子序列,左边子序列元素均小于基准值(左右子序列均不包含该基准值),右边子序列均大于基准值,然后左右子序列重复上述过程,直到所有元素排列到对应位置为止;
在这里插入图片描述

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

  1. 左右指针法(Hoare版本)
  2. 前后指针法
  3. 挖坑法

对方法逐个实现
左右指针法:
注意:以右边为基准值,左边先走,以左边为基准值,右边先走;

int PartSort1(int* arr,int left,int right)//左右指针法
{
	//优化
	int mid = GetMindIndex(arr, left, right);
	Swap(&arr[mid], &arr[right]);
	
	int key = arr[right]; // 选择基准值
	int key_index = right;
	
	//升序 left找大 , right找小
	//降序 left找小 , right找大
	while(left < right)
	{
		while(left < right && arr[left] <= key)
		{
			++left;
		}
		while(left < right && arr[right] >= key)
		{
			--right;
		}
		
		//各自找到时,进行交换
		Swap(&arr[left],&arr[right]);
	}
	//左后left和right相遇后,将基准值与当前位置交换
	Swap(&arr[left],&arr[key_index]);
	return left;
}

前后指针法:
注意:prev要么就在cur的前一位,要么和cur相隔一段距离,prev始终指向相对当前cur位置前一个小于key的值的位置,如果相隔一段距离,则prev下一位就是大于key的,此时交换cur和prev;

int PartSort2(int* arr,int left,int right)
{
	//优化
	int mid = GetMindIndex(arr, left, right);
	Swap(&arr[mid], &arr[right]);
	
	int cur = left;
	int prev = left -1;
	int key = arr[right]//基准值
	
	while(cur < right)
	{
		if(arr[cur] < key && ++prev != cur)
		{
			Swap(&arr[prev],&arr[cur]);
		}
		++cur;
	}
	++prev;
	Swap(&arr[prev],&arr[right]);
	return prev;
}

挖坑法:
注意:最后注意要填坑

int PartSort3(int* arr,int left,int right)
{
	//优化
	int mid = GetMindIndex(arr, left, right);
	Swap(&arr[mid], &arr[right]);
	
	int key = arr[right];//基准值

	while(left < right)
	{
		while(left < right && arr[left] <= key)
		{	
			++left;
		}
		Swap(&arr[left],&arr[right]);//填右边的坑
		
		while(left < right && arr[right] >= key)
		{
			--right;
		}
		Swap(&arr[right],&arr[left]);//填左边的坑
	}	
	//left = right两者相遇,左后用基准值填坑,序列划分为两个子序列
	arr[left] = key;//填坑
	return left;
}

快速排序实现:
注意:将待排序序列不断进行递归划分左右子序列,知道所有元素排序到位为止

void QucikSort(int* arr,int n)
{
	if(left >= right)
		return ;
	int key_index = PartSort1(arr,left,right);
	QucikSort(arr,left,key_index-1);
	QucikSort(arr,key_index+1,right);
}

复杂度总结:
根据快速排序是一种二叉树型结构,可知:二叉树有lgN层,总共有那N个元素
在这里插入图片描述
时间复杂度 O(N*logN)
空间复杂度 O(logN)
稳定性 : 不稳定

快速排序优化

我们知道,快速排序是一种二叉树结构的交换排序算法,可知当要排序的序列是有序的,那么二叉树就会退化成单支树,那么排序的效率将会大大降低,那么快速排序就没有什么优势可言,所以我们将快速排序进行优化,
方法一:三数取中法
方法二:递归到小的子区间时,可以考虑使用插入排序

三数取中法:用于上面三种方法中
注意:该方法主要避免形成单支树这种情况

int GetMindIndex(int* arr,int left,int right)
{
	int mid = left + (right - left)/2;
	if(arr[left] < a[mid]) // left < mid
	{
		if (a[mid] < a[right]) //left < mid <right
			return mid;
		else if (a[left] > a[right]) // right < left < mid
			return left;
		else
			return right;//left < right < mid
	}
	else //left > mid
	{
		if (a[right] < a[mid]) right < mid < left
			return mid;
		else if (a[right] < a[left]) mid < left < right
			return left;
		else
			return right;mid < right < left
	}
}

递归到小的子区间时,可以考虑使用插入排序:
当递归到小区间时,如果元素已经接近有序,但是还有快排的话,递归下去反而降低了效率,这是数据呈现接近有序和数据量小的特性,使用直接插入更加合适,就能提高效率;也是主要针对待排序序列为有序序列这种情况

优化后的快速排序:

void QucikSort(int* arr,int left,int right)
{
	if(left >= right)
		return ;
	if(right - left + 1 > 10)
	{
		int key_index = PartSort1(arr,left,right);
		QucikSort(arr,left,key_index-1);
		QucikSort(arr,key_index+1,right);
	}
	else//优化方法二
	{
		InsertSort(arr, (right - left + 1));//自己实现
	}
}
非递归的快速排序

尽管上面的快排我们已经进行了优化,但是他执行还是以递归的方式执行,但是递归有一个很重要的缺点就是栈空间溢出的问题,如果待排序的数据量很大,那么很容易造成栈溢出,反而很麻烦。因此我们可以将递归改为非递归,这样就避免了递归栈溢出的问题,这样快速排序就更加完善了!!!!

思路: 可知,递归调用不断的划分待排序序列,将问题从大化小,我们可以使用一种数据结构来模拟函数调用时栈帧这种情况-------栈,利用栈的先进后出原理就和递归相似,不断将分组后的边界位置存入栈中,然后取出进行排序知道栈为空为止;

#include<stack> //C语言下需要自己实现
void QucikSort(int* arr,int left,int right)
{
	stack<int> st;
	st.push(left);
	st.push(right);

	while(!st.empty())
	{
		int end = st.top();
		st.pop();
		int begin = st.top();
		st.pop();

		int mid = PartSort1(arr, begin, end);
		//int mid = PartSort2(arr, begin, end);
		//int mid = PartSort3(arr, begin, end);

		if (begin < mid - 1)
		{
			st.push(begin);
			st.push(mid - 1);
		}

		if (mid + 1 < end)
		{
			st.push(mid + 1);
			st.push(end);
		}
	}
}

到此快速排序介绍完了,但是要理解:"物理上:我们操作的是一个数组,逻辑上我们操作的是一个二叉树结构"

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值