- 快速排序是大多数公司面试人员的必考题,今天将自己参考别人的思想所写的两种快速排序的方式;
- 快速排序使用场景:无序列表,时间复杂度为O(log2N);
- 快速排序的思想:
3.1、在一个数组中选取一个基准数,根据这个基准数将数组分为两组,一组为大于基准数,另外一组为小于基准数;
3.2、找出两个下标,一个为末尾下标,一个为开始下标;
3.3、先从末尾下标开始于基准数比较,有两种情况:
3.3.1、arr[end] > base 当末尾数大于基准数时,end下标变成倒数第二位,即end--;
3.3.2、arr[end] < base 当末尾数小于基准数时,首尾交换位置即arr[start] = arr[end],同时start++;
3.4、分成两堆之后,再次进行3.3操作;
4、递归实现快速排序:
/**
* @author 橙橙橙。
* 描述:快速排序思想:
* 1、选择一个基准数;
* 2、选定两个坐标,一个为左边第一个数,另外一个是右边第一个数;
* 3、先通过最右边的数与基准数比较,
* 如果大于等于,执行right--操作;
* 如果小于,进行arr[left] = arr[right]操作;
* 小于,交换之后,开始从左边比较,因为左边有空位
* 当左边出现大于基准的数的时候,又进行交换:arr[right] = arr[left];
* 然后遇到左边的数小于基准数,则进行left++;
* 3、通过一趟排序将数组分为两个部分,左边部分为小于基准数,右边部分为大于基准数
* 4、对这两个部分进行1、2、3、步骤
*/
public class QuickSort1 {
public static void main(String[] args) {
int[] arr = {4,5,9,7,8,9,12,4,65,32,78,24,55,76,77,44,32,11};
int[] arr1 = QuickSort(arr, 0, arr.length-1);
for(int i = 0; i < arr1.length; i++) {
System.out.print(arr1[i] + " ");
}
}
public static int[] QuickSort(int[] arr, int left, int right) {
//这个if必不可少,因为是递归结束的条件
if(left < right) {
int baseIndex = getTargetIndex(arr, left, right);
QuickSort(arr, left, baseIndex - 1);
QuickSort(arr, baseIndex + 1,right);
}
return arr;
}
//用于获取每一次基准数下表的函数
public static int getTargetIndex(int[] arr, int left, int right) {
//这个检验是为了保证程序的健壮性
if(left >= right || arr == null) {
return -1;
}
//key为基准数,其它数都要与这个key比较
int key = arr[left];
while(left < right) { //为什么使用while?因为我们要循环的操作不止一次
while(key < arr[right] && left < right) //当末尾数大于基准数时的情况
right--; //当最右边的数大于基准数时,末尾下标进行--操作,while的作用域只有这一行
arr[left] = arr[right]; //当最右边的数小于基准数时,要做的操作
while(key >= arr[left] && left < right) //当末尾数小于基准数时的情况,这里包含了相等的情况
left++; //当最右边的数小于基准数时,开始下标进行++操作,while的作用域只有这一行
arr[right] = arr[left]; //当最左边的数大于基准数时,要做的操作
}
arr[left] = key; //当所有下标都遍历完了以后,将基准数放在left下标位置
System.out.println(left +"***");
return left; //返回基准数的下标
}
}
请查看结果:
1***
6***
2***
3***
4***
8***
11***
10***
13***
16***
14***
4 4 5 7 8 9 9 11 12 24 32 32 44 55 65 76 77 78
5、非递归实现快速排序的方法,其实也是也是使用堆栈实现的,如果对堆栈不是很了解的博友,请看它:https://baike.baidu.com/item/%E5%A0%86%E6%A0%88/1682032?fr=aladdin
public class QuickSort2 {
public static void main(String[] args) {
int[] arr = {3,5,6,8,9,2,3,4,4,2,88,1,1,2,7,3,5,4,4,32};
int[] arr1 = GenerQuickSort(arr, 0, arr.length - 1);
for(int i = 0; i < arr1.length; i++) {
System.out.print(arr1[i] + " ");
}
}
public static int[] GenerQuickSort(int[] arr, int left, int right) {
//定义一个栈
Stack<Integer> stack = new Stack<>();
//前提条件就是left < right
if(left < right) {
stack.push(left); //将left压进栈中
stack.push(right); //将right压进栈中
while(!stack.isEmpty()) { //当栈不为空时,进行以后的操作
int height = stack.pop(); //取出栈中第一位,我最后压进的是right,所以它是最末位的下标;
int low = stack.pop(); //最底下的是left的下标。到底哪个对应那个,要看你是怎么压进去的
int index = getTargetIndex(arr, low, height); //得到基准数的下标
if(index-1 > low) { //判断基准数左边的数的下标与开始下标的大小
stack.push(low); //根据情况压进去以基准数分开的下标的左半边部分的低位
stack.push(index -1); //根据情况压进去以基准数分开的下标的左半边部分的高位
}
if(index+1 < height) { //判断基准数右边的数的下标与开始下标的大小
stack.push(index + 1); //根据情况压进去以基准数分开的下标的右半边部分的低位
stack.push(height); //根据情况压进去以基准数分开的下标的右半边部分的高位
}
}
}
return arr;
}
//这是寻求中间位置的方法,此处不再赘述
public static int getTargetIndex(int[] arr, int left, int right) {
if(left >= right || arr == null) {
return -1;
}
int key = arr[left];
while(left < right) {
while(key < arr[right] && left < right)
right--;
arr[left] = arr[right];
while(key >= arr[left] && left < right)
left++;
arr[right] = arr[left];
}
arr[left] = key;
System.out.println(left +"***");
return left;
}
}
结果:
7***
11***
16***
17***
19***
13***
15***
10***
9***
6***
5***
4***
3***
1***
1 1 2 2 2 3 3 3 4 4 4 4 5 5 6 7 8 9 32 88
简要的说一下堆栈排序的原理:
1、先将最左边与最右边的的下标压进去;
2、在进入while循环,再取出堆栈中仅有的两个数,得到基准数的下标;
3、对基准数两边的数进行继续压栈;
4、压栈时要注意顺序,因为堆栈是先进后出数据结构。将两边的下标压入之后,在寻找其中一边的基准数,再进行对这个基准数的压栈操作,之后,循环3、4步骤;
备注:
对于堆栈有一种保护现场的操作,具体的体现可以参考递归。保护现场,就是我没有执行完一次操作,因为这次操作依赖下次操作,我会放下这次的操作,操作系统会讲我这部分未完成的操作保护起来,等待我完成下次操作再回来完成这次操作,就像斐波那契数列,我们想知道当自变量等于10的时候,函数的值是多少,那么你肯定要先知道9,8,7,6,5,4,3,2,1,的值是多少,而我们最先知道的是1和2的值,再会知道n=3的值,其余都处于现场保护环节。
路过走过,请指点一二~
那就完了~