排序算法--快速排序

快速排序之所以特别快,主要是由于非常精炼和高度优化的内部循环。

像归并排序一样,快速排序也是一种分治的递归算法。数组S排序的基本算法由下列简单的四部组成:

1.如果S中元素个数是0或1,则返回

2.取S中任一元素v,称之为pivot(枢纽元,主元,基准)

3.将S-{v}分成两个不想交的集合:S1={x∈S-{v} | x <= v} 和 S2={x∈S-{v} | x > v}

4.返回{QuickSort(S1)后,继随v,继而QuickSort(S2)

为了完成上述的步骤3,有两种常用方法,可以分别简称为:一前一后两个指针,一左一右两个指针。这两种方法只是实现上的差异,效率是一样的。

其实主要影响快速排序性能的是pivot的选取。

  1. 我们常用的一种选取方式(选取第一个或最后一个为pivot)是错误的,因为它有很大的可能会影响快速排序的性能。
  2. 一种安全的做法是,随机选取pivot。
  3. 最好的一种方法称为,三数中值分割法(Median-of-Three Partitioning)。就是利用数组的中值作为pivot,显然这很难算出,而且明显减慢排序的速度。所以常用的方法是,选取最左端、最右端、中心位置上的三个元素的中值作为pivot。

代码如下:

#include <iostream>
#include <random>
#include <utility>
#include <ctime>
#include <algorithm>
using namespace std;

void QuickSort(int* arr, size_t first, size_t last);
size_t Partition(int* arr, size_t first, size_t last);	        // 一前一后两个指针
size_t Partition_2(int* arr, size_t first, size_t last);	// 一左一右两个指针

int main() {
	const int ArrSize = 10;
	int* arr = new int[ArrSize];
	static default_random_engine e(time(0) % 100);
	static uniform_int_distribution<int> u(0, 100);	// 随机数范围[0, 100]

	for (size_t i = 0; i < ArrSize; ++i) {
		arr[i] = u(e);
	}

	QuickSort(arr, 0, ArrSize - 1);
	cout << boolalpha;
	cout << is_sorted(arr, arr + ArrSize) << endl;

	return 0;
}

void QuickSort(int* arr, size_t first, size_t last) {
	if (first < last) {
		size_t pivotIndex = Partition(arr, first, last);
		QuickSort(arr, first, pivotIndex - 1);
		QuickSort(arr, pivotIndex + 1, last);
	}
}

size_t Partition(int* arr, size_t first, size_t last) {
	int value = arr[first];	// 以第一个元素为主元
	size_t i = first;    // 后指针
	size_t j = first + 1;   // 前指针

	while (j <= last) {
		if (arr[j] <= value) {
			++i;
			swap(arr[i], arr[j]);
		}
		++j;
	}

	swap(arr[first], arr[i]);

	return i;
}

size_t Partition_2(int* arr, size_t first, size_t last) {
	int value = arr[first];
	size_t i = first;	//左指针 // **1
	size_t j = last;    // 右指针

	while (i < j) {
		while (arr[i] <= value) {
			++i;
		}

		while (arr[j] > value) {
			--j;
		}

		if (i < j) {
			swap(arr[i], arr[j]);
		}
	}

	swap(arr[first], arr[j]);  // **2

	return j;
}

这里,为了减少复杂度,我选取第一个元素为pivot。

两种划分方式中,一前一后两个指针方式,较好理解,i为后指针,j为前指针。i所指元素以及之前元素都是<=pivot的,j每次+1,然后判断arr[j]和pivot的关系。

一左一右两个指针方式中,我认为有两处难以理解的地方(都已在代码中标注)。

**1,选取当前范围内第一个元素为pivot,为什么还把i(左指针)从first开始?

answer: 快速排序是以递归方式进行的,递归的终止条件时,!(first < last),所以只有当前范围的元素个数>=2时,排序算法才会一直执行下去。

只有两个元素时,这两个元素之间的大小关系只有三种情况。当第一个元素大于第二个元素时,不妨设第一个元素为10,在数组中索引为0,第二个元素为0,在数组中索引为1。

如果说,左指针为first+1(这里为1),则后面的while循环不会被执行,所以步骤3没有得到满足,排序会失败。

**2,为什么是{ swap(arr[first], arr[j]); return j; },而不是{ swap(arr[first], arr[i]); return i; }

answer: while 循环的结束条件为!(i < j),循环结束后,i在j之后,i所指的元素为,第一个大于pivot的元素,j所指的元素为,最后一个小于等于pivot的元素。所以执行的是,{ swap(arr[first], arr[j]); return j; }。

参考:《数据结构与算法分析--C语言描述》

时间: 2024-11-05 19:21:03

排序算法--快速排序的相关文章

排序算法----快速排序java

快速排序是对冒泡排序的一种改进,平均时间复杂度是O(nlogn) import java.util.Arrays; import java.util.Scanner; public class test02{ public static void main(String[] args) { int n = 1; while (n != 0){ Scanner scanner = new Scanner(System.in); n = scanner.nextInt(); int s[] = ne

经典排序算法 - 快速排序Quick sort

经典排序算法 - 快速排序Quick sort 原理,通过一趟扫描将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列 举个例子 如无序数组[6 2 4 1 5 9] a),先把第一项[6]取出来, 用[6]依次与其余项进行比较, 如果比[6]小就放[6]前边,2 4 1 5都比[6]小,所以全部放到[6]前边 如果比[6]大就放[6]后边,9比[6]大,放到[6

排序算法----快速排序(链表形式)

单链表形式实现排序算法. 这个快速排序主要利用递归调用.包含4个文件,头文件QuickSort.h,fatal.h,库函数QuickSort.c,测试文件TestQuickSort. QuickSort.h 1 typedef long ElementType; 2 #ifndef _List_H//如果没有编译过 3 #include<stdbool.h> 4 struct Node; 5 typedef struct Node *PtrToNode; 6 typedef PtrToNode

java八种排序算法---快速排序

快速排序基本思想:挖坑填数+递归分治 快速排序使用分治法的策略,把一个串行分成2个子串行,快速排序又是一种分而治之的思想在排序算法是上的典型应用,本质上看,快速排序应该算冒泡排序基础上的递归分治法,快速排序名字简单粗暴,顾名思义就是快而且效率高,它是处理大数据最快的算法之一了. 算法描述:1.从数列中任意挑出一个数作为基准(pivot) 2.重新排序,所有比基准大的数放在基准左边,所有比基准大的数放在基准右边,这样排序一遍后该基准就位于数列的中间,这个就被称为分区操作(partition) 3.

白话排序算法--快速排序

前言: 写到快速排序时,之前已经进行了冒泡.选择.插入排序,发现算法问题很多东西都是和实际问题相逆的,实际你可能会由外及里,由上及下,可是算法里面它有时就需要你由里往外去扩散,好比最里面的是小成果,然后一次次往外变化,成果也慢慢变大,最后直至达到最终成果为止.本篇快速排序方法因为调用了递归,你就可以逆着由里及外来思考问题. 写这篇文章光画图就花费了我2小时时间,越抽象就越不好形容和比喻,画的不好,希望各位不要吐槽. 快速排序:先快速排序将队列一分为二,然后对每个队列进行递归调用快速排序方法,直至

排序算法--快速排序算法解析

一.快速排序算法思路 ①.取待排序列表的第一个元素p,通过归位算法,挪移他到列表中正确的位置. ②.列表被元素p分成两部分,左边都比元素p小,右边都比元素p大. ③.通过递归,在两部分,重复1.2步骤,直至列表有序. 归位算法动画演示: 二.快速排序算法代码示例 1 def partition(l,left,right):#归位算法 2 temp = l[left] #保存归位元素 3 while left <right: 4 while l[right]>=temp and left<

经典排序算法--快速排序

一.快速排序的基本思想: 快速排序使用了分治的思想,通过一趟排序将待排序列分割成两部分,其中一部分记录的关键字均比另一部分记录的关键字小.之后分别对这两部分记录继续进行排序,以达到整个序列有序的目的. 二.快速排序的三个步骤 1) 选择基准:在待排序列中,按照某种方式挑出一个元素,作为 "基准"(pivot): 2) 分割操作:以该基准在序列中的实际位置,把序列分成两个子序列.如果为升序,则此时,在基准左边的元素都比该基准小,在基准右边的元素都比基准大:而基准则在排序后正确的位置上.

经典排序算法——快速排序

对于一个int数组,请编写一个快速排序算法,对数组元素排序. 给定一个int数组A及数组的大小n,请返回排序后的数组. 测试样例: [1,2,3,5,2,3],6 [1,2,2,3,3,5] class QuickSort { public: int* quickSort(int* A, int n) { // write code here if(A==NULL || n<2) return A; process(A,0,n-1); return A; } int* process(int*

排序算法 - 快速排序(Quick Sort)

算法思想 快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序.它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod). (1) 分治法的基本思想    分治法的基本思想是:将原问题分解为若干个规模更小但结构与原问题相似的子问题.递归地解这些子问题,然后将这些子问题的解组合为原问题的解. (2)快速排序的基本思想    设当前待排序的无序区为R[low..high],利用分治法可将快速排序的基本思想描述为:分解:      在R[low..hi