開篇
今天日常刷題的時候,依舊選擇 tag
是 Binary Serach
型別的題目。這還是道熱門的題目,看點贊數就知道了。
題目介紹
給定一個 N*N
的矩陣(這麼正經的翻譯你一定不喜歡,所以你直接理解成二維陣列就行了)。它的每行和每列都按照升序排序,問題是,通過一個給定的數值 K
,找出這個二維陣列中第 K
小的數。
先說下我最初的想法,這個題目歸別到 Binary Serach
,那就肯定是可以用二分的思想的,但是按照我之前刷的二分題目,就算獲取到中位數,我也並不能確定這個中位數的位置大小排在陣列的整體大小位置,我只能確定我這一行的位置,我也只知道我當前位置的上一行對應的列比我小而已。
題目分析
之所以這樣想,是我潛意思裡都把二維結構+二分查詢,對應的中位數預設理解為就是索引位置。跳出這個思維空間再思考,查詢二維陣列中第 K
小的數那必然存在於二維陣列當中啊。調整思路。開始二分模板解題。之前寫過一篇而二分的文章)
left 初始值即陣列最小值 $left=$matrix[0][0],right 初始值即陣列最大值 $right=$matrix[行數-1][列數-1];
開始往中間擠壓。先求得中位數。這個的中位數指的是具體的值,而不是索引位置的意思。只要統計這個二維陣列中小於等於當前中位數的個數。如果統計的個數小於給定值 K
,說明第 K
小的值並不在 left
和 中位數之間,而是在中位數+1 至 right
之間,所以重新賦值 left
(不包括中位數) ,否則的話賦值 right
。最後隨意返回 left
或者 right
。
上程式碼
/**
* @param Integer[][] $matrix
* @param Integer $k
* @return Integer
*/
function kthSmallest($matrix, $k)
{
$row = count($matrix);
$col = count($matrix[0]);
$left = $matrix[0][0];
$right = $matrix[$row - 1][$col - 1];
while ($left < $right) {
$middle = ($left + $right) >> 1;
$count = $this->lessMiddleAmount($matrix, $middle, $row - 1, $col);
if ($count < $k) {
$left = $middle + 1;
} else {
$right = $middle;
}
}
return $left;
}
function lessMiddleAmount($matrix, $middle, $row, $col)
{
$left = 0;
$count = 0;
while ($row >= 0 && $left < $col) {
if ($matrix[$row][$left] <= $middle) {
$count += $row + 1;
$left++;
} else {
$row--;
}
}
return $count;
}
結尾
上面 left
為什麼不包括中位數?可以從兩個角度分析,按照邏輯思維的角度,既然統計小於等於中位數的個數都比 K
小,我還咋麼去爭取當上第 K
小的數??? 另一角度,從當前執行的程式上來分析,獲取的中位數我習慣這樣寫 ($left+$right)>>1
,如果 left
+ right
是奇數的情況下並沒有爭議,如果是偶數呢?那麼我這裡將得到一個左中位數。程式往下面走,如果走的是 if($count<$k)
這個分支的話,並且我的 $left
包含了中位數,會發生什麼?答案很明顯,程式進入死迴圈。直至你收到一個 Time Limit Exceeded
的錯誤。
執行結果也是妥妥的 AC。(千萬別相信這個執行效率,都是騙人的?)