劍指offer——二維數組的查找 Java

@author: sdubrz
@date: 2020.05.02
@e-mail: lwyz521604#163.com
題目來自《劍指offer》 電子工業出版社

在一個 n * m 的二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。

示例:

現有矩陣 matrix 如下:

[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]

給定 target = 5,返回 true。

給定 target = 20,返回 false。

限制:

  • 0 <= n <= 1000

  • 0 <= m <= 1000

我的解法

對於給定的一個二維數組,根據題意,其最小值和最大值肯定分別在數組的開頭和末尾,我們可以根據這個來初步判斷目標數值是否有可能在這個數組中出現。如果有可能,則可以使用二分法進行查找。

比較數組中間位置元素與目標數值的大小,圖1表示了比目標數值小的情況,圖2表示了比目標數值大的情況。根據不同的情況,分別去繼續探索紅色、藍色和綠色的區域。如此遞歸的實現。

二分法

下面是具體給出的Java程序實現。

class Solution {
    	public boolean findNumberIn2DArray(int[][] matrix, int target) {
		if(matrix.length==0) {
			return false;
		}
		int m = matrix[0].length;
		if(m==0) {
			return false;
		}
		
		return this.find2d(matrix, target, 0, matrix.length-1, 0, m-1);
	}
	
	/**
	 * 在矩陣中的一個矩形區域內查找是否含有目標數值
	 * @param matrix 矩陣
	 * @param target 要查找的數值
	 * @param left 矩形區域的最左邊
	 * @param right 矩形區域的最右邊
	 * @param up 矩形區域的最上方
	 * @param bottom 矩形區域的最下方
	 * @return
	 */
	private boolean find2d(int[][] matrix, int target, int up, int bottom, int left, int right) {
		if(left==right && up==bottom) {
			return matrix[up][left]==target;
		}
		
		if(matrix[up][left]>target) {
			return false;
		}
		
		if(matrix[bottom][right]<target) {
			return false;
		}
		
		// 如果範圍比較小,直接查找
		if(bottom-up<4 && right-left<4) {
			for(int i=up; i<=bottom; i++) {
				for(int j=left; j<=right; j++) {
					if(matrix[i][j]==target) {
						return true;
					}
				}
			}
			return false;
		}
		
		// 用二分法
		int col_m = (left+right)/2;
		int row_m = (up+bottom)/2;
		
		if(matrix[row_m][col_m]==target) {
			return true;
		}
		
		if(matrix[row_m][col_m]<target) {
			boolean temp = this.find2d(matrix, target, row_m, bottom, col_m, right);
			if(temp) {
				return true;
			}
			
//			if(row_m<bottom) {
//				temp = this.find2d(matrix, target, row_m+1, bottom, col_m, col_m);
//				if(temp) {
//					return true;
//				}
//			}
			
			if(row_m<bottom && col_m>left) {
				temp = this.find2d(matrix, target, row_m+1, bottom, left, col_m-1);
				if(temp) {
					return true;
				}
			}
			if(row_m>up && col_m<right) {
				temp = this.find2d(matrix, target, up, row_m-1, col_m+1, right);
				if(temp) {
					return true;
				}
			}
		}
		
		if(matrix[row_m][col_m]>target) {
			boolean temp = this.find2d(matrix, target, up, row_m, left, col_m);
			if(temp) {
				return temp;
			}
			if(row_m<bottom && col_m>left) {
				temp = this.find2d(matrix, target, row_m+1, bottom, left, col_m-1);
				if(temp) {
					return true;
				}
			}
			if(row_m>up && col_m<right) {
				temp = this.find2d(matrix, target, up, row_m-1, col_m+1, right);
				if(temp) {
					return temp;
				}
			}
		}
		
		
		return false;
	}
}

在 LeetCode 系統中提交的結果如下

執行結果: 通過 顯示詳情
執行用時 : 0 ms, 在所有 Java 提交中擊敗了 100.00% 的用戶
內存消耗 : 45.7 MB, 在所有 Java 提交中擊敗了 100.00% 的用戶

官方給出的線性解法

下面是官方給出的一種線性時間複雜度的方法。

官方

class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return false;
        }
        int rows = matrix.length, columns = matrix[0].length;
        int row = 0, column = columns - 1;
        while (row < rows && column >= 0) {
            int num = matrix[row][column];
            if (num == target) {
                return true;
            } else if (num > target) {
                column--;
            } else {
                row++;
            }
        }
        return false;
    }
}

相比較而言,官方的算法代碼實現上要稍微更加優美一點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章