快排
var sortArray = function(nums) {
// 快排
return quickStart(nums,0,nums.length-1);
}
function quickStart(nums,left,right) {
if(left<right) {
let index = partition(nums,left,right);
quickStart(nums,0,index);
quickStart(nums,index+1,right);
}
return nums;
}
// 分区: 快慢指针
function partition(array,left,right) {
let pvit = left; // 选择最左侧作为基准值,可优化(最左侧可能一直是较小的数,以此作为基准值,导致递归树向一边倾斜)
let index = left+1;
for(let i=index;i<=right;i++) { // i 指向小于基准值的指针,其之前的元素均大于基准值。index 指向大于基准值的指针
if(array[i]<array[pvit]) {
swap(array,index,i);
index++;
}
}
swap(array,pvit,index-1);
return index-1;
}
// 交换数组中的元素
function swap(array,i,j) {
let temp = array[i];
array[i] = array[j];
array[j] = temp;
}
// 分区:挖坑
function partition2(array,left,right) {
let pivot = array[left]; // 左侧元素作为第一个坑,也是基准值
while(left<right) {
while(left<right&&array[right]>=pivot) { //在right移动过程中,>right 均大于基准值,<left 均小于基准值,=left 大于基准值
right--;
}
if(left<right) array[left] = array[right];
while(left<right&&array[left]<=pivot) { //在left 移动过程中,<left 均小于基准值,>right 均大于基准值,=right 小于基准值
left++;
}
if(left<right) array[right] = array[left];
}
array[left] = pivot; // 两元素相遇,相遇点也为坑位,此时坑位之后均大于基准值,坑位之前均小于基准值,坑位填入基准值即可
return left;
}
// 分区:双指针
function partition3(array,left,right) {
let pivot = array[left]; //基准值选在左侧
let low = left;
while(left<right) {
while(left<right&&array[right]>=pivot) { // 右指针先动,i、j相遇在分界线的左侧;左指针先动,i、j相遇在分界线的右侧
right--;
}
while(left<right&&array[left]<=pivot) {
left++;
}
if(left>=right) break;
swap(array,left,right); // i、j相遇,即数组完成了分区,只需要将基准值移至分界点即可
}
swap(array,low,left);
return left;
}
// 当数组为有序序列时,由于基准值选在左侧,导致递归树倾斜,排序算法退化成O(N^2)。优化:随机选择一个数作为基准值
// 优化:
function quickStart(nums,left,right) {
if(left<right) {
let pivot = Math.floor(Math.random()*(right-left+1))+left; //随机值作为基准值
swap(nums,left,pivot) // 将随机值换到最左侧即可
let index = partition(nums,left,right);
quickStart(nums,0,index);
quickStart(nums,index+1,right);
}
return nums;
}
1. 进一步优化: 当随机值每次都是最小值或最大值也会导致递归树倾斜,此时采用三数限制,可完全避免递归树过于倾斜。见leetcode官方网站。
2. 以上均是从小到大顺序排序,那么逆序该怎么修改代码?