交换排序(2)--快速排序3种实现方案及其优化

快速排序

      快排的整体思想是找出一个key值,比key的值小的都在它的左边,比key大的值都在它的右边。这样就划分了左右两个区域,分别找左右两个区域的key值继续划分左右区间。

 


找中间值的位置有三种方法

(1)左右指针法

         思想:在序列的左右各定义begin,和end这两个指针。begin++往右走,当遇到比key大的值停下。end--往左走,遇

到比key小的值后停下。此时交换begin和end下标所代表的值。(使得比key大的数在右边,比key小的数在左边)。当

begin和end相遇时,把这个值和key值交换。此时序列中比key大的数都在key右边,比key小的数都在key左边(分成了

左右两个区间)。把key的下标返回,继续找区间的key。





int PartSort1(int* a, int left, int right)//6---1左右指针法
{
	int mid = GetMidIndex(a, left, right);
	swap(a[mid], a[right]);        //优化1 三数取中法,避免KEY值是最大值或最小值。

	int begin = left;
	int end = right;
	int key = a[right];
	while (begin < end)
	{
		while (begin < end&&a[begin] <= key)
		{
			++begin;
		}
		while (begin < end&&a[end] >= key)
		{
			--end;
		}

		if (begin < end)
		{
			swap(a[begin], a[end]);
		}
	}
	swap(a[begin], a[right]);
	return begin;
}

(2)挖坑法

思想:1  选择最右的值为key值,并把key值得位置当做第一个坑,找到合适的数来填坑。依旧定义begin和end两个最左

最右的指针。2   让begin++,先往左走,当遇到比key大的数停下,此时拿begin位置上的数去填第一个坑,此时坑的位

置在begin的位置上。3  再让end--,往右走,当遇到比key小的数停下,拿end位置上的数去填坑(坑此时在begin的置)。

当填完后,坑现在在end的位置上。4  重复上述动作,直到end和begin相遇,此时坑在begin和end位置上,拿key值来填

这个坑。






int PartSort2(int* a, int left, int right)//6--2 挖坑法,先把key的值得位置当成坑
{
	int mid = GetMidIndex(a, left, right);
	swap(a[mid], a[right]);    //优化三数取中法

	int begin = left;    //定义begin
	int end = right;     //定义end
	int key = a[right];  //定义key
	while (begin < end)   //循环  当begin<end时继续
	{
		while (begin < end&&a[begin] <= key)  //a[begin]<key,begin继续往左走
		{
			++begin;
		}
		a[end] = a[begin];   //填坑-->赋值
		while (begin < end&&a[end] >= key)
		{
			--end;
		}
		a[begin] = a[end];
	}
	a[begin] = a[end] = key;  //循环出来,begin=end  ,把key值给到这个位置上填坑
	return begin;   //任意返回begin或者end
}


(3)前后指针法:

         思想:1定义一个下标cur,刚开始赋值为序列的最左。再定义一个prev,赋值为cur-1,意思是cur的前面一个数的下标。2  用cur下标代表的数与key比较,若比key值大,则cur++往右走。若比key值小,先让prev++往右走,在比较prev和cur是不是在同一个位置上,若不在同一个位置,则,交换prev和cur所代表的数值。cur继续往前走。




int PartSort3(int* a, int left, int right)//  6---3前后指针法
{
	int mid = GetMidIndex(a, left, right);
	swap(a[mid], a[right]);     
	
	int cur = left;
	int prev =cur-1;
	int key = a[right];
	while (cur < right)
	{
		if (a[cur] < key&&++prev != cur)
		{
			swap(a[prev], a[cur]);
		}
		++cur;
	}
	++prev;
	swap(a[prev], a[right]);
	return prev;
}


2种优化方案

1 三数取中法

思想:避免key值取到最大值或者最小值。在right,left,mid三个代表的下标中选取中间数。这样是为了划分左右区间时左右尽量平衡。
//优化快排  三数取中。避免a[div]是最大值或最小值
int GetMidIndex(int* a, int left, int right)
{
	int mid = left + (right - left) / 2;
	if (a[left] > a[mid])
	{
		if (a[mid] > a[right])
			return mid;
		else if (a[right] > a[left])
			return left;
		else
			return right;
	}
	else//a[left]<a[mid]
	{
		if (a[mid] < a[right])
			return mid;
		else if (a[right] < a[left])
			return left;
		else
			return right;
	}
}


2 小区间优化

思想:在区间范围较小时,不选择快排算法。而选择插入算法。这样是算法的优化。

void QuickSort(int*a, int left, int right) //6 快速排序
{
	if (left < right)
	{
		int div = PartSort1(a, left, right);
		if (right - left < 5)     //小区间优化
		{
			InsertSort(a,right-left+1);
		}
		else  //若左右区间大的话直接还是用快排方法。
		{
			QuickSort(a, left, div - 1);//左
			QuickSort(a, div + 1, right);//右
		}
	}
}


快排非递归:借助栈结构

void QuickSortNR(int *arr, int begin, int end)
{
     assert(arr);
     stack<int> s;

     s.push(begin);
     s.push(end);
     while (!s.empty())
     {
          int left = s.top();
          s.pop();
          int right = s.top();
          s.pop;
          int div = PartSort3(arr, left, right);
          if (div - 1 > left)
          {
              s.push(div - 1);
              s.push(left);
          }
          if (div + 1 < right)
          {
              s.push(right);
              s.push(div + 1);
          }
     }
}

总结:

快速排序对于小规模的数据集性能不是很好。没有插入性能高。

快速排序算法使用了分治技术,最终来说大的数据集都要分为小的数据集来进行处理。

当数据集较小时,不必继续递归调用快速排序算法,使用插入排序代替快速排序。(小区间优化)

快速排序是时间复杂度是O(N*LogN).

                  最好情况:O(N*LogN).

                  最坏情况:O(N^2)   :当序列中的数值都相等时,快排是最坏的情况。

空间复杂度:O(lgN)

快排是一种不稳定的排序算法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值