今天,我看了一本算法书《啊哈,算法》,里面的快速排序算法思路非常简单易懂,图文并茂。我们先分析一下书上提供的内容,然后再分析我尝试实现的代码,出现的bug,以及问题分析。
快速排序又称分区交换排序法,是目前公认最佳的排序法。基本原理:先选定数据第一个元素作为基准数(或称锚点),作为数据中的一个虚拟的中间值,把小于中间值的数据放在其左边,而大于中间值的数据放在其右边。再以同样的方式分别处理左右两边的数据,直到完成为止。
最重要的是算法的复杂度分析:快速排序的平均时间复杂度是;最坏时间复杂度是;空间复杂度为;稳定性:不稳定。
“6 1 2 7 9 3 4 5 10 8”序列进行快速排序
首先在这个序列中随便找一个数作为基准数(不要被这个名词吓到了,这就是一个用来参照的数,待会儿你就知道它用来做啥了)。为了方便,就让第一个数 6 作为基准数吧。接下来,需要将这个序列中
所有比基准数大的数放在 6 的右边,比基准数小的数放在 6 的左边,类似下面这种排列。
在初始状态下,数字 6 在序列的第 1 位。我们的目标是将 6 挪到序列中间的某个位置,假设这个位置是 k。现在就需要寻找这个k,并且以第 k 位为分界点,左边的数都小于等于 6,右边的数都大于等于 6。
方法其实很简单:分别从初始序列“6 1 2 7 9 3 4 5 10 8”两端开始“探测”。先从右往左找一个小于 6 的数,再从左往右找一个大于 6 的数,然后交换它们。这里可以用两个变量 i 和 j,分别指向序列最左边和最右边。我们为这两个变量起个好听的名字“哨兵 i”和
“哨兵 j”。刚开始的时候让哨兵 i 指向序列的最左边(即 i=1),指向数字 6。让哨兵 j 指向序列的最右边(即 j=10),指向数字 8。
首先哨兵 j 开始出动。 因为此处设置的基准数是最左边的数,所以需要让哨兵 j 先出动,这一点非常重要(请自己想一想为什么)。哨兵 j 一步一步地向左挪动(即 j--),直到找到一个小于 6 的数停下来。接下来哨兵 i 再一步一步向右挪动(即 i++),直到找到一个大于 6的数停下来。最后哨兵 j 停在了数字 5 面前,哨兵 i 停在了数字 7 面前。
现在交换哨兵 i 和哨兵 j 所指向的元素的值。交换之后的序列如下。
到此,第一次交换结束。接下来哨兵 j 继续向左挪动(再次友情提醒,每次必须是哨兵j 先出发)。他发现了 4(比基准数 6 要小,满足要求)之后停了下来。哨兵 i 也继续向右挪动,他发现了 9(比基准数 6 要大,满足要求)之后停了下来。此时再次进行交换,交换之后的序列如下。
到此第一轮“探测”真正结束。此时以基准数 6 为分界点, 6 左边的数都小于等于 6, 6右边的数都大于等于 6。回顾一下刚才的过程,其实哨兵 j 的使命就是要找小于基准数的数,而哨兵 i 的使命就是要找大于基准数的数,直到 i 和 j 碰头为止。
OK,解释完毕。现在基准数 6 已经归位,它正好处在序列的第 6 位。此时我们已经将原来的序列,以 6 为分界点拆分成了两个序列,左边的序列是“3 1 2 5 4”,右边的序列是“9 7 10 8”。接下来还需要分别处理这两个序列,因为 6 左边和右边的序列目前都还是很混乱的。不过不要紧,我们已经掌握了方法,接下来只要模拟刚才的方法分别处理 6 左边和右边的序列即可。(内容省略)
看一下书中给的C语言代码:
Java相关代码:
import java.util.Arrays;
public class QuickSortDemo
{
public static void main(String[] args)
{
int[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
QuickSort(arr, 0, arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void QuickSort(int[] arr, int left, int right)
{
int i, j, temp;
if(left > right)
{
return;
}
i = left;
j = right;
temp = arr[i];
while(i != j)//或者是i < j,不能是i <= j
{
//特别注意顺序,先从右往左,再从左往右
while(arr[j] >= temp && i < j)
j--;
while(arr[i] <= temp && i < j)
i++;
/*顺序错误,结果错误
while(arr[i] <= temp && i < j)
i++;
while(arr[j] >= temp && i < j)
j--;
*/
if(i < j)//探测未结束,交换arr[i]和arr[j]
{
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
//探测结束,交换arr[i]和temp
arr[left] = arr[i];
arr[i] = temp;
QuickSort(arr, left, i-1);
QuickSort(arr, i+1, right);
}
}
/*
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
*/
错误一:代码第二21行,为什么while(i <= j)//或者是i < j,不能是i <= j
import java.util.Arrays;
public class QuickSortDemo
{
public static void main(String[] args)
{
int[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
QuickSort(arr, 0, arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void QuickSort(int[] arr, int left, int right)
{
int i, j, temp;
if(left > right)
{
return;
}
i = left;
j = right;
temp = arr[i];
while(i <= j)//或者是i < j,不能是i <= j
{
System.out.println(i);
//特别注意顺序,先从右往左,再从左往右
while(arr[j] >= temp && i < j)
j--;
while(arr[i] <= temp && i < j)
i++;
/*顺序错误,结果错误
while(arr[i] <= temp && i < j)
i++;
while(arr[j] >= temp && i < j)
j--;
*/
if(i < j)//探测未结束,交换arr[i]和arr[j]
{
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
//探测结束,交换arr[i]和temp
arr[left] = arr[i];
arr[i] = temp;
QuickSort(arr, left, i-1);
QuickSort(arr, i+1, right);
}
}
/*
程序卡死在i=j=5
*/
如上图,有种状态是i == j == 5,程序会卡死在这个地方。 分析while(i <= j)循环内的语句,i == j,循环执行,特别注意j--,i++不满足,if判断也不执行,一直在while(i <= j)循环空语句。
问题二:顺序错误,结果错误
import java.util.Arrays;
public class QuickSortDemo
{
public static void main(String[] args)
{
int[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
QuickSort(arr, 0, arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void QuickSort(int[] arr, int left, int right)
{
int i, j, temp;
if(left > right)
{
return;
}
i = left;
j = right;
temp = arr[i];
while(i != j)//或者是i < j,不能是i <= j
{
System.out.println(i);
/*//特别注意顺序,先从右往左,再从左往右
while(arr[j] >= temp && i < j)
j--;
while(arr[i] <= temp && i < j)
i++;
*/
// 顺序错误,结果错误
while(arr[i] <= temp && i < j)
i++;
while(arr[j] >= temp && i < j)
j--;
if(i < j)//探测未结束,交换arr[i]和arr[j]
{
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
//探测结束,交换arr[i]和temp
arr[left] = arr[i];
arr[i] = temp;
QuickSort(arr, left, i-1);
QuickSort(arr, i+1, right);
}
}
//[1, 2, 5, 3, 4, 9, 6, 10, 7, 8]
问题分析:分析左子问题,显然结果不正确
快速排序的动画过程:
参考资料:《啊哈,算法》 啊哈磊著