我正在研究基本的数组排序,并且正在努力完全理解它的逻辑。我理解递归,意味着将数组拆分为数据库每一侧的两个数组,然后继续对每个子数组进行分区,直到达到只有一个元素的数组。 我并不总是完全理解的是while循环本身的实现。
http://www.vogella.com/tutorials/JavaAlgorithmsQuicksort/article.html
这里我遇到了一个选择中间元素作为数据透视的实现。我理解枢轴可以是任何元素,无论是第一个,最后一个,任何随机元素还是为最大效率而专门选择的元素。 由于某种原因,将中间元素作为枢轴,我发现它更直观易懂。
while (i <= j) {
// If the current value from the left list is smaller than the pivot
// element then get the next element from the left list
while (numbers[i] < pivot) {
i++;
}
// If the current value from the right list is larger than the pivot
// element then get the next element from the right list
while (numbers[j] > pivot) {
j--;
}
// If we have found a value in the left list which is larger than
// the pivot element and if we have found a value in the right list
// which is smaller than the pivot element then we exchange the
// values.
// As we are done we can increase i and j
if (i <= j) {
exchange(i, j);
i++;
j--;
}
}
这是相关部分。
1)为什么只有当i元素小于pivot或j元素大于pivot时,我们才增加i并减少j?为什么不平等呢? 如果一个元素等于枢轴,那么它就好了,因为它最终位于枢轴的哪一侧并不重要,那么为什么我们不能继续增加/减少呢? 我试过这样做,因为我预计最终结果没有排序,即使经过一步一步的调试,我也不明白出了什么问题。
2)我是否正确地说,除了边缘情况,在退出外部循环时,i = j +1?总是?而这些元素中的一个,i或j,是否具有我们使用的支点的价值?但是哪一个和为什么?
// Recursion
if (low < j)
quicksort(low, j);
if (i < high)
quicksort(i, high);
3)我在这里期待的是,而不是传递低到j和我到高, 是: 假设pivotIndex是循环结束后的pivot的索引,
quicksort(low, pivotIndex - 1);
quicksort(pivotIndex + 1, high);
因为pivotIndex正好在枢轴所在的位置,所以这是他的最终位置。 所以我也很乐意为此做出解释。
谢谢。
答案 0 :(得分:1)
arr = {1, 20, 3, 4, 20, 70, 80, 90, 100}
i->1
j->100
pivot = 20 (the 2nd one)
while (numbers[i] <= pivot)...
=&gt; i->70
while (numbers[j] >= pivot)...
=&gt; j->20 (2nd one)
if (i <= j)...
=&gt; false
while (i <= j)
=&gt;假i -> 20 (1st one)
和j -> 20 (2nd one)
发生交换。这里重要的不是交换本身,而是i
在第一次迭代中没有变得大于j
的事实i=j
声明中的if
怎么办?这会同时执行i++
和j--
两者之间的区别2.如果不发生这种情况,那么是i=j+1
并且您无法判断它是否i
{ {1}}或j
等于枢轴,因为它取决于数组中枢轴的位置。i
将等于pivotIndex + 1
,j
将等于pivotIndex - 1
; i
或j
也将包含pivotIndex
本身。您可以使用笔和纸一步一步地完成它,看看发生了什么。希望有所帮助
答案 1 :(得分:1)
在进入角落的情况之前,让我们来看看算法的正常运行。样本起始数组如下所示。数据透视值为33.索引i
和j
显示在数组上方。
while
圈次更新i
和j
。 i
向前移动到大于枢轴的第一个值。绿色(25和8)中的值小于枢轴,并且处于最终位置。红色(49)中的值大于枢轴,并且处于最终位置。
现在,数字51和14被交换,i
递增,j
递减。
while
圈次更新i
和j
。请注意,j
没有移动。
交换后(枢轴值显示为黄色)
while
次循环和i
再次移过j
,完成了分区。您可以看到从i
到数组末尾的所有值都是红色(大于枢轴)或黄色(枢轴)。从j
到数组开头的所有值都是绿色(小于数据透视)或黄色。
所以在这种情况下i=j+1
和其中一个索引(i
)指向枢轴。
现在让我们看一下i
最终不等于j+1
的数组,i
和j
都不指向枢轴值。起始数组(再次使用33作为枢轴):
while
循环之后:
用50交换2后
while
循环之后:
请注意,i
和j
都停在了透视值处。所以在交换之后,情况看起来像这样:
我相信回答问题2和3. i=j+1
并不总是如此,并且其中一个索引指向枢轴并不总是正确的。此外,在某些情况下,算法已经从递归调用中排除了数据透视。
你可以修改算法以在更多情况下排除枢轴,但这似乎有风险和混乱。除非阵列尺寸相当小,否则没有多大好处。在生产qsort
实现中,当数组大小变小时,算法会切换到不同的方法(例如选择排序)。
问题1.如果修改了while循环以允许等于pivot的值,会发生什么?答案是其中一个索引可以在数组末尾运行。这是一个例子(使用33作为支点):
所有值都大于或等于数据透视表,因此j
索引将在数组末尾运行:
取决于数组前面的内存中的内容,j
索引可能会持续很长时间,甚至会导致程序崩溃。这可以通过比较j
和零来解决
while(j>0 && numbers[j] >= pivot)
但额外检查会减慢算法速度。不停留在数据透视表中节省的时间量与在while
循环中进行额外检查所浪费的时间差不多。
答案 2 :(得分:0)