Java——快速排序(代码出现问题及分析)

     今天,我看了一本算法书《啊哈,算法》,里面的快速排序算法思路非常简单易懂,图文并茂。我们先分析一下书上提供的内容,然后再分析我尝试实现的代码,出现的bug,以及问题分析。

      快速排序又称分区交换排序法,是目前公认最佳的排序法。基本原理:先选定数据第一个元素作为基准数(或称锚点),作为数据中的一个虚拟的中间值,把小于中间值的数据放在其左边,而大于中间值的数据放在其右边。再以同样的方式分别处理左右两边的数据,直到完成为止。

最重要的是算法的复杂度分析:快速排序的平均时间复杂度是O\left ( NlogN \right );最坏时间复杂度是O\left ( N^{2} \right );空间复杂度为O\left ( logN \right );稳定性:不稳定。

    “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]

问题分析:分析左子问题,显然结果不正确

快速排序的动画过程:

参考资料:《啊哈,算法》 啊哈磊著

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值