快速排序

1、快速排序的思路:
  • 在一组数据中选择一个数作为基准值,记为pivot
  • 将数组中小于基准值的放在基准值的左边,大于基准值的放在基准值的右边;
  • 对每个分割出来的区间递归处理。

示意图:在这里插入图片描述

下面对以上三步各自做一定说明:
1.选择基准值pivot

1). 选择数组最左/右边的数;
2). 选择数组的最左边的数和最右边的数以及最中间的数,在三个数中任意选择一个;
3). 在数组中随机选择

2.数组分成小的区间(左边小右边大):

1). 左右指针法;
2). 挖坑法;
3). 前后指针法

2、区间划分:

快速排序的重难点就在于如何将数组分成小的区间,下面对这几种方法进行说明:
1). 左右指针法

思想 :
选取最右端的值为基准值;

  • 遍历整个数组,当begin指向的值小于基准值时,begin++;
    当begin指向的值大于基准值时,将begin指向的值和end指向的值进行交换;Meanwhile,当end指向的值大于基准值时,end–;
    当end指向的值小于基准值时,将begin指向的值和end指向的值进行交换;
  • 遍历完一次后,将此时begin指向的值和基准值进行交换

下图示是第一次将数组排序成左小右大的过程,而快速排序整体上来看是一个递归的过程,因此每次进入Partition函数时,基准值pivot都会有所更新。
左右指针法
2).挖坑法

思想:

  • 选取最右端的值为基准值;
  • 遍历整个数组,当begin指向的值小于基准值时,begin++;
    当begin指向的值大于基准值时,将begin指向的值赋给基准值,此时,begin所指向的位置为空
    Meanwhile,当end指向的值大于基准值时,end–;
    当end指向的值小于基准值时,将end指向的值赋给begin指向的空值;
  • 两者交替进行;
  • 遍历结束,将基准值赋给begin指向的位置

图示是一次遍历的过程:
挖坑法

3).前后指针法

思想:

  • 选取最右端的值为基准值;
  • 用cur遍历整个数组,当cur指向的值大于基准值时,cur++;
    当cur指向的值小于基准值时,将cur指向的值和div指向的值进行交换;
  • 交换完毕后,div++
  • 遍历结束,交换cur和div的值

图示是一次遍历的过程:
在这里插入图片描述

3、稳定性、时间复杂度与空间复杂度:

在这里插入图片描述
如图示:快速排序是一个简而治之的过程,即一分二,二分四……
划分完的小区间数呈指数性增长。设划分结束后,共有n个区间,则总共划分的次数为log(n),而在每一个小区间内都需要遍历一次,则它的时间复杂度为n * log(n)。
快速排序是一个递归的过程,参考上面的树,即可得出空间复杂度为log(n)。

稳定性:快速排序显然是不稳定
时间复杂度:
最好:O(n * log(n))
平均:O(n * log(n))
最坏:O(n^2)
空间复杂度:
最好:O(log(n))
平均:O(log(n))
最坏:O(n)

最后附上代码:

void Swap(int *a, int *b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

int Partition_1(int array[], int left, int right)//左右下标
{
	int begin = left;
	int end = right;
	int pivot = array[right];
	//当pivot初值选择的是数组最右端的数时,要先走begin,
	//否则如果序列已经有序,begin所在的数大小并没有参与比较,无法得出它的大小

	while (begin < end)
	{
		while (begin < end&&array[begin] <= pivot)
		{
			begin++;
		}
		if(begin == end)
		{
			break;
		}
		while (begin < end&&array[end] >= pivot)
		{
			end--;
		}
		if(begin == end)
		{
			break;
		}
		Swap(array + begin, array + end);
	}
	Swap(array + begin, array + right);
	return begin;
}


int Partition_2(int array[], int left, int right)//挖坑
{
	int begin = left;
	int end = right;
	int pivot = array[right];

	while (begin < end)
	{
		while (begin < end&&array[begin] <= pivot)
		{
			begin++;
		}
		array[end] = array[begin];
		while (begin < end&&array[end] >= pivot)
		{
			end--;
		}
		array[begin] = array[end];
	}
	array[begin] = pivot;
	return begin;
}

int Partition_3(int array[], int left, int right)//前后指针
{
	int div = left;
	int pivot = array[right];
	for (int cur = left; cur <= right; cur++)
	{
		if (array[cur] < pivot)
		{
			Swap(array + cur, array + div);
			div++;
		}
	}
	Swap(array + div, array + right);
	return div;
}

void _QuickSort(int array[], int left, int right)
{
	if (left == right)
	{
		return;
	}
	if (left > right)
	{
		return;
	}
	int div = Partition_3(array, left, right);
	_QuickSort(array, left, div - 1);
	_QuickSort(array, div + 1, right);
}


void QuickSort(int array[], int size)
{
	_QuickSort(array, 0, size - 1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值