捐助郴维网
感谢您对郴维网的支持,你的支持将是郴维网持续发展的动力!
二维码
×
当前位置:郴维网 >查找和排序 > 正文
17 2024.01

排序算法8 - 快速排序

点击次数:8 更新时间:2024/1/17 21:00:19  【打印此页

快速排序(QuickSort)是对冒泡排序算法的一种改进。

快速排序采用的是分治思想,即在一个无序的序列中选取一个任意的基准元素pivot,利用pivot将待排序的序列分成两部分,

前面部分元素均小于或等于基准元素,后面部分均大于或等于基准元素,然后采用递归的方法分别对前后两部分重复上述操作,

直到将无序序列排列成有序序列。

 

排序流程

  快速排序算法通过多次比较和交换来实现排序,其排序流程如下:

    1、首先设定一个分界值,通过该分界值将数组分成左右两部分。

    2、将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于分界值,而右边部分中各元素都大于或等于分界值。(注:等于分界值的数据靠左靠右以实际编写的代码而异)

    3、然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。

    4、重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

 

  图解:

  

 

 

代码实现

#include <stdio.h>
#include <stdlib.h>

void swap(int arr[], int low, int high)
{
	int temp;

	temp = arr[low];
	arr[low] = arr[high];
	arr[high] = temp;
}

int Partition(int arr[], int low, int high)
{
	int point;

	point = arr[low];
	while(low<high)
	{
		while(low<high && arr[high]>=point)
		{
			high--;
		}
		swap(arr, low, high);
		
		while(low<high && arr[low]<=point)
		{
			low++;
		}
		swap(arr, low, high);
	}

	return low;
}

void QSort(int arr[], int low, int high)
{
	int point;

	if(low<high)
	{
		point = Partition(arr, low, high);
		QSort(arr, low, point-1);
		QSort(arr, point+1, high);
	}
}

void QuickSort(int arr[], int len)
{
	QSort(arr, 0, len-1);
}

int main(void)
{

	int i;
	int a[10] = {5, 2, 6, 0, 3, 9, 1, 7, 4, 8};

	QuickSort(a, 10);

	printf("排序后的结果是:");
	for(i=0; i<10; i++)
	{
		printf("%d", a[i]);
		if(i!=9)
		{
			printf(",");
		}
	}
	printf("\n");
	return 0;
}

 

快速排序的优化

  - 优化选取基准点(三数取中法)

  - 优化不必要的交换

  - 优化小数组时的排序方案

  - 优化递归操作

 

 

  优化选取基准点

    选取合适的基准点可以减少递归深度提高排序性能。

    如本例第一轮选取的是元素5,5刚好是序列的中间值,画递归树时,第一层有左右子树。

    假如选取的元素是9,那么可想而知,递归一轮后所有数据将会靠9的左边,右边则没有数据,这样并不能把序列分成左右两组数据,

    从而导致递归深度增加,排序性能下降。如下图所示:

    

    三数取中法

    从数组中找取3个元素,然后取这3个元素的中间值,如:[8, 2, 6] 那么处于中间的元素就是6。

    代码:    

#include <stdio.h>
#include <stdlib.h>

void swap(int arr[], int low, int high)
{
	int temp;

	temp = arr[low];
	arr[low] = arr[high];
	arr[high] = temp;
}

int Partition(int arr[], int low, int high)
{
	int point;

	// 优化1. 选取基准点(三数取中法)
	int mid = low + (high - low) / 2;
	if(arr[low] > arr[high])
	{
		swap(arr, low, high);
	}
	if(arr[mid] > arr[high])
	{
		swap(arr, mid, high);
	}
	if(arr[mid] > arr[low])
	{
		swap(arr, low, low);
	}
	
	point = arr[low];

	while(low<high)
	{
		while(low<high && arr[high]>=point)
		{
			high--;
		}
		swap(arr, low, high);
		
		while(low<high && arr[low]<=point)
		{
			low++;
		}
		swap(arr, low, high);
	}

	return low;
}

void QSort(int arr[], int low, int high)
{
	int point;

	if(low<high)
	{
		point = Partition(arr, low, high);
		QSort(arr, low, point-1);
		QSort(arr, point+1, high);
	}
}

void QuickSort(int arr[], int len)
{
	QSort(arr, 0, len-1);
}

int main(void)
{

	int i;
	int a[10] = {5, 2, 6, 0, 3, 9, 1, 7, 4, 8};

	QuickSort(a, 10);

	printf("排序后的结果是:");
	for(i=0; i<10; i++)
	{
		printf("%d", a[i]);
		if(i!=9)
		{
			printf(",");
		}
	}
	printf("\n");
	return 0;
}

  优化不必要的交换

    从前面的图例可以看出,选取的基准元素5在不停的和其它元素进行位置交换,优化方案是把位置交换改为直接赋值。

    代码:

#include <stdio.h>
#include <stdlib.h>

void swap(int arr[], int low, int high)
{
	int temp;

	temp = arr[low];
	arr[low] = arr[high];
	arr[high] = temp;
}

int Partition(int arr[], int low, int high)
{
	int point;

	// 优化1. 选取基准点(三数取中法)
	int mid = low + (high - low) / 2;
	if(arr[low] > arr[high])
	{
		swap(arr, low, high);
	}
	if(arr[mid] > arr[high])
	{
		swap(arr, mid, high);
	}
	if(arr[mid] > arr[low])
	{
		swap(arr, low, low);
	}
	
	point = arr[low];

	while(low<high)
	{
		while(low<high && arr[high]>=point)
		{
			high--;
		}
		arr[low] = arr[high]; // 优化2. 改为直接赋值
		
		while(low<high && arr[low]<=point)
		{
			low++;
		}
		arr[high] = arr[low]; // 优化2. 改为直接赋值
	}

	arr[low] = point; // 优化2. 改为直接赋值

	return low;
}

void QSort(int arr[], int low, int high)
{
	int point;

	if(low<high)
	{
		point = Partition(arr, low, high);
		QSort(arr, low, point-1);
		QSort(arr, point+1, high);
	}
}

void QuickSort(int arr[], int len)
{
	QSort(arr, 0, len-1);
}

int main(void)
{

	int i;
	int a[10] = {5, 2, 6, 0, 3, 9, 1, 7, 4, 8};

	QuickSort(a, 10);

	printf("排序后的结果是:");
	for(i=0; i<10; i++)
	{
		printf("%d", a[i]);
		if(i!=9)
		{
			printf(",");
		}
	}
	printf("\n");
	return 0;
}

  优化小数组时的排序方案

    对于很小的数组(array.length <= 20),快速排序不如插入排序(插入排序在排序算法中处理小数组时性能最好),因为快排是递归分解的,总会遇到小数组情况。

    因此,当是小数组时,我们采用插入排序而不是快排。

    上面说小于20的数组的小数组,其实5到20之内都可以,据说7为最佳

    代码:

#include <stdio.h>
#include <stdlib.h>

#define MAX_LENGTH_INSERT_SORT 7 // 优化3. 小数组时的排序方案


// 优化3. 小数组时的排序方案
void ISort(int arr[], int n)
{
	int i, j, temp;

	for( i=1; i<n; i++ )
	{
		if( arr[i-1]>arr[i] )
		{
			temp = arr[i];
			for( j=i-1; arr[j]>temp; j--)
			{
				arr[j+1] = arr[j];
			}

			arr[j+1] = temp;
		}
	}
}

void InsertSort(int arr[], int low, int high)
{
	ISort(arr+low, high-low+1);
}


void swap(int arr[], int low, int high)
{
	int temp;

	temp = arr[low];
	arr[low] = arr[high];
	arr[high] = temp;
}

int Partition(int arr[], int low, int high)
{
	int point;

	// 优化1. 选取基准点(三数取中法)
	int mid = low + (high - low) / 2;
	if(arr[low] > arr[high])
	{
		swap(arr, low, high);
	}
	if(arr[mid] > arr[high])
	{
		swap(arr, mid, high);
	}
	if(arr[mid] > arr[low])
	{
		swap(arr, low, low);
	}
	
	point = arr[low];

	while(low<high)
	{
		while(low<high && arr[high]>=point)
		{
			high--;
		}
		arr[low] = arr[high]; // 优化2. 改为直接赋值
		
		while(low<high && arr[low]<=point)
		{
			low++;
		}
		arr[high] = arr[low]; // 优化2. 改为直接赋值
	}

	arr[low] = point; // 优化2. 改为直接赋值

	return low;
}

void QSort(int arr[], int low, int high)
{
	int point;

	if(high-low > MAX_LENGTH_INSERT_SORT)
	{
		point = Partition(arr, low, high);
		QSort(arr, low, point-1);
		QSort(arr, point+1, high);
	}
	else
	{ // 优化3. 小数组时的排序方案
		InsertSort(arr, low, high);
	}
}

void QuickSort(int arr[], int len)
{
	QSort(arr, 0, len-1);
}

int main(void)
{

	int i;
	int a[10] = {5, 2, 6, 0, 3, 9, 1, 7, 4, 8};

	QuickSort(a, 10);

	printf("排序后的结果是:");
	for(i=0; i<10; i++)
	{
		printf("%d", a[i]);
		if(i!=9)
		{
			printf(",");
		}
	}
	printf("\n");
	return 0;
}

  优化递归操作

    代码中的快速排序有2次递归函数操作,减少递归函数的调用就可以最大程度的提高其性能,这时可以使用尾递归。

    什么是尾递归呢?如果一个函数中递归形式的调用出现在函数的末尾,我们称这个递归函数是尾递归的。

    当编译器检测到一个函数调用是尾递归的时候,它就覆盖当前的活跃记录而不是在栈中去创建—个新的。

    编译器可以做到这点,因为递归调用是当前活跃期内最后一条待执行的语句,于是当这个调用返回时栈帧中并没有其他事情可做,因此也就没有保存栈帧的必要了。

    通过覆盖当前的栈帧而不是在其之上重新添加一个,这样所使用的栈空间就大大缩减了,这使得实际的运行效率会变得更高。

    因此,只要有可能我们就需要将递归函数写成尾递归的形式。

    最终代码:

#include <stdio.h>
#include <stdlib.h>

#define MAX_LENGTH_INSERT_SORT 7 // 优化3. 小数组时的排序方案


// 优化3. 小数组时的排序方案
void ISort(int arr[], int n)
{
	int i, j, temp;

	for( i=1; i<n; i++ )
	{
		if( arr[i-1]>arr[i] )
		{
			temp = arr[i];
			for( j=i-1; arr[j]>temp; j--)
			{
				arr[j+1] = arr[j];
			}

			arr[j+1] = temp;
		}
	}
}

void InsertSort(int arr[], int low, int high)
{
	ISort(arr+low, high-low+1);
}


void swap(int arr[], int low, int high)
{
	int temp;

	temp = arr[low];
	arr[low] = arr[high];
	arr[high] = temp;
}

int Partition(int arr[], int low, int high)
{
	int point;

	// 优化1. 选取基准点(三数取中法)
	int mid = low + (high - low) / 2;
	if(arr[low] > arr[high])
	{
		swap(arr, low, high);
	}
	if(arr[mid] > arr[high])
	{
		swap(arr, mid, high);
	}
	if(arr[mid] > arr[low])
	{
		swap(arr, low, low);
	}
	
	point = arr[low];

	while(low<high)
	{
		while(low<high && arr[high]>=point)
		{
			high--;
		}
		arr[low] = arr[high]; // 优化2. 改为直接赋值
		
		while(low<high && arr[low]<=point)
		{
			low++;
		}
		arr[high] = arr[low]; // 优化2. 改为直接赋值
	}

	arr[low] = point; // 优化2. 改为直接赋值

	return low;
}

void QSort(int arr[], int low, int high)
{
	int point;

	if(high-low > MAX_LENGTH_INSERT_SORT)
	{
		// 优化4. 优化递归操作
		while(low<high)
		{			
			point = Partition(arr, low, high);
			if(point-low < high-point)
			{
				QSort(arr, low, point-1);
				low = point + 1;
			}
			else
			{
				QSort(arr, point+1, high);
				high = point -1;
			}
		}
	}
	else
	{ // 优化3. 小数组时的排序方案
		InsertSort(arr, low, high);
	}
}

void QuickSort(int arr[], int len)
{
	QSort(arr, 0, len-1);
}

int main(void)
{

	int i;
	int a[10] = {5, 2, 6, 0, 3, 9, 1, 7, 4, 8};

	QuickSort(a, 10);

	printf("排序后的结果是:");
	for(i=0; i<10; i++)
	{
		printf("%d", a[i]);
		if(i!=9)
		{
			printf(",");
		}
	}
	printf("\n");
	return 0;
}
提示
郴维网为您提供各类专业服务:
软件开发,电脑配件销售,WIFI路由器销售,上门电脑维修,上门安装系统,系统安装,软、硬件安装,电脑除尘清灰,显示器维修,WIFI安装调试,服务器维护,数据恢复,密码破解,网络布线,网络检修,打印机维修,打印机加碳粉,苹果电脑安装系统,苹果电脑安装双系统,监控安装维护,电脑外包,笔记本电脑维修,餐饮、美容行业软件安装 等。。。。。。
点击次数:8 更新时间:2024/1/17 21:00:19  【打印此页
关键词推荐:郴州电脑城 郴州电脑维修公司 维修电脑公司 郴州软件开发 上门电脑维修 上门安装系统 笔记本电脑维修 郴州打印机维修 打印机加碳粉 电脑安装双系统 苹果电脑双系统 液晶显示器维修 联想笔记本维修 联想笔记本维修电话 戴尔笔记本维修电话 郴州戴尔笔记本维修 戴尔笔记本郴州维修点 华硕笔记本维修点 郴州华硕笔记本维修 郴州笔记本上网维修