快速排序跟冒泡排序都属于交换排序,但不同于冒泡排序的是冒泡排序每一轮都只排出一个元素,而快速排序则在每一轮选定一个基准元素,然后将比它大的移到一边,小的移到另一边,拆解成两部分,这种方法叫做分治法。这样做的好处就是大大降低的时间复杂度,冒泡排序时间复杂度是O(n^2),而快速排序的平均时间复杂度是O(nlogn)。
但是这种简单的快速排序在遇到8,7,6,5,4,3这种本来逆序列的情况下就会出现时间复杂度为O(n^2),你可以不取第一个值8为基准元素,但是这存在概率问题,有很小的机率会取到想要的中间值,所以最坏的情况会出现时间复杂度为O(n^2),平均时间复杂度是O(nlogn),怎么把小于和大于povit的值分到两部分呢,下面有两种方法可以选择:
- 填坑法
首先选定一个元素为基准元素pivot,然后记住位置Index,这个位置相当于一个坑位,然后设置两个指针left和right,指向最左位置和最右位置。然后从right开始进行比较,如果比pivot大,则指针左移一下,若小于pivot则将right与index位置的povit交换,然后停止进行left指针比较,若值比povit小则left右移一下,若比povit大则将left与index位置的povit交换然后开始切换到right指针。最后left与right和index指针重合的时候将povit的值填入坑内。
package QuickSort;
public class QuickSort1 {
public static void quicksort1(int[] arr,int startIndex,int endIndex){
//递归结束条件:startIndex大于或者等于endIndex
if(startIndex>=endIndex){
return;
}
//得到基准元素位置
int pivotIndex=partition(arr, startIndex, endIndex);
//根据基准元素来分成两部分来递归进行排序
quicksort1(arr,startIndex,pivotIndex-1);
quicksort1(arr,pivotIndex+1,endIndex);
}
private static int partition(int[] arr, int startIndex, int endIndex) {
//取第一个元素为基准元素
int pivot=arr[startIndex];
int left=startIndex;
int right=endIndex;
//填充元素的坑的位置。初始位置是pivot,也就是初始位置
int index=startIndex;
//当left指针和right指针重合循环结束
while (right>=left){
while (right>=left){
if(arr[right]<pivot){
arr[left]=arr[right];
index=right;
left++;
break;
}
right--;
}
while (right>=left){
if(arr[left]>pivot){
arr[right]=arr[left];
index=left;
right--;
break;
}
left++;
}
}
arr[index]=pivot;
return index;
}
public static void main(String[] args) {
int[] arr=new int[]{5,3,2,6,4,8,7,1,0};
quicksort1(arr,0,arr.length-1);
for (int i = 0; i <arr.length; i++) {
System.out.print(arr[i]+",");
}
}
}
- 指针交换法
选定元素povit,然后设置left和right指针指向数列的最左和最右,从right开始比较,当right值比povit大时,指针左移一下,当比povit小时right停止移动转向left指针,当left指针的值比povit小或相等时left指针右移一下,当比povit大时,交换此时的left与right的值,然后继续进行right的比较。当left和right相等时,把povit的值与left和right的值交换。
package QuickSort;
public class QuickSort2 {
public static void quicksort2(int[] arr,int startIndex,int endIndex){
//递归结束条件:startIndex大于或者等于endIndex
if(startIndex>=endIndex){
return;
}
//得到基准元素位置
int pivotIndex=partition(arr, startIndex, endIndex);
quicksort2(arr,startIndex,pivotIndex-1);
quicksort2(arr,pivotIndex+1,endIndex);
}
private static int partition(int[] arrs, int startIndex, int endIndex) {
//取第一个元素为基准元素
int pivot=arrs[startIndex];
int left=startIndex;
int right=endIndex;
//当left指针和right指针重合循环结束
while (right!=left){
while (left<right&&arrs[right]>pivot){
right--;
}
while (left<right&&arrs[left]<=pivot){
left++;
}
if (left<right){
int p=arrs[left];
arrs[left]=arrs[right];
arrs[right]=p;
}
}
//left和right重合后,基准元素和重合点交换
int p=arrs[left];
arrs[left]=arrs[startIndex];
arrs[startIndex]=p;
//返回基准元素的位置
return left;
}
public static void main(String[] args) {
int[] arr=new int[]{1,3,2,6,4,8,7,0};
quicksort2(arr,0,arr.length-1);
for (int i = 0; i <arr.length; i++) {
System.out.print(arr[i]+",");
}
}
}
这里采用递归的方法实现分治的思想,当然也可以用栈的方式来代替,方法一层一层的调用本身就相当于一个函数栈,可以把其放入栈内操作一样的道理。