快速排序(Quicksort)

同冒泡排序一样,快速排序也属于交换排序,通过元素之间的比较和交换位置来达到排序的目的。

不同的是,冒泡排序在每一轮只把一个元素冒泡到数列的一端,而快速排序在每一轮挑选一个基准元素,并让其他比它大的元素移动到数列一边,比它小的元素移动到数列的另一边,从而把数列拆解成了两个部分。

快速排序(Quicksort)_第1张图片

这种方法叫分治法。

快排的主要思想就是分治,每次把大于基准和小于基准的数分在两边,每部分在下次又分为两个部分,直到不可再分。

这种方法平均情况下需要进行 logn 轮,因此快速排序算法的平均时间复杂度是 O(nlogn)。但是有时选基准会选到区间内的最大或最小值,这样每次就只是确定了基准的位置,没有发挥分治法的优势,这种极端条件下,会进行 n 轮,所以快排最坏的时间复杂度为 O(n²)

确定好基数后就是把数分为大于基数和小于基数的两部分了,分数有两种方法:

1. 挖坑法

2. 指针交换法

 

挖坑法

挖坑法顾名思义就是挖坑,挖完坑找数填坑。

我们举个栗子来理解他,规定一个数组:

序号 0 1 2 3 4 5 6 7
数字 14 7 16 5 3 22 8 10
指针 i             j

首先确定一个基准元素 key = a[0] = 14,此时 a[0] 这个位置就相当于一个坑。初始时左右各一个指针,i = 0,j = 7,i 将要指向大于基准的数的位置,j 将要指向小于基准的位置,因为要保证基准数左边全部是小于基准的数,基准右边全部大于基准,所以 j 从最右边开始左移,直到指向小于基准的数,放到左边的坑里,此时就出现了一个新坑(j 指向的小于基准的数的位置),i 再从左开始找到大于基准的数,填到右边的坑里,这时 i 指向的大于基准的数的位置就是一个新坑,重复上述步骤,直到没有可以改变的位置。

下面来模拟过程(红色位置为坑,蓝色位置为找到的要填的数的位置)

序号

0

1 2 3 4 5 6

7

数字 14 7 16 5 3 22 8 10
指针 i             j

从右开始找到位置 7 为小于基准的数的位置,把 a[7] 这个数填到 0 这个坑里,结果:

序号

0

1 2 3 4 5 6

7

数字

10

7 16 5 3 22 8 10
指针 i             j

此时 7 就是一个新坑,再从左开始找比基准数大的值

序号 0 1

2

3 4 5 6

7

数字 10 7 16 5 3 22 8 10
指针     i         j

找到了 2 这个位置,再把 a[2] 填到 7 这个坑里,结果:

序号 0 1

2

3 4 5 6

7

数字 10 7 16 5 3 22 8

16

指针     i         j

2 为新坑,再从右开始找比基准小的数填到左边

序号 0 1

2

3 4 5

6

7
数字 10 7

8 

5 3 22 8 16
指针     i       j  

将 a[6] 填入 2 中,6 为坑,继续从左边找

序号 0 1 2 3 4

5

6

7
数字 10 7  8  5 3 22

22

16
指针           i j  

找到 5 的位置,将 a[5] 填到坑6 里,5 为新坑,j 继续向左移动

序号 0 1 2 3 4

5

6 7
数字 10 7  8  5 3 22 22 16
指针           i , j    

此时 i, j 重合,5 位置为坑,把之前的基准 key 填入坑 5,这样一次循环就结束了,结果:

序号 0 1 2 3 4

5

6 7
数字 10 7  8  5 3

14

22 16
指针           i , j    

主要代码

void quick_sort(int a[], int l, int r)
{
	if(l=key) j--;
			
			if(i

运行过程

快速排序(Quicksort)_第2张图片

 

指针交换法

挖坑法是找到一个放一个,而指针交换法是同时找到两个,交换他们的位置

同样的,来举栗子:

序号 0 1 2 3 4 5 6 7
数字 14 7 16 5 3 22 8 10
指针 i             j

没错还是这个数组,还是这个基准 key = a[0] = 14,还是这个指针 i = 0,i = 7,不同的是,这次小 i 和小 j 一同奔跑(为了能快点见面也是不容易),i 还是向右找到大于基准的数,j 还是向左找到小于基准的数

序号 0 1

2

3 4 5 6

7

数字 14 7 16 5 3 22 8 10
指针     i         j

找到后交换两个指针指向的数字

序号 0 1

2

3 4 5 6

7

数字 14 7

10

5 3 22 8

16

指针     i         j

交换后继续奔跑

序号 0 1 2 3 4

5

6

7
数字 14 7 10 5 3 22 8 16
指针           i j  

继续交换

序号 0 1 2 3 4

5

6

7
数字 14 7 10 5 3

8

22

16
指针           i j  

交换后会发现,挨着了,怎么办,这时要先让小 j 跑,跑到小 i 的位置,为什么呢,先来看一下小 j 先跑的结果

序号 0 1 2 3 4

5

6 7
数字 14 7 10 5 3 8 22 16
指针           i, j    

别忘了还有个基准等着分配,此时就要和基准交换了

序号

0

1 2 3 4

5

6 7
数字

8

7 10 5 3

14

22 16
指针           i, j    

有木有感觉很完美,这就是小 j 先跑的结果,咱们再返回去看小 i 先跑的结果

序号 0 1 2 3 4 5

6

7
数字 14 7 10 5 3 8 22 16
指针             i, j  

然后进行交换

序号

0

1 2 3 4 5

6

7
数字

22

7 10 5 3 8

14

16
指针             i, j  

还记不记得快排的思想是什么,基准左边的数都要小于基准,现在 22 在 14 左边就出错了,所以每次移动,要先让小 j 动

主要代码

void quick_sort(int a[], int l, int r)
{
	if(l > r) return;
	
	int i = l, j = r, key = a[l];
	while(i!=j)
	{
		while(i=key) j--;
		
		while(i

运行过程

快速排序(Quicksort)_第3张图片

你可能感兴趣的:(算法总结,快排)