快速排序算法Quicksort
排序算法中最难的当属快速排序算法,但是快速排序算法的思想可以作为以后编程的一种想法,其基本思想是分治思想,简单的说就是分而治之,讲一个大问题分解为有限个问题,分解为的问题还可以继续向下分解,成为更简单的问题,我们将所有的小问题解决后,大的问题自然也就解决了。
首先在解决快速排序之前,先要解决的一个问题是,我们选择一个标准值,大于这个数的,将他放在标准值的右侧,小于这个数的放在标准值的左侧,这样我们就将序列的排序问题划分成了两个小序列的快速排序问题。
首要问题是我们将序列以标准值划分的问题,左侧小于标准值的,右侧大于标准值,再将标准值插入其中的序列,即Split算法。
算法的基本思想:
首先我们取第一个位置的元素作为标准值(取一个标记i),在以第二个值(取一个标记j),这样我们进行比较,假设数值并没有标准值大的话,i向前移动一位,(一般在数值比较的开头,看i和j是否相等,不相等就交换i和j的位置上的值,j++),如果值大于标准值,j++,直到遇到比标准值小得数字,交换i和j位置的数值,直到j取到最后一个数为止,此时i位置上的数字以及i之前的数字都比标准值要小,i后面的数字都比标准值要大,这是交换第一个数字和i位置的数字,就实现了在标准值的左侧都比标准值小的序列,而在标准值的右侧,所有的值都比标准值要大。
实例演示:
4,3,7,9,2,5,6
(1)第一步,取第一个值作为标准值,并且以low作为数字的标记,同时给出标记i,j=i+1,将j标记i后面的数,i比较j位置上的值与标准值的大小,如果比标准值小,i++,进行判断i是否等于将,如不等,交换i和j位置的值,否则不动,j++;;如果比标准值大,则j++;进行判断i是否等于将,如不等,交换i和j位置的值,否则不动;
i i
4,3,7,9,2,5,6 -------------------------------结果 4,3,7,9,2,5,6 操作:1、比较(小于) 2、i++,判断i==j,等于,则j++
low j low j
(2)重复(1)的操作,得以下结果
i i
4,3,7,9,2,5,6 -------------------------------结果 4,3,7,9,2,5,6 操作:1、比较(大于) 2、直接j++
low j low j
(3)结果如下
i i
4,3,7,9,2,5,6 -------------------------------结果 4,3,7,9,2,5,6 操作:1、比较(大于) 2、直接j++
low j low j
(4)结果如下
i i
4,3,7,9,2,5,6 -------------------------------结果 4,3,2,9,7,5,6 操作:1、比较(小于) 2、i++,判断i==j(不等)交换i和j上的值
low j low j
(5)结果如下
i i
4,3,2,9,7,5,6 -------------------------------结果 4,3,2,9,7,5,6 操作:1、比较(大于) 2、直接j++
low j low j
5)结果如下
i i
4,3,2,9,7,5,6 -------------------------------结果 2,3,4,9,7,5,6 操作:1、比较(大于) 2、最后一个元素审查完,交换i和low上的值
low j low j
最终经过split算法后的结果为 2,3,4,9,7,5,6 ,在之前的都比4小,在4后面的都比4要大,算法完成
算法中的要点:i和j的比较是为了在开始时不存在比标准值大的时候防止交换;
代码如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 int split(int *a, int low, int high); 4 int main() { 5 int a[7] = { 4,3,7,9,2,5,6 }; 6 for (int i = 0; i < 7; i++) { 7 if (i != 6) { 8 printf("%d,", a[i]); 9 } 10 else { 11 printf("%d\n", a[i]); 12 } 13 } 14 int m = split(a, 0, 7); 15 for (int i = 0; i < 7; i++) { 16 if (i != 6) { 17 printf("%d,", a[i]); 18 } 19 else { 20 printf("%d\n", a[i]); 21 } 22 } 23 system("pause"); 24 } 25 26 int split(int *a, int low, int high) { 27 int i = 0; 28 int x = a[low]; 29 int temp; 30 for (int j = i + 1; j < high; j++) { 31 if (a[j] < x) { 32 i++; 33 if (i != j) { 34 temp = a[i]; 35 a[i] = a[j]; 36 a[j] = temp; 37 } 38 } 39 } 40 temp = a[i]; 41 a[i] = a[low]; 42 a[low] = temp; 43 return i; 44 }
代码的结果:
上面的代码就是快速排序算法的核心步骤,下面我们来聊聊快速排序,我们使用一次split就会分出来两个部分,然后再对两个部分再次使用split算法,则又会向下分解,分解就调用quicksort算法,相当于是递归的过程,那么我们关心的就是这个排序的过程什么时候停下来。
当一个序列存在两个及两个以上的值的时候,都可以以其中一个作为标准值,另一个一定在左或者右,依旧可以调用quicksort算法,所以当一个序列中只有一个值的时候,递归就停止了,显示在序列中就是low=high,所以只要做判断low<high,递归算法继续
下面是Quicksort的代码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 int split(int *a, int low, int high); 4 int Quicksort(int *a, int low, int high); 5 int main() { 6 int a[7] = { 4,3,7,9,2,5,6 }; 7 for (int i = 0; i < 7; i++) { 8 if (i != 6) { 9 printf("%d,", a[i]); 10 } 11 else { 12 printf("%d\n", a[i]); 13 } 14 } 15 Quicksort(a, 0, 6); 16 for (int i = 0; i < 7; i++) { 17 if (i != 6) { 18 printf("%d,", a[i]); 19 } 20 else { 21 printf("%d\n", a[i]); 22 } 23 } 24 system("pause"); 25 } 26 27 int split(int *a, int low, int high) { 28 int i = low; 29 int x = a[low]; 30 int temp; 31 int w; 32 for (int j = i + 1; j <= high; j++) { 33 if (a[j] < x) { 34 i++; 35 if (i != j) { 36 temp = a[i]; 37 a[i] = a[j]; 38 a[j] = temp; 39 } 40 } 41 } 42 temp = a[i]; 43 a[i] = a[low]; 44 a[low] = temp; 45 w = i; 46 return w; 47 } 48 49 int Quicksort(int *a, int low, int high) { 50 if (low < high) { 51 int m = split(a, low, high); 52 Quicksort(a, low, m - 1); 53 Quicksort(a, m+1, high); 54 } 55 }
代码运行结果如下:
Quicksort(a,0,6)
在split中j的取值范围:j<=high
Quicksort(a,0,7)
在split中j的取值范围:j<high
我在敲代码的发现第一种情况是成立,而第二种运行的结果并不对;
原因:是因为j的取值可以取到high,7是数组的上界取不到是必然,但是在循环中,可能会有一个小段high的值达不到7,(以5为例),j是可以取得到5的但是不等式中,要求j<high ,就会发生漏排的情况,所以Quicksort(a,0,7),j<high是错误的;