Swift 获取无序的整数序列的中位数(堆 + 归并)

346 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情 。如果哪里写的不对,请大家评论批评。

题目

有一个无序的整数序列,获取序列的中位数,要求算法的时间复杂度小于O(n2),不能使用内置的排序函数。

输入:一个无序的数组

输出:中位数

分析

数组是无序的,获取中位数肯定需要排序,然而时间复杂度要小于O(n2),那只能使用归并排序或者堆排序了,

20200208192504226.jpg

代码(堆排序)

func adjustHeap(_ array : inout Array<Int>, _ i : Int, _ length : Int){
    // 当前节点,暂定最大数据
    var lar = i
    // 左孩子节点
    let left = i * 2 + 1
    // 右孩子节点
    let right = i * 2 + 2
    
    // 如果左孩子节点存在 && 数据大于当前最大数据 则记录下标数据最大
    if left < length && array[left] > array[lar] {
        lar = left
    }
    // 如果右孩子节点存在 && 数据大于当前最大数据 则记录下标数据最大
    if right < length && array[lar] < array[right] {
        lar = right
    }
    
    // 如果最大数据坐标有变化, 则去替换数据
    if lar != i {
        array.swapAt(lar, i)
        // 替换数据之后,如果当前下标也是父节点,需要继续和子节点比较
        adjustHeap(&array, lar, length)
    }
}

func heapSort(_ array :inout Array<Int>){
    // 建堆
    for i in (0...((array.count - 1) / 2)).reversed() {
        adjustHeap(&array, i, array.count)
    }
    // 排序
    for j in (1...(array.count-1)).reversed() {
        //将堆顶元素与末尾元素进行交换
        array.swapAt(0, j)
        //重新对堆进行调整
        adjustHeap(&array, 0, j)
    }
}

func findMedian(_ list :inout Array<Int>) -> Double {
    heapSort(&list)
    print(list)
    if list.count % 2 == 1 {
        return Double(list[list.count/2+1])
    }else{
        return Double(list[list.count/2] + list[list.count/2-1]) / 2.0
    }
}

var list : Array<Int> = [2, 3, 8, 1, 4, 9, 10, 7, 16, 14,111,231,51412,3125,12,13]
print(findMedian(&list))

代码(归并)

func mergeSort(_ array : [Int]) -> [Int] {
    guard array.count > 1 else {
        return array
    }
    // 拆分成独立的数组
    var tempList:[[Int]] = []
    for itme in array {
        var sublist:[Int] = []
        sublist.append(itme)
        tempList.append(sublist)
    }
    // 合并数组 两两数组合并
    while tempList.count != 1 {
        var i = 0
        while i < tempList.count - 1 {
            tempList[i] = merge(tempList[i], rList: tempList[i + 1])
            tempList.remove(at: i + 1)
            i += 1
        }
        print(tempList)
    }
    return tempList.first!
}

// 数组合并
func merge(_ lList:[Int] ,rList :[Int]) -> [Int] {
    // 标记位
    var leftIndex = 0
    var rightIndex = 0
    
    // 创建临时数组存储数据
    var tempList:[Int] = []
    
    // 对比两个有序数组,依次获取最小的值
    while leftIndex < lList.count && rightIndex < rList.count {
        
        // 左边第一个 和 右边第一个比较,假设左边小,存放临时数组,左边下标+1
        if lList[leftIndex] < rList[rightIndex] {
            tempList.append(lList[leftIndex])
            leftIndex += 1
        }else{
            // 如果相等或者右边小,取右边的值存放临时数据,右边下标+1
            tempList.append(rList[rightIndex])
            rightIndex  += 1
        }
    }
    
    // 最后肯定有一个数组剩下至少一位数据,全部最后在数组最后
    while leftIndex < lList.count {
        tempList.append(lList[leftIndex])
        leftIndex += 1
    }
    
    while rightIndex < rList.count {
        tempList.append(rList[rightIndex])
        rightIndex  += 1
    }
    // 返回当前排序结果
    return tempList
}

func findMedian(_ list : Array<Int>) -> Double {
    let array = mergeSort(list)
    print(array)
    if array.count % 2 == 1 {
        return Double(array[array.count/2+1])
    }else{
        return Double(array[array.count/2] + array[array.count/2-1]) / 2.0
    }
}

var list : Array<Int> = [2, 3, 8, 1, 4, 9, 10, 7, 16, 14,111,231,51412,3125,12,13]
print(findMedian(list))

关于堆排序不熟悉的朋友们请等待我明天得文章,堆排序的内容有点多