劍指 offer 第一題: 二維陣列中的查詢
題目描述
在一個二維陣列中(每個一維陣列的長度相同),每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函式,輸入這樣的一個二維陣列和一個整數,判斷陣列中是否含有該整數。
題目分析
如果沒有頭緒的話,很顯然使用 暴力解法 是完全可以解決該問題的。
即遍歷二維陣列中的每一個元素,時間複雜度:O(n^2)。
其實到這裡我們就可以發現,使用這種暴力解法並沒有充分利用題目給出的資訊。這個二維陣列是有特點的。
每一行都是遞增的
每一列都是遞增的
解法
解法一:二分法
對於有序陣列的查詢問題而言,二分法是最容易想到的一個解法。
在這裡,對每一行使用二分查詢,時間複雜度為 O(nlogn) 。二分查詢複雜度 O(logn),一共 n 行,所以是總體的時間複雜度是 O(nlogn) 。
解法二:規律法
根據二維陣列由上到下,由左到右遞增的規律。
從左下角開始遍歷,如果當前值比 target 小則往右找,如果比 target 大則往上找,如果存在,必然可以找到目標數字。
即選取右上角或者左下角的元素 a[row] [col] 與 target 進行比較, 當target小於元素 a[row] [col] 時,那麼 target 必定在元素 a 所在行的左邊,讓 col-- ;當 target 大於元素 a[row] [col] 時,那麼 target 必定在元素 a 所在列的下邊,讓 row++ ;
程式碼如下:
public class Solution {
public boolean Find(int target, int [][] array) {
int row = 0;
int col = array[0].length - 1;
while(row <= array.length - 1 && col >= 0){
if(target == array[row][col])
return true;
else if(target > array[row][col])
row++ ;
else
col-- ;
}
return false;
}
}
解法三:二分規律法
將解法一和解法二進行結合:對每行每列都使用二分查詢,此時的時間複雜度為 O(logn * logm)。
比如查詢數字 9,首先使用用二分查詢選出一行,總共有 5 行,那麼( 0 + 5 ) / 2 = 2
,所以我們找出了第 2行為基準行。
接下來對這一行(即第 2 行)又使用二分查詢, 找出這一行(即第 2 行)中最後一個比目標值小的值,這裡是 6。
6 及其所在的行和列把這個矩形劃分為 4 部分:
左上部分(圖 7 灰色部分),包括所在行的左邊部分和所在列的上邊部分:這一部分是絕對不會有目標數字的。因為這部分數字肯定比 6 小,而 6 又是小於目標數字的,所以左上部分全部小於目標數字。也就是說這個區域的數字不需要再進行判斷了。
右下部分(圖 7 綠色部分),包括所在行的右邊部分,但不包括所在列的下面部分, 這一部分也是絕對不會有目標數字的。因為這部分都比 6 右邊的數字 11 大,而 11 又比目標數字 9 更大,所以右下部分全部都比目標數字大。也就是說這個區域的數字也不需要再進行判斷了。
左下部分(圖 7 藍色部分),可能含有目標數字。
右上部分(圖 7 棕色部分),可能含有目標數字。
這樣,實際上篩選的區域就只剩下左下部分(圖 7 藍色部分)和 右上部分(圖 7 棕色部分)這兩塊區域了,相比於解法二而言,使用這種解法平均情況下每一次查詢,都可以把行和列的長度減少一半。
程式碼如下:
public class Solution {
public boolean Find(int target, int [][] array) {
// 特殊情況處理
if (array == null || array.length == 0 || array[0].length == 0) {
return false;
}
int h = array.length - 1;
int w = array[0].length - 1;
// 如果目標值小於最小值 或者 目標值大於最大值,那肯定不存在
if (array[0][0] > target || array[h][w] < target) {
return false;
}
return binarySearchIn2DArray(array, target, 0, h, 0, w);
}
public static boolean binarySearchIn2DArray(int[][] array, int target, int startX, int endX, int startY, int endY) {
if (startX > endX || startY > endY) {
return false;
}
//首先,根據二分法找出中間行
int x = (startX + endX) / 2;
//對該行進行二分查詢
int result = binarySearch(array[x], target, startY, endY);
//找到的值位於 x 行,result 列
if (array[x][result] == target) {
return true; // 如果找到則成功
}
//對剩餘的兩部分分別進行遞迴查詢
return binarySearchIn2DArray(array, target, startX, x - 1, result + 1, endY)
|| binarySearchIn2DArray(array, target, x + 1, endX, startY, result);
}
public static int binarySearch(int[] array, int target, int start, int end) {
int i = (start + end) / 2;
if (array[i] == target || start > end) {
return i;
} else if (array[i] > target) {
return binarySearch(array, target, start, i - 1);
} else {
return binarySearch(array, target, i + 1, end);
}
}
}
相關文章
- 【劍指offer】二維陣列中的查詢陣列
- 【劍指offer】【1】二維陣列中的查詢陣列
- 二維陣列中的查詢——牛客劍指offer陣列
- 劍指 Offer 04. 二維陣列中的查詢陣列
- 劍指offer刷題記錄_2. 二維陣列中的查詢陣列
- nowcoder-劍指offer-二維陣列的查詢陣列
- 劍指Offer積累-JZ1-二維陣列中的查詢陣列
- 1. 二位陣列中的查詢(劍指offer)陣列
- offer通過--3二維陣列中查詢-2陣列
- 二維陣列中的查詢陣列
- 每日一題 - 劍指 Offer 53 - I. 在排序陣列中查詢數字 I每日一題排序陣列
- 【JZOF】二維陣列中的查詢陣列
- 二維陣列查詢陣列
- 劍指 offer(1) -- 陣列篇陣列
- 劍指Offer-37-陣列中逆序對陣列
- 劍指Offer--陣列中重複的數字陣列
- 劍指offer——陣列中的逆序對C++(75%)陣列C++
- 劍指offer刷題之路--1.陣列中重複的數字陣列
- 劍指Offer-連續子陣列中的最大和陣列
- 劍指offer之順序列印陣列陣列
- 劍指offer-例題 連續子陣列的最大和陣列
- 劍指offer:旋轉陣列的最小數字陣列
- 劍指offer 旋轉陣列的最小數字陣列
- Leetcode 劍指 Offer 03. 陣列中重複的數字LeetCode陣列
- 指標陣列和陣列指標與二維陣列指標陣列
- 劍指offer-轉陣列的最小數字-php陣列PHP
- 劍指Offer-34-把陣列排成最小的數陣列
- 劍指offer——把陣列排成最小的數C++陣列C++
- 關於二維陣列指標的問題陣列指標
- 劍指Offer-把陣列中的數排成一個最小的數陣列
- 劍指 Offer 56 - I. 陣列中數字出現的次數陣列
- 【劍指offer】7.旋轉陣列的最小數字陣列
- 【劍指 Offer】11. 旋轉陣列的最小數字陣列
- 劍指 Offer 42.連續子陣列的最大和陣列
- 劍指 Offer 11. 旋轉陣列的最小數字陣列
- 0二分查詢簡單 牛客NC.71旋轉陣列的最小數字 leetcode劍指 Offer 11. 旋轉陣列的最小數字陣列LeetCode
- 雙指標查詢陣列的連續規律子陣列問題指標陣列
- 二維陣列與指標陣列指標