算法 - 快速排序
1.算法解析
方法其实很简单:分别从初始序列“6 1 2 7 9 3 4 5 10 8”两端开始“探测”。先从右往左找一个小于6的数,再从左往右找一个大于6的数,然后交换他们。这里可以用两个变量i和j,分别指向序列最左边和最右边。我们为这两个变量起个好听的名字“哨兵i”和“哨兵j”。刚开始的时候让哨兵i指向序列的最左边(即i=1),指向数字6。让哨兵j指向序列的最右边(即j=10),指向数字8。
现在交换哨兵i和哨兵j所指向的元素的值。交换之后的序列如下。
2.为什么右哨兵必须先移动?
总结
症结在,要和最左边的benchmark交换。benchmark和相遇点的元素交换后,依然要符合“左边的比benchmark小,右边的比benchmark大”。
右哨兵先动
因为右哨兵先动,所以相遇点的元素(红色)一定小于X,所以交换后,相遇点元素位于X的左边。依然符合要求
左哨兵先动
因为左哨兵先动,所以相遇点的元素(红色)一定大于X,所以交换后,相遇点元素位于X的左边。不再符合要求。
因此:
选取最左边的元素作为benchmark, 那么右哨兵需要先移动。
选取最右边的元素作为benchmark, 那么左哨兵需要先移动
3.代码
易错点
benchmark和left左边必须是一致的
benchmark和left, right相等的情况,继续move
如果需要自定义大小关系,不要用boolean函数,而是返回1,0,-1来代表大于,等于,小于。这样可以完美融合原来的逻辑。
public int[] quickSort(int[] nums){ if(nums == null || nums.length == 0){ return nums; } System.out.print("Original nums[] is: "); printIntArray(nums); quickSortHelper(0, nums.length - 1, nums); return nums; } public void quickSortHelper(int left, int right, int[] nums){ if(left >= right){ return; } //为什么这里要把left,right重新赋值给l,r? //是因为l,r需要在while过程中变动去交换值,而left,right是为了递归,直到进入下一个递归前,需要需要保持不变 int l = left; int r = right; int temp; //l<r的条件控制,放在子while中去控制。这里只控制l != r,因为当l == r时,会跳出循环,触发benchmark与nums[l]/nums[r]交换 while(l != r){ //加入l < r,防止移动过程中出现l >= r, nums[left]作为benchmark while(nums[r] >= nums[left] && l < r){ r--; } //加入l < r,防止移动过程中出现l >= r, nums[left]作为benchmark while(nums[l] <= nums[left] && l < r){ l++; } //这时候,不一定l==r了, 也有可能是各自找到符合条件的,但还没有最终汇合 if(l < r){ temp = nums[r]; nums[r] = nums[l]; nums[l] = temp; System.out.print("Swap nums[l] and nums[r]: "); printIntArray(nums); } } //当l,r汇合后,触发benchmark与nums[l]/nums[r]交换 temp = nums[left]; nums[left] = nums[l]; nums[l] = temp; if(left < right){ System.out.print("After finish one process: "); printIntArray(nums); } //递归 quickSortHelper(left, l - 1, nums); quickSortHelper(l + 1, right, nums); } public void printIntArray(int[] nums){ for(int num : nums){ System.out.print(num + " "); } System.out.print("\n"); } public static void main(String[] args){ Solution solution = new Solution(); int[] ints = new int[]{6,1,2,7,9,3,4,5,10,8}; solution.quickSort(ints); }
运行结果:
Original nums[] is: 6 1 2 7 9 3 4 5 10 8 Swap nums[l] and nums[r]: 6 1 2 5 9 3 4 7 10 8 Swap nums[l] and nums[r]: 6 1 2 5 4 3 9 7 10 8 After finish one process: 3 1 2 5 4 6 9 7 10 8 After finish one process: 2 1 3 5 4 6 9 7 10 8 After finish one process: 1 2 3 5 4 6 9 7 10 8 After finish one process: 1 2 3 4 5 6 9 7 10 8 Swap nums[l] and nums[r]: 1 2 3 4 5 6 9 7 8 10 After finish one process: 1 2 3 4 5 6 8 7 9 10 After finish one process: 1 2 3 4 5 6 7 8 9 10
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?