1.为什么使用do while循环,而不用while循环: do while循环的好处在于,i和j会一直在更新,而不会因为在某种特殊的情况下,导致循环卡死。 例如当arr[i] == arr[j] == x 时,i和j都不会更新,导致while进入了死循环当中。 此案例也可以说明为什么会写成i = l - 1;和j = r + 1; 就是为了让i和j会一直在更新 ,适配do while循环所变化的 2.循环条件为什么是 arr[i]<x / arr[j]>x ,而不是 arr[i]<=x / arr[j]>=x. 如果数组中的数值全都相同的话,do i++; while(arr[i] <= x);这个语句会一直执行,最后导致数组越界访问。这和另一种情况,所选数值x为整个数组中max一直,do i++; while(arr[i] <= x);此语句会一直执行,最后到数组越界访问而停止。 3. 递归分界点的选择,为什么以 j 划分,不能让 x=arr[r];以 i 划分,不能让 x=arr[l]呢。 以 j 划分为例,首先我们可以知道myQuickSrot(arr, j+1 , r)这个递归是不会进入死循环的,因为 j+1 始终会改变再次进入函数的 l 值。myQuickSrot(arr, l, j)这个递归却有出现 死循环的可能,原因就是,程序永远会走到 arr[l..r-1]<=x , arr[r]=x;当这一步继续执行就是 i=r,j=r; 而这就是进入本次循环的 j 是相同的,所以就会这样无限划分下去。 4. while(i < j) 能否改为 while(i <= j) 答案是不能的,因为在某些情况下,j=l-1 的此时会造成数组越界访问。 比如说 在数组只剩 [a,b] 且 a<b 时,这种情况下,初始 i = l - 1, j = r + 1,第一轮 while 循环结束 i = l, j = l,第二轮 while 循环结束 i = r, j = l-1。而 j = l-1 会造成数组越界访问,while(i<j) 就不会,因为在第一次while循环结束后,就跳出此次循环了。 有些人可能会疑惑: 这种情况看起来比较极端啊, 如果构造数组 [3, 2, 1] 会不会就不会遇到这种情况了,其实不然, 因为快排是分治算法, 往下递归时总会遇到 [a, b], a < b 这种情况,只要有一个这种情况, 就会进入无限划分出不来.
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] arr = new int[n];
for (int i = 0; i < n; i ++) {
arr[i] = sc.nextInt();
}
myQuickSrot(arr,0,n - 1);
for(int i = 0; i < n; i++) {
System.out.print(arr[i]+" ");
}
}
public static void myQuickSrot(int[] arr,int l,int r) {
if(l >= r) {
return ;
}
int i = l - 1;
int j = r + 1;
int x = arr[r+l >> 1];
while(i < j) {
do i++; while(arr[i] < x);
do j--; while(arr[j] > x);
if (i < j) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
myQuickSrot(arr,l,j);
myQuickSrot(arr,j + 1,r);
}