一、进行划分
快速排序的原理:按照数组中的一个数,将数组划分成为小于
这个数、等于
这个数,大于
这个数三个部分
从而进行排序。
首先我们定义一个partition
方法,实现划分的功能:
定义L
为数组的左边界,R
为数组的右边界,把arr[R]
作为划分值,
即L
初始值定义为数组 0 的位置,R
初始值定义为数组最大索引(arr.length-1
)。
定义一个小于区域最右边的位置lessR
,初始值为-1
。
定义一个大于区域最左边的位置moreL
,初始值为R
(即最大索引位置)。
定义当前位置为index
,初始值为0
。
index
从第一位开始,和arr[R]
进行比较:
- 如果当前位置
小于
划分值(arr[R]
),则交换index
和lessR+1
的位置,然后index向后移动一位。即 :交换位置index++
和++lessR
; - 如果
大于
划分值,则交换index
和moreL-1
的位置, 但是index不移动
,但是moreL向前移动一位
。即index
和--moreL
; - 如果等于划分值,则
index向下移动一位
,即:index++
;
比较直到 index
和 moreL
相遇为止。
注意:最后还需要将arr[R]
和moreL
交换位置,此时数组完成划分。
/**
* 数组arr[L...R]范围上用arr[R]划分,返回等于arr[R]的范围
* partition 划分;分层
* @param arr
* @param L
* @param R
*/
public static int[] partition(int[] arr,int L ,int R){
//lessR:小于划分值arr[R]的做右边位置
int lessR = L - 1;
//moreL:大于划分值arr[R]的最左边位置
int moreL = R;
//index:当前位置
int index = L;
while (index < moreL){
if (arr[index] < arr[R]){
//小于arr[R] 交换lessR 和 index 的位置,index向下一位(即index++),lessR自增一位(先自增在使用即++lessR)
swap(arr,index++,++lessR);
}else if (arr[index] > arr[R]){
//大于arr[R] ,交换moreL和index 的位置,index位置不变,moreL向前一位(先减一再使用即--moreL)
swap(arr,index,--moreL);
}else {
//等于arr[R],不交换,index向下一位
index++;
}
//当index指针和moreL重合,跳出循环;所以最后index和moreL的值是一样的。
}
swap(arr,index,R);
return new int[] {lessR+1,moreL};
}
二、递归方法
/**
* 快速排序1 递归实现
* @param arr
*/
public static void quickSort1(int[] arr){
if (arr == null || arr.length < 2){
return;
}
process(arr,0,arr.length-1);
}
/**
* 快速排序-递归过程
* @param arr
* @param L
* @param R
*/
public static void process(int[]arr,int L,int R){
if (L >= R){
//如果 L>R 无效范围,如果L=R只有一个数,都直接返回
return;
}
//L<R
//将arr[L...R]范围上划分
int [] equal = partition(arr,L,R);
//equal[0] 等于区域的第一个数
//equal[1] 等于区域的最后一个数
//将等于区域左边的重新排序
process(arr,L,equal[0]-1);
//将等于区域右边的重新排序
process(arr,equal[1]+1,R);
}
三、非递归方法
定义一个Job辅助类,存放L和R边界,
/**
* 快速排序-非递归实现
* @param arr
*/
public static void quickSort2(int[] arr){
if (arr == null || arr.length < 2){
return;
}
Stack<Job> stack = new Stack<>();
Job job = new Job(0, arr.length - 1);
stack.push(job);
while (!stack.isEmpty()){
Job cur = stack.pop();
//从栈去除任务,进行划分
int[] queals = partition(arr, cur.L, cur.R);
if (queals[0] > cur.L){
//等于区域的最小值 大于L 说明存在小于区域
stack.push(new Job(cur.L,queals[0]-1));
}
if (queals[1] < cur.R){
//等于区域的最大值 小于R 说明存在大于区域
stack.push(new Job(queals[1]+1,cur.R));
}
}
}
public static class Job{
public int L;
public int R;
public Job(int L,int R){
this.L = L;
this.R = R;
}
}
四、测试
对数器
/**
* 通过对数器测试
* @param args
*/
public static void main(String[] args) {
int testTime = 500000;
int maxSize = 100;
int maxValue = 100;
boolean succeed = true;
System.out.println("=====测试开始=====");
for (int i = 0; i < testTime; i++) {
//随机生成数组
int[] arr1 = generateRandomArray(maxSize, maxValue);
//复制数组
int[] arr2 = copyArray(arr1);
quickSort1(arr1);
quickSort2(arr2);
//进行对比
if (!isEqual(arr1, arr2) ) {
System.out.println("排序错误!");
succeed = false;
break;
}
}
System.out.println("=====测试结束=====");
System.out.println(succeed ? "成功!" : "失败!");
}
其他方法
//交换位置
public static void swap(int[]arr, int i ,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
/**
* 生成随机数组
* @param maxSize
* @param maxValue
* @return
*/
public static int[] generateRandomArray(int maxSize, int maxValue) {
int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
}
return arr;
}
/**
* 复制数组
* @param arr
* @return
*/
public static int[] copyArray(int[] arr) {
if (arr == null) {
return null;
}
int[] res = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
res[i] = arr[i];
}
return res;
}
/**
* 判断数组是否相同
* @param arr1
* @param arr2
* @return
*/
public static boolean isEqual(int[] arr1, int[] arr2) {
if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
return false;
}
if (arr1 == null && arr2 == null) {
return true;
}
if (arr1.length != arr2.length) {
return false;
}
for (int i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]) {
return false;
}
}
return true;
}