1. 程式人生 > >無序陣列求中位數

無序陣列求中位數

長度為 n 的無序陣列,求中位數,如何儘快的估算出中位數,演算法複雜度是多少?

演算法 1(建立最小堆):

如果陣列中元素有奇數個,可以採用這種演算法:
步驟 1 :可以將陣列的前 (n+1)//2 個元素,建立 1 個最小堆;
步驟 2 :遍歷剩餘元素,如果剩餘元素小於堆頂元素,則丟棄或不作處理;如果剩餘元素大於堆頂元素,則將其取代堆頂元素,並將當前堆調整為最小堆。
步驟 3 :返回堆頂元素,即 nums[0],就是所要尋找的中位數。

一點解釋:
不管是步驟 1、2 還是整個過程中,最小堆的棧頂元素必然滿足:
中位數 >= 最小堆的堆頂元素
例如,[7,8,9,10,11,12,13] 中位數是 10 ,n 等於 7 ,(n+1)//2 等於 4 ,不管是取前 4 個數、後 4 個數、任意 4 個數,構造的最小堆的堆頂元素,最小為 7 ,最大為 10。
因此,小於堆頂元素的元素,必然不可能是中位數,可以直接丟棄;中位數只有可能在最小堆、剩餘元素中。

實現:

# coding:utf-8
#from heap_sort import filter_down


def filter_up(nums, p, n):
        parentIdx = p
        rootVal = nums[parentIdx]

        while 2*parentIdx+1 <= n-1:
                kidIdx = 2*parentIdx+1

                if kidIdx != n-1 and nums[kidIdx] > nums[kidIdx+1]:
                        kidIdx += 1

                if rootVal < nums[kidIdx]:
                        break
                else:
                        nums[parentIdx] = nums[kidIdx]

                parentIdx = kidIdx

        nums[parentIdx] = rootVal


def changeToMinHeap(nums, n):
        ''' 建立最小堆 '''
        for index in range(n//2-1, -1, -1):
                filter_up(nums, index, n)


def find_median(nums, n):

        assert n%2 == 1

        aboutHalf = (n+1)//2

        changeToMinHeap(nums, aboutHalf)

        pointer = aboutHalf

        for index in range(aboutHalf, n):
                if nums[index] > nums[0]:
                        nums[0] = nums[index]
                        changeToMinHeap(nums, aboutHalf)

        return nums[0]


def test():
        nums = list(range(4, 10)) + list(range(0, 4)) + list(range(10, 15))
        print('nums:', nums)

        assert find_median(nums, 15) == 7

        print('Pass!')


if __name__ == '__main__':
        test()

複雜度分析:

暫時略。。

參考文獻:

  1. 無序陣列的中位數
  2. 讓人眼前一亮的演算法!求無序陣列的中位數

演算法 2 (建立最大堆、最小堆)

時間複雜度

O(n)

參考文獻:

  1. 無序陣列中求中位數
  2. 【演算法】無序陣列中求中位數
  3. 試用O(n)來實現求出一個無序陣列的中位數

其他參考文獻:

  1. 求一個無序陣列的中位數
  2. 求中位數,快速選擇演算法