我现在正在学校学习Java,我们最新的主题是Java中的排序算法。我试图理解的那个是快速的。
要理解该算法如何对数组中的数字进行排序,我决定在Eclipse调试器窗口中执行我的代码步骤。
现在有一个步骤让我无法理解,即使经历了数百次之后也无法理解。
我的初始数组是ACTION_SEND
当我浏览代码时,程序首先交换[10, 5, 3, 22, 11, 2]
和10
,然后2
和5
,然后3
和{{1} }。此时,2
的值为2
,i
的值为1
。
现在我看到的方式是有三个条件
j
返回-1
,因为while(i<=j)
和false
i = 1
返回j = -1
,因为if(left < j)
和false
left = 0
这也会返回j = -1
,因为if(i < right)
和false
但令我惊讶的是,当程序到达i = 1
之前的最后一个括号时,程序会跳回第40行right = 1
但是public static void display
,if(i < right)
,right
和i
的价值突然变为j
,pivot
,5
,和2
分别。
如果有人能解释为什么值会发生变化,我会非常感激
。我还添加了两张图片,显示了我在Eclipse窗口step I don't understand
上看到的内容-1
非常感谢!
答案 0 :(得分:3)
我认为你的问题是你不能完全理解递归。至少从你对这个问题的描述中听起来是什么。
无论如何,我试图通过记录所有变量来简单地遵循你的程序。希望这会有所帮助:
arr left right i j pivot
----------------- ------- -------- ------- ----- ----------
[10,5,3,22,11,2] 0 5
0 5 arr[2] = 3
[2,5,3,22,11,10] 1 4
3
2
[2,3,5,22,11,10] 2 1
while循环已经完成,因为i<=j
现在是false
(2 > 1
)。
第一个条件left < j
(0 < 1
)是true
,因此您再次递归地调用quicksort
:quicksort(arr, 0, 1)
- 这意味着您现在对数组进行排序{ {1}}已经排序,所以不会发生任何事情:
[2,3]
while循环条件现在是arr left right i j pivot
----------------- ------- -------- ------- ----- ----------
[2,3,5,22,11,10] 0 1
0 1 arr[0] = 2
0
[2,3,5,22,11,10] 1 -1
。第一个条件false
也是left < j
(因为false
)而第二个条件0 > -1
也是假的(因为i < right
)所以此调用已完成,你回到原来的位置。是那样的吗?上表的第一个条件。变量和参数的状态现在是:
1 == 1
检查第二个条件(因为它是下一行执行的)。它的值也是正确的,因为条件是arr left right i j pivot
----------------- ------- -------- ------- ----- ----------
[10,5,3,22,11,2] 0 5 2 1 3
(i < right
)。所以你现在再次递归2 < 5
:quicksort
- 这意味着你现在对数组quicksort(arr, 2, 5)
进行排序:
[3,22,11,10]
arr left right i j pivot
----------------- ------- -------- ------- ----- ----------
[2,3,5,22,11,10] 2 5
2 5 arr[3] = 22
3
[2,3,5,10,11,22] 4 4
5
现在我们退出while循环。
第一个条件i > j
(left < j
)是2 < 4
,因此我们调用true
以对已排序的quicksort(arr, 2, 4)
进行排序。我将跳过这一部分,因为它根本不会改变数组。
当递归调用完成后,我们返回到原来的状态,现在将检查第二个条件。 [5,10,11]
(i < right
)是5 < 5
所以我们已经完成了。
原始false
调用已完成,数组已排序。
答案 1 :(得分:2)
你看到的第一张图片显示调试器位于两个快速调用的递归调用中:quicksort从main调用,然后在第38行调用quicksort(这是quicksort的原理,它是一个递归策略)。所以你看到你在内部调用的第40行,然后当你从那里步进时,你回到上一次快速调用的调用(调试器在左上角的窗口显示一堆两行而不是三行并且你回到了先前的pivot等值。数组按原样传递给所有递归调用,因此它被共享,但是整数变量不是。
我认为这是一个很难理解的递归,检查你的调用堆栈。
答案 2 :(得分:1)
您使用打印语句和调试器研究排序算法的努力是值得称道的!但是你目前的Quicksort实现很难理解,至少在最初阶段,因为它已经进行了迭代和递归(即你使用循环,同时,你有一个程序quicksort
调用自己)。
让我们采用一种相当不同的方法(纯递归方法)来了解Quicksort的工作原理及其工作原理。将这个递归过程转换为迭代过程(有点像你已经写过的),幸运的是,这是技术问题(在这种情况下)!我建议我们在这里这样做,因为这样我们可以更好地控制复杂性。 同样,我这样做的原因是为了更好地了解正在发生的事情。
当Tony Hoare爵士提出算法并证明其正确性时,它是这样的:
public static void qsort(int[] ints, int fi, int li) {
/* the recursive procedure */
if (fi < li) {
int p = partition(ints, fi, li); // key routine -- see below
qsort(ints, fi, p - 1);
qsort(ints, p + 1, li);
}
}
就是这样!不漂亮吗?嗯,确实如此。你所做的就是:
qsort
过程对两个分区进行排序(递归调用的顺序不问题)你已经完成了!我尝试自己实施partition
程序:
/**
Implements partitioning of array a from a[p] to a[r], leaves all the other elements of the array intact.
@param a the given int array
@param p int first index of the part of array to be partitioned
@param r int the second index of the part of array to be partitioned
*/
public static int partition(int[] a, int p, int r) {
//this is something I have written
int pivot = a[p];
int i = p + 1;
int j = i - 1;
while (i <= r) {
if (a[i] < pivot) {
a[j] = a[i];
a[i] = a[j+1];
j++;
}
i++;
}
a[j] = pivot;
return j;
}
private static void swap(int[] ints, int i1, int i2) {
int tmp = ints[i2];
ints[i2] = ints[i1];
ints[i1] = tmp;
}
现在,我还没有证明这个程序的正确性,但我们可以单独完成。您甚至可以重新实现Lomuto procedure,并确保它让您满意。
就是这样。如果您现在想要使用调试器进行调试,那么您可以胜任。这是entire implementation。
现在,将这个递归过程转换为迭代过程的问题非常有趣,本文档:(无耻的插件:我写过它)http://bit.ly/recurse-iterate应该给你一些线索。将上述Quicksort程序实际转换为迭代程序留给读者练习: - )。