4 兩個有序陣列中第k小的數

2021-07-24 12:57:17 字數 3373 閱讀 3553

參考**

求兩個有序陣列中第k個元素。要求時間複雜度o(log(m+n)) ,空間複雜度o(1)。

def

median

(num1,num2,k):

len1,len2 = len(num1),len(num2)

if (len1 + len2) < k:#首先得檢查合法行啊

return -1

index1,index2 = 0,0

while index1 < len1 and index2 < len2:

if index1 + index2 == k - 1: #此時已經比較了k-1個,所以此時較小的那個就是第k個小的數

return min(num1[index1],num2[index2])

if num1[index1] < num2[index2]:

index1 += 1

else:

index2 += 1

if index1 >= len1 or index2 >= len2:#防止越界

break

#num2空了但是未取到第k個數,所以num1中第index1 + k - index2 - 1就為第k個數(-1是因為索引從0開始)

if (index1 + index2) < k and index1 < len1:

return num1[k - index2 - 1]

#同理if (index1 + index2) < k and index2 < len2:

return num2[k - index1 - 1]

這種方法的時間複雜度為o(m + n),不合要求啊。看到(log(m+n))就會想到二分。。。

假設有序序列a和b的元素個數都大於k/2,下標從0開始,a[x]代表a的第x+1個元素,總序列是由a,b組成的有序序列,尋找總序列中的第k個元素。

比較a[k/2-1]和b[k/2-1]的值,

a[k/2-1]:a中第k/2個元素;

b[k/2-1]:b中第k/2個元素;

這兩個元素比較共有三種情況:

如果a[k/2-1] < b[k/2-1],這表明a[0]至a[k/2-1] (包括a[k/2-1])的元素都在a和b合併之後的總序列中的前k個元素中。也就是說,a[0]至a[k/2-1]不可能是總序列的第k個值,可以將其拋棄;

同理,如果a[k/2-1] > b[k/2-1],這表示b[0]至b[k/2-1]的元素都在a和b合併之後的前k個元素中,可以拋棄;

如果a[k/2-1] == b[k/2-1],運氣很好啊,說明已經找到第k個元素了,直接返回。因為兩個陣列中分別有k/2 - 1個元素小於a[k/2-1],並且序列都有序,那麼a[k/2 - 1]肯定是第k個元素了。

舉例說明:

a = (a0,a1,a2,a3);b = (b0,b1,b2,b3);a0 < a1 < a2 < a3,b0 < b1 < b2 < b3.尋找總序列的第4個元素。

分別取a和b中的第2個元素,a1,b1;

如果a1 < b1,說明a0,a1不可能是總序列的第4個元素,捨棄a0,a1;

因為a0,a1,b0,b1中b1最大,並且序列有序,a0,a1不可能是總序列的第k個元素,所以可以捨棄a0,a1;

如果a1 > b1,說明b0,b1不可能是總序列的第4個元素,捨棄b0,b1;

因為a0,a1,b0,b1中a1最大,並且序列有序,b0,b1不可能是總序列的第k個元素,所以可以捨棄b0,b1;

如果a1 = b1,說明a1恰好為總序列的第k個元素,因為a0,b0比a1,b1小,總序列排序:min(a0,b0) <= max(a0,b0) <= a1<=b1,所以a1恰好為總序列的第4個元素。

由以上可知,可以通過二分法將問題規模縮小,假設p + q = k,a[p -1]對應a中第p個元素,總序列是由a,b組成的有序序列

如果序列a中的第p個元素小於序列b中的第q個元素,則序列a的前p個元素肯定都小於總序列的第k個元素,即序列a中的前p個元素肯定不是總序列的第k個元素,所以將序列a的前p個元素全部拋棄,形成乙個較短的新序列;然後,用新序列替代原先的a序列,再找其中的第k - p個元素(因為已經排除了p個元素,k需要更新為k-p),依次遞迴;

同理,如果a序列中的第p個元素大於序列b中的第q個元素,則拋棄序列b的前q個元素,k = k - q;

如果序列a的第p個元素等於序列b的第q個元素,則第k個元素為a(p-1)

遞迴終止條件:

如果乙個序列為空,那麼第k個元素就是另乙個序列的第k個元素;

如果k = 1,那麼直接返回min(a[0],b[0])

如果a[p - 1] == b[q - 1],則第k個數就為a[p - 1];

根據以上就可以寫**啦- -

def

fun(nums1,nums2,k):

if (len(nums2) + len(nums1)) < k: #防患於未然

return

'error,k大於兩個list總個數'

#必須優先檢查list是否為空,不然會越界...

#如果nums2為空,那麼第k個元素,肯定是nums[k - 1]了啊(k - 1為序列第k個元素,是因為list從0開始...)

if len(nums2) == 0:

return nums1[k - 1]

#為了防止越界,保證nums1長度大於nums2

if len(nums2) > len(nums1):

return self.fun(nums2,nums1,k)

#取第乙個元素,肯定是要比兩個list的首個元素中比較小的那個

if k == 1:

return min(nums1[0],nums2[0])

#防不勝防啊...因為nums2比nums1短,取k/2有可能越界...,檢查了n久沒查出來...

#如果nums2個數不足k/2,所以只能取比k/2小,又不越界的最大值了,即len(nums2),只要保證p + q = k就好啦...

q = min(k / 2, len(nums2))

p = k - q

if nums1[p - 1] < nums2[q - 1]:

return self.fun(nums1[p:],nums2,k - p)

elif nums1[p - 1] > nums2[q - 1]:

return self.fun(nums1,nums2[q:],k - q)

else:

return nums1[p - 1] #返回nums2[q - 1]也可以,反正一樣大....

兩個有序數序列中找第k小

description 已知兩個已經排好序 非減序 的序列x和y,其中x的長度為m,y長度為n,現在請你用分治演算法,找出x和y的第k小的數,演算法時間複雜度為o max 分析 既然說明了分治,那肯定是劃分為子問題。又已經排好序,模擬於二分查詢,每次丟棄一半,我們可以將x的一半元素與y的一半元素合併...

兩個有序陣列的第k大數

coding utf 8 兩有序陣列的第k大 一種思路是用o m n 的空間複雜度,然後時間複雜度的話只需要o k 吧,就是從後往前掃k個就ok了 另一就是不需要額外的空復,然後時復o log m n 遞迴 a 19,11,8,7,4,2,0 b 13,8,6,3,1 k 4 m,n len a l...

查詢兩個有序陣列的中位數和第K小的數

陣列中查詢中位數 1 將兩個數組合合併並排序,查詢中位數,時間複雜度為o n m lg m n 空間複雜度o m n 2 分別比較兩個陣列的k 2處的數,如果mida midb,說明要查詢的兩個數在陣列a的右半部分和陣列b的左半部份。如果mida midb,說明要查詢的兩個數在陣列a的左半部分和陣列...