快速排序
一、思路:
- 从待排的数组中选出来一个基准数(key)
- 遍历整个数组,将比key小的放在左边,比key大的放在右边
- 分别对左右两个小区间用相同的方式进行处理
- 直到小区间的size=1或0(小区间有序或没有数据)
二、拿一个数组进行快速排序:
三、代码实现:
大思路:
public static void quicksort(long[] array) {
//一个内部的快速排序
quicksortInternal(array,0,array.length-1);
}
public static void quicksortInternal(long[] array,int lowIndex,int highIndex) {
int size = highIndex - lowIndex + 1;
if(size <= 1) {
return;
}
//1.找到一个基准数(最左边的数)
long key = array[lowIndex];
//2.执行partition的过程,将小的放的前面大的放到后面
int keyIndex = partition(array, lowIndex, highIndex);//返回partition之后下一次key位置的下标
//分别对左右区间进行相同的处理
quicksortInternal(array, lowIndex, keyIndex - 1);
quicksortInternal(array, keyIndex + 1, highIndex);
}
遍历数组,将数组分为两个小区间(小的放左,大的放右)
Hoare法:
要遍历的区间[lowIndex ,rightIndex]
- 选最左边的数为key
- 从右边开始走:
- rightIndex --;直到遇到小于 key 的数停下来
- leftIndex++; 直到遇到大于 key 的数停下来
- 交换leftIndex 和rightIndex 位置的数
- 交换lowIndex 和leftIndex位置的数(此时letIndex和rightIndex 指向的是同一位置)
- 返回leftIndex
private static int partitionHoare(long[] array, int lowIndex, int highIndex) {
int leftIndex = lowIndex;
int rightIndex =highIndex;
//选择最左边的数
long key = array[lowIndex];
//选择了左边的数,从右边开始走
while (leftIndex < rightIndex){
//由于leftIndex 和 rightIndex 在不同的变化,所以要在里面的循环保证leftIndex 小于rightIndex
while (leftIndex < rightIndex && array[rightIndex]>=key){
rightIndex--;
}
//说明遇到了小于key的数
while (leftIndex < rightIndex && array[leftIndex] <= key) {
leftIndex++;
}
//说明遇到了大于key的数
swap(array,rightIndex,leftIndex);//交换两个数
}
swap(array,lowIndex,leftIndex);
return leftIndex;
}
private static void swap(long[] array, int rightIndex, int leftIndex) {
long t = array[rightIndex];
array[rightIndex] = array[leftIndex];
array[leftIndex] = t;
}
挖坑法:
实质上和Hoare方法是一致的,只是将交换的过程变成了挖坑、填坑的过程。
private static int partition(long[] array,int lowIndex,int highIndex){
int leftIndex = lowIndex;
int rightIndex =highIndex;
long key = array[lowIndex];//挖坑
while (leftIndex < rightIndex) {
while (leftIndex < rightIndex && array[rightIndex] >= key) {
rightIndex--;
}
array[leftIndex] = array[rightIndex];
while (leftIndex < rightIndex && array[leftIndex] <= key){
leftIndex++;
}
array[leftIndex] = array[rightIndex];
}
//填坑
array[leftIndex] = key;
return leftIndex;
}
前后遍历法
private static int parition前后(long[] array,int lowIndex,int highIndex){
int separateIndex = lowIndex +1; //用来分隔,当出现小于lowIndex位置的数时,交换,separateIndex往后走
for(int i = lowIndex+1;i<=highIndex;i++) {//用来遍历数组
if(array[i] < array[lowIndex]) {
swap(array,i,separateIndex);
separateIndex++;
}
}
swap(array,lowIndex,separateIndex-1);
return separateIndex-1;
}
四、性能分析:
时间复杂度 | 空间复杂度 | ||||
最好 | 平均 | 最坏 | 最好 | 平均 | 最坏 |
O(n*log(n)) | O(n*log(n)) | O(n^2) | O(log(n)) | O(log(n)) | O(n) |
时间复杂度:每做一层数组的partition的时间复杂度为O(n),一共有多少层看执行partition的次数,最好为O(log(n)),最坏为O(n)(二叉树的高度)
稳定性:不具备稳定性,在进行partition的过程中不能保证相等的数保持原来的顺序。
五、优化:
1.在选择基准数上进行优化(左边或右边,随机选取,几个数取中间值)
2.当区间内的个数低于某个阈值时(16)时,使用插入排序更快
3.把相等的值进行特殊处理