排序之快速排序

一、基本思想

快速排序的基本思想都是大的数据往后放,小的数据往前放,key放在对应的正确的位置上,且由key划分的区间两边数据所在区间的位置都是正确

二、递归实现

1.方法一:双向扫描法

两边同时扫描,begin遇到比key大的停下来,而end遇到比key小的停下来,然后两者交换,直到两个指针相遇(注意两个元素组成的有序区间的情况)

void QuickSort(int arr[], int left,int right)
{
	assert(arr);
	if (left >= right)
		return;

	int div = PartSort1(arr, left, right);
	QuickSort(arr, left, div - 1);
	QuickSort(arr, div + 1, right);
}<pre name="code" class="cpp">
 


//(1)大的数据往后翻,小的数据往千帆,key在正确的位置上

int PartSort1(int arr[], int left, int right)//返回key位置对应的下标
{
	int key = arr[right];
	int begin = left;
	int end = right - 1;


	while (begin < end)
	{
		while (begin<end&&arr[begin] <= key)
		{
			++begin;
		}
		while (begin < end&&arr[end]>=key)
		{
			--end;
		}
		if (begin < end)
		{
			swap(arr[begin], arr[end]);
		}
	}

	if (arr[begin]>arr[right])//由于begin先,所以begin所在的位置一定是大于key值所在的位置
	{
		swap(arr[begin], arr[right]);
		return begin;
	}
	else//两个元素组成的区间有序
	{
		return right;
	}
}


2、方法二:挖坑法

本质上与双向扫描法相同,但是这次是去挖一个坑,准备埋left和right遍历过程中需要停止的那个点,进行交换,只不过这个方法原有的key的值被覆盖了,而上一个方法中key的值最后才交换

void QuickSort(int arr[], int left,int right)
{
	assert(arr);
	if (left >= right)
		return;

	int div = PartSort2(arr, left, right);
	QuickSort(arr, left, div - 1);
	QuickSort(arr, div + 1, right);
}

//2、挖坑法[left,right]
int PartSort2(int arr[], int left, int right)
{
	int key = arr[right];

	while (left < right)
	{ 
		while (left < right&&arr[left] <= key)
		{
			++left;
		}
		if (left < right)
		{
			arr[right--] = arr[left];
		}
		while (left<right&&arr[right]>=key)
		{
			--right;
		}
		if (left < right)
		{
			arr[left++] = arr[right];
		}
	}
	arr[left] = key;//<span style="color:#ff0000;">注意是left</span>
	return left;
}


3、方法三:单向扫描法

就是用prev标记比key小的数字区间的最后一个元素的下标,然后不断遍历,不断扩大小的数字的区间,最后将key的值放入到小的数的区间的最后一个元素的下一个位置

void QuickSort(int arr[], int left,int right)
{
	assert(arr);
	if (left >= right)
		return;

	int div = PartSort3(arr, left, right);
	QuickSort(arr, left, div - 1);
	QuickSort(arr, div + 1, right);
}


//3.单向扫描法
//(思想相同但是这个可以给单链表快速排序,因为不同倒着遍历链表)
int PartSort3(int arr[], int left, int right)
{
	int key = arr[right];
	int prev = left - 1;//指向大的数据前一个位置,用来换大
	int cur = left;//用来找小
	while (cur < right)
	{
		if (arr[cur] < key&&++prev != cur)
		{
			std::swap(arr[prev], arr[cur]);
		}
		++cur;
	}
	std::swap(arr[++prev], arr[right]);
	return prev;
}

三、优化方法

1、三数取中法

上述实现的代码key都是选取最后一个元素,当然也可以选取第一个元素。

快速排序当序列本身是有序的时候是最坏的情况,时间复杂度为O(N^2),当每次选取的key恰好在中间时是最优的,时间复杂度为log2N

所以使用三数取中法进行优化,防止有序是最坏的情况

//优化;三数取中法
int GetMinIndex(int arr[], int left, int right)
{
	int mid = left + (right - left) / 2;
	if (arr[left] < arr[mid])
	{
		if (arr[left] < arr[mid])
		{
			return mid;
		}
		else if (arr[left]<arr[right])//left<mid&&right<mid
		{
			return right;
		}
		else
		{
			return left;
		}
	}
	else //mid<left
	{
		if (arr[left] < arr[right])
		{
			return left;
		}
		else if (arr[mid] < arr[right])//mid<left&&right<left
		{
			return right;
		}
		else
		{
			return mid;
		}
	}
}


int PartSort3_MIN(int arr[], int left, int right)
{
	int midIndex = GetMinIndex(arr, left, right);
	if (midIndex != right)
	{
		std::swap(arr[midIndex], arr[right]);//<span style="color:#ff0000;">快速排序一定要选取左右作key</span>
	}
	int key = arr[right];
	int prev = left - 1;//指向大的数据前一个位置
	int cur = left;
	while (cur < right)
	{
		if (arr[cur] < key&&++prev != cur)
		{
			std::swap(arr[prev], arr[cur]);
		}
		++cur;
	}
	std::swap(arr[++prev], arr[right]);
	return prev;
}

2、减少递归次数,

当要排序的区间[left,right]比较小的时候,这个时候意味这递归的深度也比较深,这个时候就有可能造成栈溢出,所以这个时候用插入排序代替快速排序进行优化

void InsertSort(int arr[],int left, int right)//[left,right]
{
	if (arr == NULL || right-left+1<= 0)
		return;

	for (int i = left; i < right; i++)
	{
		int end = i;//一个有序序列的结尾
		int tmp = arr[i + 1];
		while (end >= 0 && arr[end]>tmp)
		{
			arr[end + 1] = arr[end];
			end--;
		}
		arr[end + 1] = tmp;
	}
}

void QuickSort_Insett(int arr[], int left, int right)
{
	assert(arr);
	if (left >= right)
		return;

	if (right - left < 13)
	{
		InsertSort(arr, left,right);
	}

	int div = PartSort3_MIN(arr, left, right);
	QuickSort(arr, left, div - 1);
	QuickSort(arr, div + 1, right);
}



三、非递归实现

用栈进行处理,每次都压两个边界

//非递归实现
void QuickSort_NonR(int arr[], int left, int right)
{
	assert(arr);
	stack<int> s;

	if (left < right)
	{
		s.push(right);
		s.push(left);
	}
	while (!s.empty())
	{
		left = s.top();
		s.pop();
		right = s.top();
		s.pop();
		if (right - left < 13)//库中给的是13,减少压栈
		{
			InsertSort(arr, left,right);
		}
		else
		{
			int div = PartSort3_MIN(arr, left, right);
			if (left < div - 1)
			{
				s.push(div - 1);
				s.push(left);
			}
			if (div < right)
			{
				s.push(right);
				s.push(div + 1);
			}
		}
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值