数据结构:快速排序 三种选择方法

快速排序

最差情况:元素基本有序
在这里插入图片描述
类似单支树的结构
总比较次数2+3+4+…+N
时间复杂度为O(N^2)

最优情况:每次取的基准值都刚好将区间元素进行划分
在这里插入图片描述
二叉平衡树
高度logN
快排时间复杂度O(NlogN)

所以快速排序比较适合元素杂乱的场景

#include<stdio.h>

void Swap(int* left, int* right)
{
	int temp = *right;
	*right = *left;
	*left = temp;
}

//快排的基准值:三数取中法 降低取到极值问题
int GetMiddleIndex(int array[], int left, int right)
{
	int mid = left + ((right - left) >> 2);
	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[left])
			return left;
		else if (array[mid] < array[right - 1])
			return right - 1;
		else
			return mid;
	}
}
//划分方式一 O(N)
int Partion1(int array[], int left, int right)//默认初始基准值为最右侧
{
	int keyIndex = GetMiddleIndex(array, left, right);
	Swap(&array[keyIndex], &array[right - 1]);

	int begin = left;
	int end = right - 1;
	int key = array[end];
	while (begin < end)
	{
		while (begin < end && array[begin] <= key)
			begin++;
		while (begin < end && array[end] >= key)
			end--;
		if (begin < end)
		{
			Swap(&array[begin], &array[end]);
		}
	}
	if (begin != right - 1)
	{
		//Swap(&array[begin], &key);错误
		Swap(&array[begin], &array[right - 1]);
	}

	return begin;
}

//划分方式二:挖坑法  O(N)
int Partion2(int array[], int left, int right)//默认初始基准值为最右侧
{
	int begin = left;
	int end = right - 1;
	int key = array[end];//end现在是一个坑

	while (begin < end)
	{
		//begin从前往后找,找比基准值大的元素
		while (begin < end && array[begin] <= key)
			begin++;
		//找到比基准值大的元素
		if (begin < end)
		{
			array[end] = array[begin];
			end--;
		}
		//begin位置成为一个坑位
		//让end从后往前找比基准值小的元素,填充begin位置
		while (begin < end && array[end] >= key)
			end--;
		//找到比基准值小的元素
		if (begin < end)
		{
			//填充begin位置
			array[begin] = array[end];
			begin++;
		}
		//end为新坑位
	}
	//最后用key基准值将最后一个坑位填充
	array[begin] = key;
	return begin;
}

//划分方式三:前后指针 O(N)
//当prev和cur是一前一后的关系时,说明cur从前往后找的过程中暂时未遇到比基准值大的元素
//当prev和cur中间有间隔时,prev的下一个位置到cur之间的元素都是比基准值大的元素
int Partion3(int array[], int left, int right)//默认初始基准值为最右侧
{
	int cur = left;
	int prev = cur - 1;
	int key = array[right - 1];

	while (cur < right)
	{
		//让cur从前往后找,找比基准值小的元素
		//找到之后,如果prev的下一个位置和cur不相等则交换
		if (array[cur] < key && ++prev != cur)
		{
			Swap(&array[prev], &array[cur]);
		}
		++cur;
	}
	if (++prev != right - 1)
	{
		Swap(&array[prev], &array[right - 1]);
	}
	
	return prev;
	
}

void QuickSort(int array[], int left, int right)
{
	if (right - left <= 1)
		return;
	//在[left,right]区间找基准值,将区间分割成两部分
	int div = Partion1(array, left, right);
	//div为分割后基准值在数组的下标

	//递归基准值的左侧
	QuickSort(array, left, div);
	//递归基准值的右侧
	QuickSort(array, div + 1, right);
}

int main()
{
	int array[] = { 3,1,8,6,0,2,7,9,4,5 };
	//快速排序
	QuickSort(array, 0, sizeof(array) / sizeof(array[0]));
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", array[i]);
	}

	return 0;
}

上机实践:在这里插入图片描述

进一步提高排序效率
随着递归不断进行,区间中元素不断减少,少到一定规模,则不需要往下划分了,直接进行插入排序。
因为:当数量少时,适合插入排序——而且如果不断往下划分也会增加递归深度,如果元素过多递归深度可能会导致栈溢出,则可以设置一个阈值,当元素少于阈值时,采用插入排序。

如果数据量比较大,在没有达到阈值之前,都通过快速递归来划分区分的,通过递归分化时,数据量过大导致递归深度过深栈溢出:
1.可以设置快排递归的阈值,当递归的深度到达阈值,则不递归(阈值使用logN来计算)
2.为了不影响快排的时间复杂度:后序的分组直接采用堆排序来处理

采用堆排序优化原因是:堆排序时间复杂度也是O(NlogN)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值