初夏小谈:排序算法---快速排序(超详解)(三种方式实现及优化版本)

       快速排序正如它的名字一样牛逼,它是实践中最快的已知排序算法。那么快速排序是怎么一种排法呢?往下看 

       快速排序是利用了分治的思想,分而治之。将一个大的问题拆分成几个子问题,那么解决掉这些子问题,用它们的解将得到原问题的解。

       它的算法思想是:三步骤

       ①:首先选择一个基准值(这个基准值可以是待排数列中的任意一个)

       ②:把小于基准值的所有元素全部移到基准值的左边,把所有大于基准值的元素全部移到基准值的右边(切块操作,此时基准值的位置便是最终位置)

       ③:数列被切分为两部分,就继续对这两部分继续进行上述两部操作,直到所有子集合不在满足切分条件

可以简称:选基准,分两边,重复做

        举个例子:5,6,3,8,9,7(去掉3是因为如果基准值是3则第一趟无法看到i与j的变化过程,因为3是最小的)

                           初夏小谈:排序算法---快速排序(超详解)(三种方式实现及优化版本)

        开始取到最后一个元素为基准值。(指向数列第一个元素,j指向最后一个)(i先走)

                     初夏小谈:排序算法---快速排序(超详解)(三种方式实现及优化版本)

         第一次i所在下标的值和基准值相比较,发现小于7所以i继续向后走一步,j不走

                       初夏小谈:排序算法---快速排序(超详解)(三种方式实现及优化版本)

         第二次i所在下标的值和基准值相比较,发现还是小于7所以i继续向后走一步,j不走

                       初夏小谈:排序算法---快速排序(超详解)(三种方式实现及优化版本)

        第三次3依旧比7(基准值)小,i继续向后走,j不动

                      初夏小谈:排序算法---快速排序(超详解)(三种方式实现及优化版本)

      第四次发现i下标所在的值8比7(基准值)大,则i不在往前走

                     初夏小谈:排序算法---快速排序(超详解)(三种方式实现及优化版本)

       第一次j和基准值一样所以j继续往前走

                      初夏小谈:排序算法---快速排序(超详解)(三种方式实现及优化版本)

       第二次j下标所在值还是比7大,继续往前走

                    初夏小谈:排序算法---快速排序(超详解)(三种方式实现及优化版本)

       第三次j和i相遇终止循环。

                   初夏小谈:排序算法---快速排序(超详解)(三种方式实现及优化版本)  

     这次交换在这里看着没有意义,其实它是在交换最后一个最小的值和第一个最大的值。

                      初夏小谈:排序算法---快速排序(超详解)(三种方式实现及优化版本)

    此时已经将数组分为两部分,即i下标以前都比基准值小,j下标以后都比基准值大(不包含基准值位置),此时将基准值和第一个比基准值大的元素交换。并且记录此时基准值的下标。

  一趟快排就完成了,后面就是一次递归。

代码如下:

头文件:

#include<stdio.h>

//交换两个数
void SwapNum(int* num1, int* num2);

//打印数组
void PrintArray(int array[], int size);

//快速排序
//快速排序1基准值在中间
//left 数组第一个元素下标,right数组最后一个元素下标
void QuickSort(int array[], int left, int right);

函数体:

#include"QuickSort.h"

//交换两个数
void SwapNum(int* num1, int* num2)
{
	int temp = *num1;
	*num1 = *num2;
	*num2 = temp;
}

//打印数组
void PrintArray(int array[], int size)
{
	int i = 0;
	while (i < size)
	{
		printf("%d ", array[i]);
		i++;
	}
	printf("\n");
}

//快速排序1基准值在中间
//left 数组第一个元素下标,right数组最后一个元素下标
void QuickSort(int array[], int left, int right)
{
	if (left >= right)
	{
		return;
	}
	///进行一趟排序
	int mid = right;
	int i = left, j = right;
	while (i < j)
	{
		while (i < j&&array[i] <= array[mid])
		{
			i++;
		}
		while (i < j&&array[j] >= array[mid])
		{
			j--;
		}
		SwapNum(&array[i], &array[j]);
	}
	SwapNum(&array[i], &array[mid]);
	mid = i;
	//尾递归实现
	QuickSort(array, left, mid - 1);
	QuickSort(array, mid + 1, right);
}

main函数:

#include"QuickSort.h"
#include<stdlib.h>

int main()
{
	int array[] = { 5,6,3,8,9,7};
	int size = sizeof(array) / sizeof(array[0]);
	int left = 0;
	int right = size - 1;
	PrintArray(array, size);
	//进行快速排序
	QuickSort(array, left , right);
	printf("快速排序:");
	PrintArray(array, size);
	system("pause");
	return 0;
}

结果显示:

初夏小谈:排序算法---快速排序(超详解)(三种方式实现及优化版本)

 

快速排序的其它两种不同版本:

一、挖坑法:就是把基准值先保存起来,然后先去从左到右找比基准值大的数填进去,这时左边就有一个坑,接着从右往左找到比基准值小的填进去。以此类推。最后再把基准值填到最终的坑上。

代码如下:

//快速排序2挖坑法基准值在中间
//left 数组第一个元素下标,right数组最后一个元素下标
void QuickSort2(int array[], int left, int right)
{
	if (left >= right)
	{
		return;
	}
	///进行一趟排序
	int mid = array[right];
	int i = left, j = right;
	while (i < j)
	{
		while (i < j&&array[i] <= mid)
		{
			i++;
		}
		array[j] = array[i];
		while (i < j&&array[j] >= mid)
		{
			j--;
		}
		array[i] = array[j];
	}
	//将基准值放到最后的坑中
	array[i] = mid;
	//尾递归实现
	QuickSort(array, left, i - 1);
	QuickSort(array, i + 1, right);
}

第二种:拉帘法:

此时i,j都指向第一个元素,i往后走遇见比基准值小的,就和j所在下标的值进行交换j往前走一步。直到i走到数组尽头。

函数实现:

//快速排序3基准值在后边i,j都从头开始
//left 数组第一个元素下标,right数组最后一个元素下标
void QuickSort3(int array[], int left, int right)
{
	if (left >= right)
	{
		return;
	}
	///进行一趟排序
	int mid = right;
	int i = left, j = left;
	for (i = left; i <= right; i++)
	{
		if (array[i] < array[mid])
		{
			SwapNum(&array[i], &array[j]);
			j++;
		}
	}
	SwapNum(&array[j], &array[mid]);
	mid = j;
	//尾递归实现
	QuickSort(array, left, mid - 1);
	QuickSort(array, mid + 1, right);
}

快速排序的优化:

              存在问题:在以上排序中都有许多重复的比较,以及基准值的取值可能取到最值的情况。

             解决办法:①:针对基准值:可以采取三数取中值法,就是一次取到数列第一个,中间一个,和最后一个。进行排序,取到中值,确保不会取到最值。

                               ②:针对重复的问题可以中值和数组的倒数第二个交换,j取倒数第三个,因为最后一个一定比中值大,没有必要再去比较。

快速排序优化版本:

头文件:


//快速排序
void QuickSort(int array[], int left, int right);

函数体:

#include"QuickSort.h"


void Swap(int* num1, int* num2)
{
	int temp = *num1;
	*num1 = *num2;
	*num2 = temp;
}
//快速排序
//left左下标 right右下标
void QuickSort(int array[], int left, int right)
{
	//有一个数或者没有
	if (right - left <= 0)
	{
		return;
	}

	int i = left;
	int j = right;
	int mid = (j + i) / 2;

	//有两个数
	if (right - left == 1)
	{
		if (array[mid] > array[mid + 1])
		{
			Swap(&array[mid + 1], &array[mid]);
			mid = mid + 1;
			return;
		}
		return;
	}

	//取三数中值为基准
	//至少有三个数
	//3 6 2
	if (right - left > 1)
	{
		if (array[mid] > array[j])
		{
			Swap(&array[mid], &array[j]);
		}
		if (array[i] > array[j])
		{
			Swap(&array[i], &array[j]);
		}
		if (array[i] > array[mid])
		{
			Swap(&array[i], &array[mid]);
		}
	}

	//至少有四个数
	if (right - left > 2)
	{
		//将中值和倒数第二个值进行交换并且记录中值所在下标
		Swap(&array[mid], &array[right - 1]);
		mid = right - 1;

		//最右边j开始的下标
		j = right - 2;
		i = left + 1;
		while (i < j)
		{
			if (array[i] > array[mid])
			{
				while (i < j)
				{
					if (array[j] < array[mid])
					{
						Swap(&array[i], &array[j]);
						break;
					}
					j--;
				}
			}
			if (i < j)
			{
				i++;
			}
		}
		//将一趟交换后第一个大于中值的元素进行交换并且记录中值下标
		if (array[i] > array[mid])
		{
			Swap(&array[i], &array[mid]);
			mid = i;
		}
		QuickSort(array, left, mid - 1);
		QuickSort(array, mid + 1, right);
	}
}

main函数:

#include"QuickSort.h"
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define SIZE 100

int main()
{
	srand((unsigned int)time(NULL));
	int array[SIZE];
	for (int i = 0; i < SIZE; i++)
	{
		array[i] = rand() % 100;
	}
	/*int array[] = { 62,62,2,79,0,15};*/
	int Left = 0;
	int Right = sizeof(array) / sizeof(array[0]) - 1;
	printf("未 排 序:");
	for (int i = 0; i <= Right; i++)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
	QuickSort(array, Left, Right);
	printf("快速排序:");
	for (int i = 0; i <= Right; i++)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
	system("pause");
	return 0;
}

结果显示:

初夏小谈:排序算法---快速排序(超详解)(三种方式实现及优化版本)

如有疑问欢迎留言。

                                                                                                                                                                                      珍&源码