快速排序

思想:

1.填坑和划分

以某个数为基准,一般选第一个数,从数组头尾两边开始遍历。

选出来的基准数会用一个变量保存,这样的话,这个基准数所在的数组的位置就相当于有个坑。

先从尾那边开始,找到比基准数小的,就放到左边的坑。放了之后左边的坑就填上了,右边就出现坑了。

然后又从头那边开始,找到比基准数大的,就放到右边的坑。这样子左边又出现了坑。

直到头尾相遇的时候,把基准数放到此时的坑的位置。

这样子数组就划分成了前后两部分。

2.递归

对前面一部分快排。

对后面一部分快排。

 

图解:

 

 

代码:

 

void quickSort(vector<int>& arr, int low, int high){
    if(low < high){
        int i = low;
        int j = high;

        //三值取中 一般做法的话没有这一步
        int mid = low + (high-low)/2;
        if(arr[low] < arr[mid]){
            arr[low] += arr[mid];
            arr[mid] = arr[low]-arr[mid];
            arr[low] -= arr[mid];
        }
        if(arr[low] > arr[high]){ //如果此时low比high还大, 那low就是最大的, 并且此时不能将low和high交换, 因为我们不知道high和mid哪个是中值
            if(arr[mid] > arr[high]){//如果mid比high大, 那mid就是中值
                arr[low] += arr[mid];
                arr[mid] = arr[low] - arr[mid];
                arr[low] -= arr[mid];
            }
            else{//否则high是中值
                arr[low] += arr[high];
                arr[high] = arr[low] - arr[high];
                arr[low] -= arr[high];
            }
        }
        //三值取中 一般做法的话没有这一步

        int x = arr[i];
        
        while(i < j){
            while(i < j && arr[j] >= x) j--;
            if(i < j) arr[i++] = arr[j];
            while(i < j && arr[i] < x) i++;
            if(i < j) arr[j--] = arr[i];
        }
        arr[i] = x;
        quickSort(arr,low,i-1);
        quickSort(arr,i+1,high);
    }
}

 

 

 

 

 

 

复杂度分析:

在一次划分中,因为从low和high开始,直到两边重合。所以对所有元素都遍历了一遍,时间复杂度为O(n)。

而在整个算法中,时间复杂度还与划分的趟数有关。

如果每次都恰好选到中间的数,则每次划分都能划成两个等长的子数组。此时时间复杂度为O(nlog2n)。

而如果每次都恰好选到子数组最大或最小的数,则有一个子列表时空的,那么就要产生n个子列表。所以最坏的情况下时间复杂度为O(n2)。

为了改善性能,避免出现最坏情况,可以在选取中间数的时候,对最左边,最右边以及中间的数进行比较,选择这三者中的中间数。

对于空间复杂度也一样,最好的情况下,需要所需栈的深度为O(nlog2(n+1)),最坏情况是O(n)。

 

posted @ 2020-03-17 13:57  jenningszheng  阅读(177)  评论(0编辑  收藏  举报