
在java快速排序的partition函数中,第二次swap(array, links, j)用于将基准元素(pivot)放置到其最终正确位置,确保左子数组所有元素≤pivot、右子数组所有元素>pivot,这是递归分治的前提。
在java快速排序的partition函数中,第二次swap(array, links, j)用于将基准元素(pivot)放置到其最终正确位置,确保左子数组所有元素≤pivot、右子数组所有元素>pivot,这是递归分治的前提。
快速排序的核心在于分区(partition):选定一个基准元素(pivot),通过一次扫描将数组划分为三部分——左段(≤ pivot)、pivot自身、右段(> pivot)。你实现的Lomuto风格分区以array[links]为pivot,初始位于最左端。整个while循环的作用是寻找所有“错位”元素(即左侧出现大于pivot的值,或右侧出现小于等于pivot的值),并通过第一次swap完成成对矫正。
但关键在于:循环结束时,pivot仍滞留在原位(array[links]),而指针j停在了左段的最后一个有效位置(即最后一个≤ pivot的元素索引)。此时数组状态并非真正分区完成——pivot尚未就位,左右两段也未被pivot隔开。
举个例子,对数组 [7, 6, 1, 3, 5, 10, 15, 11, 1, 2, 18] 执行你的partition(pivot = 7):
- 循环结束后,
i = 10,j = 9,此时array[j] == 2(≤7),且array[9]是左段最右元素; - 但
array[0]仍是7,而它本应位于索引9处,使[0..8]全≤7、[10..]全>7; - 因此必须执行
swap(array, links, j)—— 即swap(array, 0, 9),将7与2互换,得到:[2, 6, 1, 3, 5, 10, 15, 11, 1, 7, 18]
此时,返回j = 9,恰好是pivot的新下标,也是左子数组的右边界(含),右子数组从j+1开始。
⚠️ 注意事项:
- 若省略第二次swap,pivot将始终卡在首位置,导致递归调用时左子数组包含pivot本身,引发无限递归或逻辑错误(即使对已排序数组“看似工作”,实则是巧合掩盖了分区失效);
-
j是循环终止后最后一个≤ pivot的元素索引,而非pivot应处位置的“预测值”——它正是通过这次swap才成为pivot的最终落点; - 返回
j而非i,是因为i > j时循环退出,j稳定指向左段末尾,语义清晰且边界安全(避免i越界)。
// 正确的分区后调用示意 int pivotIndex = partition(array, left, right); quicksort(array, left, pivotIndex - 1); // 左子数组:[left, pivotIndex-1] quicksort(array, pivotIndex + 1, right); // 右子数组:[pivotIndex+1, right]
简言之,第二次swap不是“锦上添花”,而是分区操作的收尾动作和契约履行:它把pivot钉入最终位置,使j成为可信赖的分割点,从而保障整个快速排序算法的正确性与稳定性。










