題目描述
在一個二維數組中(每個一維數組的長度相同),每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。
思考
- 二分查找
a1 | a2 | a3 | a4 | a5 | a6 |
---|---|---|---|---|---|
b1 | → | → | → | → | → |
c1 | ↓ | ||||
d1 | ↓ | ||||
e1 | ↓ | ||||
f1 | ↓ |
總結
題目的理解錯了,錯誤的認為,矩陣滿足這樣的規律:
1 | 2 | 3 | 4 |
---|---|---|---|
5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 |
其實這只是一個特例,題目只說舉證中的元素滿足“左小右大,上小下大”的特點,更一般的矩陣是這樣的:
1 | 2 | 8 | 9 |
---|---|---|---|
2 | 4 | 9 | 12 |
4 | 7 | 10 | 13 |
7 | 8 | 15 | 15 |
正確理解的題目的意思以后,我編寫出了滿足題目要求的代碼。具體的思路是:遍歷每一行,對每一行進行二分查找。
package com.learn.java;
import java.util.Arrays;
/**
* @author xzy
* @date 2020-03-25 18:56
* 說明:
*/
public class Main {
public static void main(String[] args) {
Main main = new Main();
int[][] array = new int[][]{
{1, 2, 8, 9},
{2, 4, 9, 12},
{4, 7, 10, 13},
{6, 8, 11, 15}
};
for (int i = 0; i < 40; i++) {
System.out.println("target:" + i + " find:" + main.find(i, array));
}
}
/**
* @param target - 查找目標
* @param array - 查找數組
* @return 找到:元素下標 找不到:-1
*/
public int binarySearch(int target, int[] array) {
// head:查找區間頭 middle:查找區間中 end:查找區間尾
int head = 0;
int end = array.length - 1;
int middle = end / 2;
//1. 不可能找得到
if (target < array[head] || target > array[end]) {
return -2;
}
//2. 二分查找
while (head <= end) {
if (target == array[middle]) {
//1. 找到
return middle;
} else if (target < array[middle]) {
//2.向左側繼續找
end = middle - 1;
middle = (end - head) / 2 + head;
} else {
//3.向右側繼續找
head = middle + 1;
middle = (end - head) / 2 + head;
}
}
return -1;
}
public boolean find(int target, int[][] array) {
for (int row = 0; row < array[0].length; row++) {
if (binarySearch(target, array[row]) >= 0) {
return true;
}
}
return false;
}
}
優化
仔細的琢磨“左小右大,上小下大”
1 | 2 | 3 | 9 |
---|---|---|---|
2 | 4 | 9 | 12 |
4 | 7 | 10 | 13 |
7 | 8 | 15 | 15 |
- 行角度
第1行最右邊的元素,是第一行最大的。
第2行最右邊的元素,是前兩行最大的。
第3行最右邊的元素,是前三行最大的。
第4行最右邊的元素,是前四行最大的。
- 列角度
第1列最后一個元素,是第一列最大的。
第2列最后一個元素,是前兩列最大的。
第3列最后一個元素,是前三列最大的。
第4列最后一個元素,是前四列最大的。
新的算法思想:從行列角度出發,從第一行開始,將target與每一行最后一個元素比較,排除target不可能存在的行,同時排除target不可能存在的列。
以target == 11為例,將target與第一行最后一個元素9比較,發現target > 9,而9又是第1行最大的元素,顯然,target不可能存在於第一行。繼續將target與第二行最后一個元素12比較,發現target<12,而12又是第4列后3行中最小的元素,顯然,target不可能存在於第4列。
上述算法不斷縮小元素可能存在的范圍,范圍的縮小過程是這樣的:
- target > 9
1 | 2 | 3 | 9 |
---|---|---|---|
2 | 4 | 9 | 12 |
4 | 7 | 10 | 13 |
7 | 8 | 15 | 15 |
- target < 12
2 | 4 | 9 | 12 |
---|---|---|---|
4 | 7 | 10 | 13 |
7 | 8 | 15 | 15 |
- target > 9
2 | 4 | 9 |
---|---|---|
4 | 7 | 10 |
7 | 8 | 15 |
- target > 10
4 | 7 | 10 |
---|---|---|
7 | 8 | 15 |
-
target < 15
7 8 15 -
target > 8
7 8 -
已經沒有范圍縮小到0,可以確認,矩陣中不存在target。
利用新的思想,新的實現代碼:
public boolean find2(int target, int[][] array) {
//rowMax:行坐標最大值 columnMax:列坐標最大值
int rowMax = array.length - 1;
int columnMax = array[0].length - 1;
//空矩陣判斷
if (rowMax < 0 || columnMax < 0) {
return false;
}
//target是否有可能存在於矩陣
if (target < array[0][0] || target > array[rowMax][columnMax]) {
return false;
}
//(rowPoint,columnPoint)當前檢查元素的坐標
int rowPoint = 0;
int columnPoint = columnMax;
while (rowPoint <= rowMax && columnPoint >= 0) {
if (target == array[rowPoint][columnPoint]) {
return true;
} else if (target > array[rowPoint][columnPoint]) {
//不可能在這一行
rowPoint++;
} else {
//不可能在這一列
columnPoint--;
}
}
return false;
}