快速排序的实现

97 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天

思想

快速排序选中待排序数据其中一个元素为基准值,通过一趟排序将全部数据分为两部分,一部分比基准值大,一部分比基准值小,分别放在右边与左边,中间以基准值作为分界点,再对两边数据分别再使用上述方法,取新基准值,再次进行划分,直到分组内只有两个元素时进行最后一次比较排序,至此全部数据变得有序。

举例:

我们对数组:[7, 2, 9, 3, 6, 5, 10, 2]进行排序

第一步,取基准值:暂不考虑优化,我们取第一个元素2为基准值,将7另外保存为key

第二步,开始遍历,我们定义L、R两个指针,分别指向第一个元素和最后一个元素,从头尾向中进行遍历,我们先移动判断R,当R遇到比基准值小的数字,就令arr[L] = arr[R],覆盖掉L所指向的元素 (因为我们取第一个元素为基准值,又把基准值单独保存,所以可直接大胆覆盖) ,现在数组就变为了:[2, 2, 9, 3, 6, 5, 10, 2](加粗为覆盖内容)

第三步:R找到了一个小于基准值的数字,将其覆盖给了L,所以现在L开始工作,向右遍历寻找大于基准值的元素,这里找到了9,还是一样的操作,用9将R指向的元素覆盖,至此L的一次工作完成,数组变为了:[2, 2, 9, 3, 6, 5, 10, 9](加粗为覆盖内容)

第四步:轮班到了R,指向5时发现它比基准值7小,于是交给L,覆盖后:[2, 2, 5, 3, 6, 5, 10, 9]

第五步:L干活,但一直到L遇到R为止,都没有比基准值7大的元素,所以一次遍历结束,我们把基准值再请回来,交给L和R共同指向的位置:[2, 2, 5, 3, 6, 7, 10, 9],至此,以7为界,7前均小于它,7后均大于它,我们就把数组分为了两部分:[2, 2, 5, 3, 6]和[10, 9]

第六步,对我们分出来的两个子数组再分别执行上述的步骤,反复进行,直到不可再分为止,我们就通过快速排序得到了一个有序数组

实现(JavaScript)
function quickSort(arr, begin, end){
    if(begin < end){
        var left = begin;
        var right = end;
        var key = arr[left];//保存基准值
        //完成一次遍历的条件:当L遇上R
        while(left < right){
            //这里不要忘记再次进行判断L与R的位置,防止越界
            while(arr[right] >= key && left < right){
                right--;
            }
            //R找到小于基准值的部分,赋值给L
            arr[left] = arr[right];
            //这里不要忘记再次进行判断L与R的位置,防止越界
            while(arr[left] < key && left < right){
                left++;
            }
            //L找到大于基准值的部分,赋值给R
            arr[right] = arr[left];
        }
        //一趟遍历结束,请回基准值
        arr[left] = key;
        //对左侧部分进行排序
        quickSort(arr, begin, left - 1);
        //对右侧部分进行排序
        //切记这里end要传入end值,而不是整条数组的长度-1,因为在递归调用时,左侧数组再分出子数组,也会调用这个方法,这时候的end就不是整条数组的长度-1了,而是上一次调用传入的left-1
        quickSort(arr, right + 1, end);
    }else{
        return
    }
        
}
时空复杂度

最好时间复杂度为O(nlog2n),最坏时间复杂度为O(n^2),平均时间复杂度为O(nlog2n)

空间复杂度主要是由于递归造成栈空间的使用而造成的,平均情况为O(logn)