1 思想(王道p287)
快速排序是对冒泡排序的一种改进。其基本思想是基于分治法的。在待排序列中任选一个基准值,对待排序列进行一趟快速排序,便将整段序列分为两个部分,其中一部分的值都小于基准值,另一部分都大于基准值。然后分别递归对这两部分继续上述过程,直到每一部分只有一个元素或者空为止,即所有元素都排序好了。
2 重点
对待排序的表进行划分(进行一趟快速排序)。
其步骤是
1)从后往前找第一个比基准值小(相等也行)的元素,将比基准值小的元素移动到左端,即将right位置的值赋值给left位置;
2)从后往前找第一个比基准值大(相等也行)的元素,将比基准值大的元素移动到右端,即将left位置的值赋值给right位置;
3)重复第1)和2)步,直到left等于right。
3 空间复杂度(函数的上下标好像被吃了)
最佳情况: log2(n + 1)的向上取整
最差情况: O(n)
平均情况: O(log2n)
4时间复杂度(函数的上下标好像被吃了)
快速排序算法是所有内部排序算法中平均性能最好的排序算法
最佳情况:T(n) = O(nlog2n)(得到的两个子问题都不可能大于n/2)
最差情况:T(n) = O(n2)(基本有序或者逆序的时候)
平均情况:T(n) = O(nlog2n)
5 稳定性
不稳定排序算法
6 优化
1)递归过程中划分得到的子序列的规模较小的时候不再递归使用快速排序,而是使用直接插入排序。
2)选取一个可以将数据中分的基准元素
方法1:取头中尾三个元素的中间大那个为基准值
方法2:随机从序列里面选一个
7 参考
王道2017
8 代码
1)递归代码
基准值是第一个元素
/**
*快速排序算法,递归实现
* @param {number[]} arr
* @param {number} left
* @param {number} right
*/
let quickSort = (arr, left, right) =>{
if(left < right){//长度为1以下不做处理
let index = partition(arr,left, right); //对数组left, right之间值进行一躺快速排序,被返回排序后基准值的最终坐标
quickSort(arr, left, index - 1);//对左边进行递归
quickSort(arr,index + 1, right);//对右边进行递归
}
return arr;
}
/**
*实现功能:一趟快速排序(不是一次,是一趟)
* 何为一趟排序:先在待排序的列表中(这里是数组)中取出一个数作为基准数(这里是第一个数);
分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边
* 具体做法:
*
* @param {number[]} arr
* @param {number} left
* @param {number} right
* @return {number}
*/
var partition = (arr, left, right) => {
let base = arr[left]; //基准值,数组中的第一个数,也可以选择最后一个数,中间的也行。可以优化(第一个,中间,最后一个中中间的那个数)
while(left < right){ //循环跳出条件,注意不能等于
while( left < right && base <= arr[right] ){ //从后往前找第一个比基准值小(相等也行)的元素
/*这里记得left < right这个条件,因为内层循环之后可能出现left>=right现象,如果没有这个条件,那么它可能会执行这个循环。
* 记住,外层循环的条件只是管能进入外层循环,而不会管内层循环的*/
right--;
}
//这句不能和下面第四句组合互换两个数,因为这里的left还是变,才到第四句
arr[left] = arr[right]; //比基准值小的元素移动到左端
while(left < right && base >= arr[left]){ //从前往后找第一个比基准值大的元素 将记住这里的base有等于号
left++;
}
arr[right] = arr[left]; //比基准值大的元素移动到右端
} 跳出循环时left和right相等,此时的left和right就是base的正确索引位置
arr[left] = base; //最终left=right,基准元素的最终存放位置
return left; //返回基准元素的最终存放位置
}
let arr = [49, 38, 65, 97, 23, 22, 76, 1, 5, 8, 2, 0, -1, 22];
console.log(quickSort(arr, 0,arr.length -1))
2)递归代码
两个额外数组保存基准值左右两边序列,基准值取中间值
var quickSort = function (array) {
if(array.length <= 1){
return array;
}
let left_arr = [];
let right_arr = [];
let pivotIndex = Math.floor(array.length / 2); //取中间(大概中间)的为基准值
let pivot = array[pivotIndex]; //取中间(大概中间)的为基准值
array.splice(pivotIndex, 1); //将该值从数组中删除
for(let i = 0; i < array.length; i++){
if(pivot >= array[i]){
left_arr.push(array[i]); //小于基准值的放在这里
}else{
right_arr.push(array[i]); //大于基准值放在这里
}
}
return quickSort(left_arr).concat([pivot],quickSort(right_arr));
};
console.log(quickSort([3,44,38,5,47,15,36,26,27,2,46,4,19,50,48]));
3)非递归代码
借助栈(数组模拟栈)来实现
快速排序的思想就是分治法,第一趟将序列分成两部分,每一部分都可以看出一个小的序列,可以将小的序列最左最右指针下表入栈。
/**
*快速排序算法,非递归实现
* 主要思想:利用栈实现
* 过程:快速排序的思想就是分治法,第一趟将序列分成两部分,每一部分都可以看出一个小的序列,可以将小的序列最左最右指针下表入栈。
* @param {number[]} arr
* @param {number} left
* @param {number} right
*/
let quickSort = (arr, left, right) =>{
let stack = []; //js中用数组模拟栈
stack.push(left); //左指针入栈
stack.push(right); //右指针入栈
while(stack.length > 0){ //栈不为空时,说明还有序列没有排序好
let right = stack.pop();//后进先出,栈顶元素出栈,是为待排序列的最右下标(指针)
let left = stack.pop(); //栈顶元素出栈,是为待排序列的最左下标(指针)
let index = partition(arr, left, right); //划分,将待排序列进行一趟快速排序,最终有一个数获得最终位置,其下标为index
if(left < index - 1){ //将index将待排序列分为两部分
stack.push(left); //左边那部分左指针入栈
stack.push(index - 1);//左边那部分右指针入栈
}
if(right > index + 1){ //右边部分入栈
stack.push(index + 1);
stack.push(right);
}
}
return arr; //返回数组
}
/**
*实现功能:一趟快速排序(不是一次,是一趟)
* 何为一趟排序:先在待排序的列表中(这里是数组)中取出一个数作为基准数(这里是第一个数);
分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边
* 具体做法:
*
* @param {number[]} arr
* @param {number} left
* @param {number} right
* @return {number}
*/
var partition = (arr, left, right) => {
let base = arr[left]; //基准值,数组中的第一个数,也可以选择最后一个数,中间的也行。可以优化(第一个,中间,最后一个中中间的那个数)
while(left < right){ //循环跳出条件,注意不能等于
while( left < right && base <= arr[right] ){ //从后往前找第一个比基准值小(相等也行)的元素
/*这里记得left < right这个条件,因为内层循环之后可能出现left>=right现象,如果没有这个条件,那么它可能会执行这个循环。
* 记住,外层循环的条件只是管能进入外层循环,而不会管内层循环的*/
right--;
}
//这句不能和下面第四句组合互换两个数,因为这里的left还是变,才到第四句
arr[left] = arr[right]; //比基准值小的元素移动到左端
while(left < right && base >= arr[left]){ //从前往后找第一个比基准值大的元素 将记住这里的base有等于号
left++;
}
arr[right] = arr[left]; //比基准值大的元素移动到右端
} 跳出循环时left和right相等,此时的left和right就是base的正确索引位置
arr[left] = base; //最终left=right,基准元素的最终存放位置
return left; //返回基准元素的最终存放位置
}
let arr = [49, 38, 65, 97, 23, 22, 76, 1, 5, 8, 2, 0, -1, 22];
console.log(quickSort(arr, 0,arr.length -1))
百里于2020年5月22日
如果有错,请您指出!如有侵权,请联系我删除!