排序算法

快速排序:顾名思义就是快,c语言底层实现的排序算法主要就是用的快速排序。
快速排序,最好时间复杂度是nlogn,最坏是n^2,一般时间复杂度是nlogn。

    func quickSort(_ array: inout Array, _ left: Int, _ right: Int) -> Void{
        if left < right {
            let point = partition(&array, left, right)
            quickSort(&array, left, point - 1)
            quickSort(&array , point + 1, right)
        }
    }
 
    func partition(_ array: inout Array, _ left: Int, _ right: Int) -> Int {
        let pivot = array[right] // 选最后一个数作为基准值
        var i = left // 暂时初始化想选的中间值为最左值,动态改变更适合的中间值位置
        for j in left ..< right { // 从最左边遍历数组,除了最后一个已经挑出来的数
            if array[j] < pivot {// 把遍历得到的数和最后一个数进行比较,如果它比最后数小,则说明需要它和暂时选择的中间值进行交换,
                array.swapAt(j, i)
                i += 1// 这里加1,是需要理解i值的作用,它是暂时的中间值下标,也就是说它应该起到分割数据的作用,左边的数都是小于基准值的,右边是待比较的数据,通过上面一步的交换后,说明旧的i下标现在存储的值是小于基准值的,如果不加1,那就起不到分割数据的作用,所以加1后,相对于新i下标,左边的数又是小于基准值的。
            }
        }
        array.swapAt(right, i)
        return i
    }

原地分区函数 不占有额外空间,空间复杂度为O(1),经过最后处理后,得到的是中间值的和中间值位置,而它的左边的数肯定都是比它小的数,当然也要知道左边的数不一定是排好序的,而它的右边肯定都是比它大或者等于的数,右边的数也不一定都排好序。举个例子好理解。
待排序数据是:3 4 2 1 5 3 。
跑完一次分区函数结果为 2 1 3 4 5 3。
其中i值,此时是2,是下标 ,而对应的值是3。
遍历第1轮(i , j  = 0):  3[a] (i,j) 4 2 1 5 3(b) 由于3[a]和3(b)一样,所以不交换,
遍历第2轮(i = 0 , j  = 1): 3[a] (i) 4(j) 2 1 5 3(b) 由于4大于3(b),所以不交换
遍历第3轮(i = 0 , j  = 2): 3[a] (i) 4 2(j) 1 5 3(b) 由于2小于3(b),所以交换i ,j 值,同时i + 1
遍历第4轮(i = 0 , j  = 3): 2 4(i) 3[a] 1(j) 5 3(b) 由于1小于3(b),所以交换i ,j 值,同时i + 1
遍历第5轮(i = 0 , j  = 4): 2  1 3[a](i) 4 5(j) 3(b) 由于5大于3(b),所以不交换
遍历结束,最后把最后没有参与比较的基准数和最后取得的i下标的值进行交换,因为此时i值就是给基准数的,相当于经过交换才真正归位到正确的位置i
从上面对于相同数据3的排序,没跑函数前相对位置是3[a]    3[b],而跑完函数就变成了 3[b]    3[a],说明快速排序不是稳定的排序。

结论:参考稳定算法的定义,如果相同的数,排序前后发生了交换,那就不是稳定的排序算法,而从上面的分析就可以知道快速排序是不稳定的。
还有可以发现快速排序的分区函数并没有使用到临时数组,所以它的分区函数空间复杂度是O(1),但由于快速函数外面是通过递归调用的,所以还需要用到递归栈空间,所以经过复杂的计算得到平均整体时间复杂度为O(logn),最坏是O(logn),最好是O(logn)。

快速排序常见的排序题:
查找无序数组中,第k大的数,也就是对应的下标应该是k-1

    func quickSortSelectK(_ array: inout Array, _ left: Int, _ right: Int, _ k: Int) -> Int {
        if left == right {
            return array[left]
        }
        let point = partition(&array, left, right) // 求得的是中间值的下标
        let realIndex = k - 1 // 实际要求的下标
        if point == realIndex {
            return array[point]
        }else if (point > realIndex) {// point的下标比realIndex大,说明真正要找的数在point的左边,需要再对左边区间数据做一次快速排序
           return quickSortSelectK(&array, left, point - 1,  k) // 注意不包括point
        }else  {// point的下标比realIndex小,说明真正要找的数在point的右边,所以需要再对右边区间数据做一次快速排序
           return quickSortSelectK(&array,  point + 1, right, k)// 注意不包括point
        }
    }

你可能感兴趣的:(排序算法)