第 k 小的數

2022-03-29 18:43:02 字數 2583 閱讀 8482

給定兩個大小為 m 和 n 的不同時為空的有序陣列 nums1 和 nums2。找出這兩個有序陣列的中位數,並且要求演算法的時間複雜度為 o(log(m + n))。

題目要求的時間複雜度是 o(log(m + n)),要產生這樣級別的時間複雜度只有採用二分查詢法,用分治遞迴的思路來考慮這個問題。

需要轉換題目中求中位數的問題為求第 k 小數的問題。如果 m + n 是奇數,那麼尋找第 k = (m + n)/2 + 1 小的數即可;如果長度和是偶數,那麼我們還需要尋找第 (m + n)/2 小的數,然後計算兩數的平均值。

在求解整個問題的過程中,我們始終需要考慮乙個很重要的問題--陣列索引越界問題。

下面將詳細地分析整個遞迴流程。

①、首先定義遞迴函式的作用:尋找兩個有序陣列 nums1 陣列中 [l1, r1] 範圍內和 nums2 陣列 [l2, r2] 範圍內第 k 小的數,k 從 1開始計數。

/**

* l1 nums1陣列的尋找範圍的左邊界

* r1 nums1陣列的尋找範圍的右邊界

* l2 nums2陣列的尋找範圍的左邊界

* r2 nums2陣列的尋找範圍的右邊界

* k 需要尋找第k小的元素

*/int findkth(int nums1, int l1, int r1, int nums2, int l2, int r2, int k)

②、用 len1 = r1 - l1 + 1 來記錄 nums1 陣列中尋找範圍的長度,用 len2 = r2 - l2 + 1 來記錄 nums2 陣列中尋找範圍的長度。

③、如果要尋找的 k > len1 + len2,就像只有 3 個數字要找第 4 小的數一樣,超出尋找區域,顯然無法找到。

④、遞迴的終止條件:

當 len1 = 0 時,說明只有 nums2 陣列中有元素,直接取 nums2[l2 + k - 1] 位元素即可。

當 k = 1 時,說明要取的是兩個有序陣列中的最小值 min(nums1[l1], nums2[l2])。

⑤、遞迴過程:

由於要求的是第 k 小的數,而且是在兩個有序陣列中求。劃分兩個陣列時按照 k 值來分。取變數 i = min(len1, k/2),之所以這麼取,是為了防止 l1 + k/2 - 1 > len1 導致從 nums1 取值越界。再取變數 j = min(len2, k/2)。

接下來比較 nums1[l1 + i - 1] 和 nums2[l2 + i - 1] 這兩個值。

如果 nums1[l1 + i - 1] <= nums2[l2 + j - 1],顯然 nums1 陣列中索引為 l1 + i - 1 及之前的元素不可能是中位數,去除 nums1 陣列中 [l1, l1 + i - 1] 範圍內的元素,縮小了查詢範圍。我們遞迴呼叫該函式,此時在 nums1 中的查詢範圍變成了 nums1[l1 + i, r1],此時要找的也不應該是第 k 小的元素,因為已經剔除了 i 個比 k 小的元素,因此我們要找的元素變成了第 k - i 小的元素。

如果 nums1[l1 + i - 1] > nums2[l2 + j - 1],同理,nums2 陣列中索引為 l2 + j - 1 及之前的元素不可能是中位數,縮小查詢範圍,剔除了 j 個比 k 小的元素,因此我們要找的元素變成了第 k - j 小的元素。

因為 i + j = min(len1, k/2) + min(len2, k/2) <= k,所以可以直接判斷 [l1, l1 + i - 1] 或者 [l2, l2 + j -1] 區間的元素不可能是中位數。 

總結:演算法的思想是不斷的剔除資料,逐漸逼近第 k 小的數。

假設陣列長度足夠長,每次剔除的元素都是 k/2(i 或者j),顯然我們需要 log(k) 次才能找到第 k 小數,這和二分查詢法是同理的,而我們要找的 k 值要麼是 (m + n)/2 + 1,要麼額外再加上 (m + n)/2,因此時間複雜度是 o(log(m + n)) 級別的。

#define min(a, b) (a) < (b) ? (a) : (b)

int findkth(int* nums1, int left1, int right1, int* nums2, int left2, int right2, int k)

if(n1 == 0)

else if (n2 == 0)

if(k == 1)

int i = min(n1, k / 2);

int j = min(n2, k / 2);

// 剔除比第 k 小的數還小的數,逐漸逼近

if(nums1[left1 + i - 1] > nums2[left2 + j - 1])

else

}double findmediansortedarrays(int* nums1, int nums1size, int* nums2, int nums2size)

// 兩個陣列總長度是偶數

else

}

leetcode004——兩個排序陣列的中位數

第k小的數

輸入n個整數和乙個正整數k 1 k n 輸出這些整數從小到大排序後的第k個 思路1 最容易想到的方法 先對這個序列從小到大排序,然後輸出前面的最小的k個數即可。如果選擇快速排序法來進行排序,則時間複雜度 o n logn class solution 時間複雜度o nlogn 思路2 在思路1的基礎...

第k小的數

time limit 5000 ms memory limit 65536 kib problem description 現有乙個包含n個整數 1 n 10000000 的無序序列 保證序列內元素各不相同 輸入乙個整數k 1 k n 請用較快的方式找出該序列的第k小數並輸出。input 多組輸入。...

第k小的數

time limit 5000 ms memory limit 65536 kib submit statistic problem description 現有乙個包含n個整數 1 n 10000000 的無序序列 保證序列內元素各不相同 輸入乙個整數k 1 k n 請用較快的方式找出該序列的第k...