快速排序是基于分治策略的。对一个子数组A[p…r]快速排序的分治过程的三个步骤为:
分解:
数组A[p…r]被划分成两个(可能空)子数组A[p…q-1]和A[q+1…r],使得A[p…q-1]中的每个元素都小于等于A[q],且小于等于A[q+1…r]中的元素。下标q也在这个划分过程中进行计算。
解决:
通过递归调用快速排序,对子数组A[p…q-1]和A[q+1…r]排序。
合并:
因为两个子数组就是原地排序的,将它们的合并不需要操作:整个数组A[p…r]已排序。
伪代码
算法的关键是求q的partition过程,它实现子数组A[p,..,r]的原地址重排
伪代码
算法选取数组的右边界A[r]作为分裂元素
讲解
可以看出p-i部分的数都小于等于x,i+1--j部分的数都大于j
在(b)情况:i++,交换后A[i]的值小于等于x,而原始A[i]的值本身就是大于x的交换到j的位置当然也是大于x的
最后当j==r时候,i++后也需要进行交换,虽然A[i]大于x,当时A[r]的值是等于x。快排是:左边的是小于等于x,右边的大于x
partition函数
public int partition(int [] A,int low ,int high){ int x = A[high]; int i = low - 1; for( int j = low;j<= high - 1;j++){ if( A[j] <= x){ i = i + 1; swap(A,i,j); } } i = i + 1; swap(A,i,high); return i; } public void swap(int[] array,int i,int j){ int tmp = array[i]; array[i] = array[j]; array[j] = tmp; }
求划分id的partition函数的另外一种写法
核心思想:找到左侧较大的数a,找到右侧较大的数b,a、b交换,在下一次寻找,两个指针相遇时候结束
public int Paratition(int[] A,int left ,int right){ if(left> right) return -1; int i = left; int j = right; int mid = A[left]; if(i<j){ while(i<j){ while(i<j && mid< A[j]) j--; if(i<j){ A[i] = A[j]; i++; } while(i<j && A[i]< mid) i++; if(i<j){ A[j] = A[i]; j--; } } A[i] = mid; } return i; }
快排函数
public void quickSort(int[] A,int left,int right){ if(left>right) return; int id = partition(A,left,right); quickSort(A,left,id-1); quickSort(A,id+1,right); }
时间复杂度
最坏情况:O(N^2)
平均情况:O(NlogN)