快速排序(左右指针法)

快速排序

快速排序的原理

快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。

快速排序的名字起的是简单粗暴,因为一听到这个名字你就知道它存在的意义,就是快,而且效率高!它是处理大数据最快的排序算法之一了。

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。
请添加图片描述

假如我们需要排序一串数组:
在这里插入图片描述

左右指针法:

第一步:
首先先在数组中找到一位基准数也可以称做“key”,通常是数组的(第一位)或(最后一位);
我们先以第一位设为基准数(key);

在这里插入图片描述
第二步:
我们需要把选择的基准数移至数组的某个正确的位置:
比如说基准数是3 移动到一个位置,该位置左边都是<=3的数,而该位置右边都是>=3的数;
因为是左右指针法,是从数组两端分别开始寻找,先从右往左找一个小于3的数,再从左往右找一个大于3的数,然后交换他们。在这里可以用两个变量,变量的名称可以为“left”和“right”。在刚开始可以用“left”指向最左边,“right”指向最右边。
在这里插入图片描述
在这里需要注意:若选择最左边的数据作为key,则需要 “right” 先走;若选择最右边的数据作为key,则需要 “left” 先走。
所以是 “right”先出发,一步一步向左走(right=right -1),直到遇到一个小于 基准数3的数停下来指向那个数,然后“left”再出发,一步一步向右走(left=left +1),直到遇到一个大于基准数3的数停下来。
在这里插入图片描述
然后让“left”所指的数与“right”所指的数进行交换。

在这里插入图片描述
到此为止,才是第一次交换的结束。接下来进行第二次交换,以上述过程一样,让“right”先进行向左移动(right=right - 1),找到一个小于基准数3的数,直到找到1,这个时候“left”和“right”所指向的数相同。
在这里插入图片描述
每当“left”和“right”相遇时,将所指向的 1 与基准数 3进行交换。
在这里插入图片描述
此时将基准数放回原位置,这时“key”的左边都是小于3的数,而“key”的右边的都是大于3的数。这个是后就可以把“key”看作是分界线将这串数组看出两组数组,“key”左边一组,“key”右边为一组。

我们先来处理“key”左边的这组数组吧!
在这里插入图片描述

我们可以看到,好巧不巧,我随便输入的一组数组,此时根本不需要处理。
但是如何判断他不需要再进行处理了呢?
——————
当重新处理数组时,也是一样的重新设置好基准数 1 ,“left”指向最左边 1,“right”指向原来“key”的左边一位也就是 2,将key的左序列和右序列再次进行这种单趟排序,如此反复操作下去,直到左右序列只有一个数据,或是左右序列不存在时,便停止操作。

接下来处理原“key”的右边数组。

在这里插入图片描述
此时 以上面相同的方法进行处理,先是“right”向左找到小于基准数 6,在“left”向右找到大于基准数 6的数,停下。
在这里插入图片描述
找到进行交换。
在这里插入图片描述
然后“right”再次寻找,指向4停止,此时“left”和“right”指向同一个数,将其与“key”进行交换。

在这里插入图片描述

然后再将“key”当做分界线,将这组数组在进行分别的处理。
按照与上面一样的方法进行操作,最终会将数组成功排序成功:
在这里插入图片描述
细心的同学可能已经发现,快速排序的每一轮处理其实就是将这一轮的基准数归位,直到所有的数都归位为止,排序就结束了。接下来用图示的方法来展示完整的过程:
在这里插入图片描述
代码实现:

void quick_sort(int *arr, int begin,int end)
{
    
    
	if (end < begin )
		return;
	//left 指向最左边
	int left = begin ;
	//right  指向最右边
	int right = end ;
	int* key = &arr[begin];

	while (left != right)
	{
    
    
		while (arr[right] >= *key && right > left) 
		{
    
    
			right--;
		}
		while (arr[left] <= *key && left < right)
		{
    
    
			left++;
		}
		if (left < right)
		{
    
    
			swap(&arr[left],&arr[right]);
			
		}
		
	}
	swap(&arr[left],key);
	quick_sort(arr, begin, left-1);
	quick_sort(arr, left+1, end );
}

整体代码:

void swap(int*left,int *right)
{
    
    
	int tem = *left;
	*left = *right;
	*right = tem;
}

void quick_sort(int *arr, int begin,int end)
{
    
    
	if (end < begin )
		return;
	//left 指向最左边
	int left = begin ;
	//right  指向最右边
	int right = end ;
	int* key = &arr[begin];

	while (left != right)
	{
    
    
		while (arr[right] >= *key && right > left) 
		{
    
    
			right--;
		}
		while (arr[left] <= *key && left < right)
		{
    
    
			left++;
		}
		if (left < right)
		{
    
    
			swap(&arr[left],&arr[right]);
			
		}
		
	}
	swap(&arr[left],key);
	
	

	quick_sort(arr, begin, left-1);
	quick_sort(arr, left+1, end );
}

int main()
{
    
    
	int arr[] = {
    
     6,1,2,7,9 ,10,3,4,5,0,8 };
	int sz = sizeof arr / sizeof arr[1];

	int begin = 0;
	int end = sz-1;
	quick_sort(arr, begin,end);

	int i = 0;
	for (i = 0; i < sz; i++)
	{
    
    
		printf("%d ", arr[i]);
	}

	return 0;
}

快速排序使用分治法策略来把一个串行分为两个子串行。

快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。

猜你喜欢

转载自blog.csdn.net/m0_66780695/article/details/131116712