陣列與矩陣 找到無序陣列中最小的k個數

2021-08-21 17:07:03 字數 4342 閱讀 8524

【題目】

給定乙個無序的整型陣列arr,找到其中最小的k個數。

【要求】

如果陣列arr的長度為n,排序之後自然可以得到最小的k個數,此時時間複雜度與排序演算法的時間複雜度相同,為o(nlogn)。本題要求讀者實現時間複雜度為o(nlogk)和o(n)的方法。

【基本思路】

o(nlogk)的方法。思路很簡單,就是一直維護乙個大小為k的大根堆,這個堆表示目前選出的k個最小的數。接下來遍歷整個陣列,遍歷過程中看當前數是否比堆頂元素小,如果是的話,將堆頂元素替換成當前的數,然後從堆頂向下調整堆;否則,不進行任何操作。遍歷結束後,堆中的k個數就是答案。

下面是使用python3.5實現的**:

def getminknumsbyheap(arr, k):

def heapinsert(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

def heapify(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

def getminkthbybfprt(arr, k):

copyarr = [arr[i] for i in range(len(arr))]

return select(copyarr, 0, len(copyarr)-1, k-1)

def select(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)

def medianofmedian(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)

def median(arr, begin, end):

insertsorts(arr, begin, end)

mid = (begin + end) // 2

return arr[mid] if (end - begin + 1) % 2 == 1 else arr[mid+1]

def insertsorts(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

def partition(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 ...