题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
思考
- 二分查找
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;
}