【題目】
給定乙個無序的整型陣列arr,找到其中最小的k個數。
【要求】
如果陣列arr的長度為n,排序之後自然可以得到最小的k個數,此時時間複雜度與排序演算法的時間複雜度相同,為o(nlogn)。本題要求讀者實現時間複雜度為o(nlogk)和o(n)的方法。
【基本思路】
o(nlogk)的方法。思路很簡單,就是一直維護乙個大小為k的大根堆,這個堆表示目前選出的k個最小的數。接下來遍歷整個陣列,遍歷過程中看當前數是否比堆頂元素小,如果是的話,將堆頂元素替換成當前的數,然後從堆頂向下調整堆;否則,不進行任何操作。遍歷結束後,堆中的k個數就是答案。
下面是使用python3.5實現的**:
def
getminknumsbyheap
(arr, k):
defheapinsert
(heap, value, i):
heap[i] = value
parent = (i-1) // 2
while parent >= 0:
if arr[parent] < value:
arr[i] = arr[parent]
i = parent
parent = (parent-1) // 2
else:
break
arr[i] = value
defheapify
(heap, index):
n = len(heap)
child = 2 * index + 1
tmp = heap[index]
while child < n:
if child < n-1
and heap[child] < heap[child+1]:
child += 1
if heap[child] > tmp:
heap[index] = heap[child]
index = child
child = 2 * child + 1
else:
break
heap[index] = tmp
if arr == none
or len(arr) == 0
or k < 1
or k > len(arr):
return arr
heap = [0
for i in range(k)]
for i in range(k):
heapinsert(heap, arr[i], i)
for i in range(k, len(arr)):
if arr[i] < heap[0]:
heap[0] = arr[i]
heapify(heap, 0)
return heap
o(n)的方法。需要用到乙個經典的演算法——bfprt 演算法。該演算法解決的問題是,在時間複雜度o(n)內,從無序陣列中找到第k個最小的數。顯然,如果我們找到第k個最小的數,只需要再遍歷一次陣列即可找到最小的k個數。
假設bfprt演算法的函式是selct(arr, k),表示在arr中找到第k個最小的數並返回。具體演算法過程如下:
將arr中的n個元素5個5個分成一組,不夠5個的單獨分成一組
對每個組進行組內排序(插入排序)。排序後找到每個組的中位數,如果組的元素個數是偶數,這裡規定找下中位數
步驟2中一定會找到n/5個中位數,讓這些中位數組成乙個新的陣列,假設為res。遞迴呼叫select(res, len(res)/2),意義是找到這個新陣列的中位數,即res中第res/2小的數
假設步驟3中遞迴呼叫select後,返回的數是x。根據這個x劃分整個arr陣列(partition過程):比x小的都放在x的左邊,比x大的都放在x的右邊。假設劃分完後,x在arr中的位置記為 i
如果i == k-1,說明x為整個陣列中第k個小的數,直接返回;如果i > k-1,說明第k個最小的數在x左邊,呼叫select函式在左半區尋找;如果i < k-1,說明第k個最小的數在x右邊,呼叫select函式在右半區尋找
在整個實現的過程中,對bfprt做了一點改進,改進的地方是當中位數的中位數x出現多次時,我們返回的是乙個下標範圍而不是乙個值。這樣可以減少遞迴呼叫的次數。
下面是使用python3.5實現的**:
def
getminknumsbybfprt
(arr, k):
if arr == none
or len(arr) == 0
or k < 1
or k > len(arr):
return arr
minkth = getminkthbybfprt(arr, k)
res =
for i in range(len(arr)):
if arr[i] < minkth:
for i in range(len(res), k):
return res
defgetminkthbybfprt
(arr, k):
copyarr = [arr[i] for i in range(len(arr))]
return select(copyarr, 0, len(copyarr)-1, k-1)
defselect
(arr, begin, end, index):
if begin == end:
return arr[begin]
pivot = medianofmedian(arr, begin, end)
pivotrange = partition(arr, begin, end, pivot) #返回乙個大小為2的陣列,表示pivot這個數 \
在陣列中的範圍,因為pivot可能出現了不止一次
if index >= pivotrange[0] and index <= pivotrange[1]:
return arr[index]
elif index < pivotrange[0]:
return select(arr, begin, pivotrange[0]-1, index)
else:
return select(arr, pivotrange[1]+1, end, index)
defmedianofmedian
(arr, begin, end):
num = end - begin + 1
offset = 0
if num % 5 == 0
else
1 res = [0
for i in range(num//5 + offset)]
for i in range(len(res)):
start = begin + i * 5
last = start + 4
if start + 4
<= end else end
res[i] = median(arr, start, last)
return select(res, 0, len(res) - 1, len(res) // 2)
defmedian
(arr, begin, end):
insertsorts(arr, begin, end)
mid = (begin + end) // 2
return arr[mid] if (end - begin + 1) % 2 == 1
else arr[mid+1]
definsertsorts
(arr, begin, end):
for i in range(begin+1, end+1):
tmp = arr[i]
j = i
while j > begin and tmp < arr[j-1]:
arr[j] = arr[j-1]
j -= 1
arr[j] = tmp
defpartition
(arr, begin, end, pivot):
small = begin - 1
cur = begin
big = end + 1
while cur != big:
if arr[cur] == pivot:
cur += 1
elif arr[cur] < pivot:
arr[small+1], arr[cur] = arr[cur], arr[small+1]
small += 1
cur += 1
else:
arr[cur], arr[big-1] = arr[big-1], arr[cur]
big -= 1
ran =
return ran
陣列與矩陣 找到無序陣列中最小的k個數
題目 給定乙個無序的整型陣列arr,找到其中最小的k個數。要求 如果陣列arr的長度為n,排序之後自然可以得到最小的k個數,此時時間複雜度與排序演算法的時間複雜度相同,為o nlogn 本題要求讀者實現時間複雜度為o nlogk 和o n 的方法。基本思路 o nlogk 的方法。思路很簡單,就是一...
找到無序陣列中最小的k個數
找到無序陣列中最小的k個數 給定乙個整型陣列arr,找到其中最小的k個數。輸入描述 輸入包含兩行,第一行包含兩個整數n和k 1 k n 105 1 leq k leq n leq 10 5 1 k n 105 代表陣列arr的長度,第二行包含n個整數,代表陣列arr 1 a rri 109 arr ...
8 4找到無序陣列中最小的k個數
題目 給定乙個無序的整型陣列arr,找到其中最小的k個數。時間複雜度至少為o nlogk 實現public int getminknumsbyheap int arr,int k int kheap newint k 代表目前被選出的k個最小的數 for int i 0 i k i for int ...