前言
面试中无可避免地会问到算法题,快速排序是很常见的题目,那么用java代码如何实现呢?
基础思想
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
基本步骤
1.从数列中挑出一个元素,称为 “基准”(pivot);
2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
3.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
测试代码
话不多说,先上代码,然后逐步分析,看每一步执行了什么操作。
package com.unicom;
/**
* @Description:java快速排序
* @Author: tkai
* @CreateDate: 2021年4月23日
* @Version:
*/
public class AarrySort {
public static void main(String[] args) {
int[] a = {20,1,34,32,18,13,16};
System.out.print("原始数组:");
printArray(a,0);
fastSortArray(a,0,a.length-1);
}
/**
* @Description:快速排序主函数
* @param ary 数组
* @param low 最小下标
* @param high 最大下标
*/
public static void fastSortArray(int[] ary,int low ,int high) {
System.out.println("开始下标:"+low+"----------------结束下标:"+high);
int start = low;
int end = high;
int key = ary[low];
System.out.println("基数下标:"+low+"基数值:"+key);
//进入循环
while(start<end) {
//从后往前比较
while (end>=start&&ary[end]>key) {
end--;
System.out.println("end减1");
}
/*如果大于关键值,就跳到前一个进行比较
* 如果小于等于关键值,替换当前值与start位的值*/
if(ary[end]<=key) {
int temp = ary[end];
ary[end] = ary[start];
ary[start] = temp;
System.out.print("[start:"+start+"]|[end:"+end+"]<"+ary[end]+">换<"+ary[start]+">");
printArray(ary,key);
System.out.println("跳出从后往前:");
}
//从前往后比较
while (end>start&&ary[start]<=key) {
start++;
System.out.println("start+1");
}
/*如果当前值小于关键值,就跳到后一个比较
* 如果当前值大于关键值,替换当前值与end的值*/
if(ary[start]>=key) {
int temp = ary[start];
ary[start] = ary[end];
ary[end] = temp;
System.out.print("[start:"+start+"]|[end:"+end+"]<"+ary[end]+">换<"+ary[start]+">");
printArray(ary,key);
System.out.println("跳出从前往后:");
}
//至此完成第一轮比较
}
//递归
if(start>low+1) {
//左边,从第一个到关键值位置的前一个
System.out.println("左侧进入递归");
fastSortArray(ary,low,start-1);
}
if(end+1<high) {
//右边,从关键值位置的后一个,到最后一个
System.out.println("右侧进入递归");
fastSortArray(ary,end+1,high);
}
}
/**
* @Description:便于识别基数的打印数组
* @param a
* @param key
*/
public static void printArray(int[] a,int key) {
System.out.print("[");
for (int i = 0; i < a.length; i++) {
if(a[i]==key) {
System.out.print("("+a[i]+"),");
}else if(i==a.length-1) {
System.out.print(a[i]);
}else {
System.out.print(a[i]+",");
}
}
System.out.print("]");
System.out.println();
}
}
过程分析
第一轮:
原始数组:[20,1,34,32,18,13,16]
开始下标:0----------------结束下标:6
基数下标:0基数值:20
先从后往前依次与基数20相比,
[start:0]|[end:6]<20>换<16>[16,1,34,32,18,13,(20),]
跳出从后往前:
start+1
start+1
[start:2]|[end:6]<34>换<20>[16,1,(20),32,18,13,34]
跳出从前往后:
end减1
[start:2]|[end:5]<20>换<13>[16,1,13,32,18,(20),34]
跳出从后往前:
start+1
[start:3]|[end:5]<32>换<20>[16,1,13,(20),18,32,34]
跳出从前往后:
end减1
[start:3]|[end:4]<20>换<18>[16,1,13,18,(20),32,34]
跳出从后往前:
start+1
[start:4]|[end:4]<20>换<20>[16,1,13,18,(20),32,34]
第一轮执行完成后,得到的数组为[16,1,13,18,20,32,34],此时进入判断是否需要进入左侧递归环节,start为4,大于最小下标0+1=1,需要进入递归。
这一轮最小下标不变,仍然为0,最大下标则变为上一轮结束时的start-1,即是3。即是前4个数的比较,看日志:
第二轮
左侧进入递归
开始下标:0----------------结束下标:3
基数下标:0基数值:16
end减1
[start:0]|[end:2]<16>换<13>[13,1,(16),18,20,32,34]
跳出从后往前:
start+1
start+1
[start:2]|[end:2]<16>换<16>[13,1,(16),18,20,32,34]
跳出从前往后:
执行完成后,得到数组[13,1,16,18,20,32,34],此时start为2,仍然大于最小下标0+1,仍然进入左侧递归。递归下标为0到1,即前两个数的比较。
第三轮
左侧进入递归
开始下标:0----------------结束下标:1
基数下标:0基数值:13
[start:0]|[end:1]<13>换<1>[1,(13),16,18,20,32,34]
跳出从后往前:
start+1
[start:1]|[end:1]<13>换<13>[1,(13),16,18,20,32,34]
跳出从前往后:
执行完成后,得到数组为[1,13,16,18,20,32,34],此时start为1,不大于0+1,不再进入左侧递归。可以看到,第一轮选择的基数20左侧的数字已经按从小到大排列了。
至此,判断是否进入右侧递归。end的值为4(我们看到第三轮结束时end和start为1,实际上是在判断进入左侧方法的代码块里的值,此处的值为4),end+1=5,
小于此时的最大下标6(与上一行标红处解释相同),应当进入右侧递归。
右侧递归的最小下标为end+1=5,最大下标仍为原最大下标6,即第6个数至第7个数的排序。
第四轮
右侧进入递归
开始下标:5----------------结束下标:6
基数下标:5基数值:32
end减1
[start:5]|[end:5]<32>换<32>[1,13,16,18,20,(32),34]
跳出从后往前:
[start:5]|[end:5]<32>换<32>[1,13,16,18,20,(32),34]
跳出从前往后:
执行完成后得到的数组为[1,13,16,18,20,32,34],可以看到,此时的数组已经排序完成。
但作为程序,依然会进入判断。
start =5 ,low=5, high = 6;
start不大于low+1,固不进入左侧递归;
end+1不小于high,故不进入右侧递归。
========================================至此结束,排序完成===============================