数据结构------排序3(快速排序)

发布于:2022-08-08 ⋅ 阅读:(250) ⋅ 点赞:(0)

引言:

 

上面两篇文章讲了几种排序方法,它们的一些性能和时间复杂度我进行了总结,如果一些代码不懂可以参考《排序1》,《排序2》

1.什么是快速排序?      

1.1快拍定义及图解

1.首先设定一个分界值,通过该分界值将数组分成左右两部分。  2.将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于分界值,而右边部分中各元素都大于或等于分界值。   3.然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。  4.重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

 2.hoare法实现

hoare法是快拍中比较常用的一种方法。

2.1hoare单趟实现

//快速排序单趟实现
int PartSort1(int* a, int begin, int end)
{
	int left = begin;
	int right = end;
	int key = begin;
	int keyi = begin;
	if (begin >= end)//如果最多有一个就不用递归了
	{
		return;
	}
	while (left < right)
	{
		while (left < right && a[right] >= a[key])
		{
			right--;
		}

		while (left < right && a[left] <= a[key])
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[left], &a[keyi]);
	return left;

 2.2多趟实现

2.21大致思想:

前面我们以基准值为参考进行了简单排序,现在比基准值小的在其左边,大的在其右边。

那我们就可以分两组实现左边一组,右边一组就行了,在这里我们用递归来实现。

 2.22第一种方法实现

//hoare法整体实现
void PartSort1D(int* a, int begin, int end)
{
	int left = begin;
	int right = end;
	int key = begin;
	int keyi = begin;
	if (begin >= end)//如果最多有一个就不用递归了
	{
		return;
	}
	while (left < right)
	{
		while (left < right && a[right] >= a[key])
		{
			right--;
		}

		while (left < right && a[left] <= a[key])
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[left], &a[keyi]);

	keyi = left;  //把keyi交换到中间
	PartSort1D(a, begin, keyi - 1);
	PartSort1D(a, keyi + 1, end);
}

2.23第二种方法实现

这种只不过是单趟排序再加一个主函数,但是更便捷了。

int PartSort1(int* a, int begin, int end)
{
	int left = begin;
	int right = end;
	int key = begin;
	int keyi = begin;
	if (begin >= end)//如果最多有一个就不用递归了
	{
		return;
	}
	while (left < right)
	{
		while (left < right && a[right] >= a[key])
		{
			right--;
		}
		while (left < right && a[left] <= a[key])
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[left], &a[keyi]);
     return left;
}
//整体法
void QuickSort(int* pa, int begin, int end)
{
	assert(pa);
	if (begin >= end)
	{
		return;
	}
	//这里为单趟实现的方式
	int keyi = PartSort1(pa, begin, end);

	QuickSort(pa, begin, keyi - 1);
	QuickSort(pa, keyi + 1, end);
}

3.挖坑法实现

3.1大致思路:

以最左侧的数为基准数key,此时将key的值保存并将这个位置变成一个坑, 让right左移找小于key的数,找到后将这个数要到刚才的这个坑中,right1位置又形成一个坑,再将left的值移到这个坑中,最后将key移到left,right重叠位置,重复多次。

 3.2挖坑法单趟实现

//挖坑法
int PartSort2(int* a, int begin, int end)
{
	int key = a[begin];
	int tmp = begin;//坑位
	int right = end;
	int left = begin;
	while (left < right)
	{
		//当左边小于右边时循环结束
		while (left < right && a[right] >= key)
		{
			right--;
		}
		Swap(&a[tmp], &a[right]);
		tmp = right;  //把right位置变成坑位
		
		while (left < right && a[left] <= key)
		{
			left++;
		}
		Swap(&a[tmp], &a[left]);
		tmp = left;
	}
	Swap(&key, &a[tmp]);
	return  tmp;
	
}

大致思想和上面的基本一致。

3.3多趟实现第一种

思路和hoare法一模一样,以坑位tmp为界限,左边调用一次递归,右边调用一次递归就行。

//挖坑法
void PartSort2D(int* a, int begin, int end)
{
	int key = a[begin];
	int tmp = begin;//坑位
	int right = end;
	int left = begin;
	while (left < right)
	{
		//右边找小,填左坑
		while (left < right && a[right] >= key)
		{
			right--;
		}
		Swap(&a[tmp], &a[right]);
		tmp = right;
		//左边找大,填右坑
		while (left < right && a[left] <= key)
		{
			left++;
		}
		Swap(&a[tmp], &a[left]);
		tmp = left;
	}
	Swap(&key, &a[tmp]);
	PartSort2D(a, begin,tmp - 1);
	PartSort2D(a, tmp + 1, end);
}

3.4第二种方法实现

//挖坑法
int PartSort2(int* a, int begin, int end)
{
	int key = a[begin];
	int tmp = begin;//坑位
	int right = end;
	int left = begin;
	while (left < right)
	{
		//当左边小于右边时循环结束
		while (left < right && a[right] >= key)
		{
			right--;
		}
		Swap(&a[tmp], &a[right]);
		tmp = right;  //把right位置变成坑位
		
		while (left < right && a[left] <= key)
		{
			left++;
		}
		Swap(&a[tmp], &a[left]);
		tmp = left;
	}
	Swap(&key, &a[tmp]);
	return  tmp;
	
}
//整体法
void QuickSort(int* a, int begin, int end)
{
	assert(a);
	if (begin >= end)
	{
		return;
	}
	//这里为单趟实现的方式
	int keyi = PartSort2(a, begin, end);

	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

这里就不过多解释了。

4.前后指针法

4.1前后指针思路分析

一个先行指针cur,一个后行prev,cur先走,当遇见小于key的值时,两者同步,当遇见大于key的值时,cur继续往前走,prev不动,所以prev前面一定是大于key的数,直到cur找到小于key的值时停下,然后交换cur和prev前面那个数.

4.11那能不能进行单趟实现呢?

  可以,当cur遇见的都是小于key的值时,prev,cur同步,当cur出界限时,prev和key完成交换,单趟结束。

4.2源码单趟实现

//前后指针法
int PartSort3(int* a, int begin, int end)
{
	int key = begin;
	int cur = begin + 1;
	int prev = begin;
	while (cur <= end)
	{
		if ((a[cur] < a[key]) && (++prev != cur))
		{
			Swap(&a[cur], &a[prev]);
		}
		cur++;
	}
	Swap(&a[prev], &a[key]);
	return prev;
	
}

4.3多趟实现

//前后指针法
void PartSort3D(int* a, int begin, int end)
{
	int key = begin;
	int cur = begin + 1;
	int prev = begin;
	while (cur <= end)
	{
		if ((a[cur] < a[key]) && (++prev != cur))
           //交换prev的前一个值
		{
			Swap(&a[cur], &a[prev]);
		}
		cur++;
	}
	Swap(&a[prev], &a[key]);
	
	QuickSort(a, begin, prev - 1);
	QuickSort(a, key + 1, end);
}



非递归实现下次在更新吧。