快速排序算法与三路快排
快速排序(Quicksort)是对冒泡排序的一种改进。
它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
1.普通快排
快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
一趟快排算法步骤
设要排序的数组arr有N个元素。
1.设置两个变量left、right,排序开始的时候:left=0,right=N-1。
2.以第一个数组元素作为关键数据(基准元素pivot),赋值给pivot,即pivot=A[0];(优化时选择合适的基准元素)。
3.从rightj开始向前搜索,即由后开始向前搜索(right–),找到第一个小于pivot的值A[right],将A[right]和A[left]的值交换。
4.从left开始向后搜索,即由前开始向后搜索(left++),找到第一个大于pivot的A[left],将A[left]和A[right]的值交换。
5.重复第3、4步,直到left=right; (3,4步中,没找到符合条件的值,即3中A[right]不小于pivot,4中A[left]不大于pivot的时候改变right、left的值,使得right=right-1,left=left+1,直至找到为止。找到符合条件的值,进行交换的时候left,right指针位置不变。另外,left==right这一过程一定正好是left++或right–完成的时候,此时令循环结束)。
javascript代码实现如下:
function swap(array,left,right){
let temp = array[right];
array[right] = array[left];
array[left] = temp;
}
// 每次选择最左边的数作为基数
function quickSort(arr){
//递归出口条件
if (arr.length<2) { return arr; }
// 1.定义左右指针
var left=0;
var right=arr.length-1;
// 2.找基准值
var pivot = arr[0];
//开启每一轮的排序
while(left<right){
// 3.不断递减变量right的值,直到找到右边比arr[0]小的数的下标,找到后二者交换位置
while(arr[right]>=pivot && left<right){
right--;
}
// 4.不断递增left变量的值,直到找到左边比arr[0]大的数的下标,找到后二者交换位置
while(arr[left]<=pivot && left<right){
left++;
}
//5.当左边指针与右边指针相遇后,交换arr[0]与当前两个指针所在的元素
if (right==left) {
swap(arr,right,0)
break;
}
//符合上面条件后 交换两个指针当前位置的元素
swap(arr,right,left)
}
//多次分解数组,直到数组不能再分解为止(只有一个数据),才能得到正确结果
//递归实现
var leftarr=quickSort(arr.slice(0,left))//每次抽取指针左侧元素做quickSort,left值在变化
var rightarr=leftarr.concat(arr.slice(left,right+1))//将指针所指元素连接到左侧数组中
var newarr = rightarr.concat(quickSort(arr.slice(right+1)))//先把right+1到末尾的数组元素做quickSort,然后连接到上面数组后面
return newarr
}
//对数组进行排序
console.log(quickSort([5,8,4,9,3,4,7]));//[3, 4, 4, 5, 7, 8, 9]
涉及的API方法介绍:
方法名 | 用途 | 语法 |
---|---|---|
slice() | 提取字符串的某个部分,并以新的字符串返回被提取的部分 | stringObject.slice(start,end) |
concat() | 用于连接两个或多个数组,该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。 | arrayObject.concat(arrayX,arrayX,…,arrayX) |
参数说明:
2.三路快排
定义:
三路快速排序是快速排序的的一个优化版本, 将数组分成三段, 即小于基准元素、 等于基准元素和大于基准元素这三组, 这样可以比较高效的处理数组中存在相同元素的情况,其它特征与快速排序基本相同。
条件:
当大量数据,且重复数多时,用三路快排。
步骤:
1.新建三个数组变量left、center、right,分别存储小于、等于、大于基准元素的元素。
2.选取基准值pivot (可随机选取)。
3.遍历数组元素,小于基准值的存到left数组,等于基准值的存到center数组,大于基准值的存到right数组。
4.递归调用。反复对left数组和right数组执行步骤1、2、3,直到满足递归出口条件,即left和right数组各自排好序。使用ES6的扩展运算符...
展开left、center、right数组,返回排序后的数组。
代码
function quickSort_3(arr1){
// 递归出口
if (arr1.length == 0) {
return [];
}
//1.新建数组变量left、center、right
var left = [];
var center = [];
var right = [];
// 2.选基准值
var pivot = arr1[0];//可随机选
// 3.遍历数组元素,小于基准值的存到left数组,等于基准值的存到center数组,大于基准值的存到right数组。
for (var i=0;i<arr1.length;i++){
if (arr1[i]<pivot) {
left.push(arr1[i])
} else if (arr1[i]==pivot){
center.push(arr1[i])
} else{
right.push(arr1[i])
}
}
// 4.递归调用
return [...quickSort_3(left),...center,...quickSort_3(right)]
}
console.log(quickSort_3([5,8,4,3,7,1,4]))//[1, 3, 4, 4, 5, 7, 8]
3. 快排应用
例如LeetCode第75题,颜色分类。
题目:
/*
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
注意:
不能使用代码库中的排序函数来解决这道题。
示例:
输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
进阶:
一个直观的解决方案是使用计数排序的两趟扫描算法。
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。
你能想出一个仅使用常数空间的一趟扫描算法吗?
*/
此题重复元素较多,就可以使用三路快排的思想。
上述代码,输入数组[2,0,2,1,1,0],输出如下: