唯心主义蠢货的[算法学习] 含有重复元素的快排

425 阅读3分钟

快排存在相同值时

常规快排

设定基准值key,将数组以key为中心,通过前后两个指针来调整数组元素的位置顺序,使得key左边的元素全部小于key,key右边的元素全部大于key,然后再对两边的元素分别进行快排

// 伪代码
quick_sort(int A[],int left,int right){
    if(left > right) return; 
    key = A[left] // 以左值为基准
    I = left, J = right; // I从左边出发,J从右边出发
    while(I < J){
        //右指针先出发
        while(J对应的元素大于key && I < J){
            J--
        }
        // 得到A[J] < key的元素的位置
        while(I对应的元素小于key && I < J){
            I++
        }
        // 得到A[I] > key的元素的位置
        swap(A[I],A[J]) // 交换I和J的位置
    }
    // 最后相遇的位置值肯定是小于key的
    // 1. 如果J遇到I,说明当前的I是上一步J换过来的元素,所以是小于key的
    // 2. 如果I遇到J,说明J找到了一个小于key的值,但是前边没有大于key的值了
    swap(A[I],A[Left]) // 交换
    quick_sort(A,left,I-1);
    quick_sort(A,I+1,right);
}

有重复元素的快排

由于存在重复元素,我们的分割就不能以key为中心分为两部分了,应该将数组分为三部分,小于key、等于key、大于key的部分,即我们的目的是数组分为三部分。 很自然的想到通过双指针的方式,来进行调整,最后左指针I和右指针J之间的元素即为等于key的部分,所以这样不能像常规快排一样调整大于key和小于key元素的位置,应该是如果存在小于key的元素,则I++,存在大于key的元素,则J--,每次调整一步,将和key相同的元素放在中间

// 代码
let test = [20,40,32,60,50,60,70,40];

let swap = function(arr,a,b){
    let temp = arr[a];
    arr[a] = arr[b];
    arr[b] = temp;
}
let partition = function(A,left,right){
    let M = left-1;
    let N = right;
    let key = A[right];
    for(let i = left; i < right; i++){
        if(i >= N){
            break;
        }
        while(A[i] > key && i < N){
            swap(A,--N,i);
        }
        if(A[i] < key){
            swap(A,++M,i);
        }
    }
    swap(A,right,N);
    M++;
    return { M , N };
}
let quickSort = function(A,left,right){
    if(left < right){
        const {M,N} = partition(A,left,right);    
        quickSort(A,left,N-1);
        quickSort(A,M+1,right);
    }
}
quickSort(test,0,test.length - 1);
console.log(test.length,test);

这里重点解释一下partition函数,我们要保证N指针之后的元素都是比key大的,M指针前都是比key小的,对于i对应的元素有以下三种情况

  1. 比key小,那么交换他和++M对应的指针的位置,++M 要么等于 i,要么对应的元素和key相等,所以交换这两个的位置,最后总能将和key相等的元素调整到后边
  2. 比key大,那么就交换他和后边对应--N的元素的位置,那为什么要用while循环呢,因为有可能换过来的这个元素也比key大,那么继续交换,直到换过来的这个元素小于等于key,或者--N已经到达当前i的位置了
  3. 如果等于key,这样就会使得当前的M,是指向当前序列中第一个等于key的位置的前一位

图例解释