九度OJ第1384题 http://ac.jobdu.com/problem.php?pid=1384
剑指offer面试题3
思路分析
由于已知每一行从左到右都是递增的,每一列从上到下都是递增的。因此可知,最左上角的是矩阵中最小的,最右下角是矩阵中最大的,那么,可以往二分查找的思路上去考虑,只不过这是一个二维的。在最大和最小之间找一个中间位置mid,这个位置可以是左下角,也可以是右上角,两者思路是一样的。若是这个中间值小于target,那么可以知道,在中间值之前的都小于target,可以不做考虑;反之,在中间值之后的值都大于target,可以不做考虑
其实如果只要求题目AC的话,还有一个讨巧的办法:利用题目本身的bug。由于要查找的数在输入数组之前已经存在了,那么什么算法都不需要,直接在输入数组的时候一个一个对比就是了。
使用c++实现
#include<cstdio>
#include<vector>
using namespace std;
class Solution {
public:
bool findTarget(vector<vector<int> >& matrix, const int target);
//matrix是一个m行,n列的矩阵
};
bool Solution::findTarget(vector<vector<int> >& matrix, const int target) {
if (0 == matrix.size()) {
return false;
}
const int m = matrix.size();
const int n = matrix[0].size();
int row = 0; // 行
int column = n -1; // 列
while (row < m && column >= 0) {
//if (matrix[row][column] == target) {
if (matrix[row][column] == target) {
return true;
}
//matrix[row][column] > target 则往前回退一列
if (matrix[row][column] > target) {
--column;
} else { //matrix[row][column] < target 往下前进一行
++row;
}
} // close while
return false;
}
int main() {
Solution M;
int m = 0, n = 0, target = 0;
while (scanf("%d %d", &m, &n) != EOF) {
scanf("%d", &target);
// 当知道容量大小的时候,使用c++的vector效率并不比原生的数组低。
vector<vector<int> > arr(m, vector<int>(n, 0));
for (int i = 0; i != m; ++i) {
for (int j = 0; j != n; ++j) {
scanf("%d", &arr[i][j]);
}
}
bool found = M.findTarget(arr,target);
if (found) {
printf("Yes\n");
}
else {
printf("No\n");
}
}
return 0;
}
/**************************************************************
Problem: 1384
User: buptxxz
Language: C++
Result: Accepted
Time:670 ms
Memory:4880 kb
****************************************************************/
使用java实现
注意,由于输入的数据量较大,所以使用Scanner将会超时,因此使用BufferedReader
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
public class Main {
private boolean findTarget(int[][] matrix, int target) {
if (null == matrix || 0 == matrix.length) {
return false;
}
final int m = matrix.length;
final int n = matrix[0].length;
int row = 0;
int col = n - 1;
while (row < m && col >= 0) {
if (target == matrix[row][col]) {
return true;
}
if (target > matrix[row][col]) {
++row;
} else {
--col;
}
}
return false;
}
public static void main(String[] args) throws IOException {
Main M = new Main();
StreamTokenizer st = new StreamTokenizer(new BufferedReader(
new InputStreamReader(System.in)));
while (st.nextToken() != StreamTokenizer.TT_EOF) {
int m = (int)st.nval;
st.nextToken();
int n = (int)st.nval;
st.nextToken();
int target = (int)st.nval;
int[][] matrix = new int[m][n];
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
st.nextToken();
matrix[i][j] = (int)st.nval;
}
}
boolean ret = M.findTarget(matrix, target);
if (ret) {
System.out.println("Yes");
} else {
System.out.println("No");
}
}
}
}
/**************************************************************
Problem: 1384
User: buptxxz
Language: Java
Result: Accepted
Time:1790 ms
Memory:31560 kb
****************************************************************/
讨巧做法
下面是讨巧的做法,充分利用了题目设计上的bug,在读取数组中数字的时候就进行判断。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
StreamTokenizer st = new StreamTokenizer(new BufferedReader(
new InputStreamReader(System.in)));
while (st.nextToken() != StreamTokenizer.TT_EOF) {
int m = (int)st.nval;
st.nextToken();
int n = (int)st.nval;
st.nextToken();
int target = (int)st.nval;
boolean found = false;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
st.nextToken();
if (target == (int)st.nval) {
found = true;
}
}
}
if (found) {
System.out.println("Yes");
} else {
System.out.println("No");
}
}
}
}
/**************************************************************
Problem: 1384
User: buptxxz
Language: Java
Result: Accepted
Time:1690 ms
Memory:24748 kb
****************************************************************/
仔细对比发现,其实时间并没有节省多少,这说明整个题目的运行时间,多数都花在输入输出上面了,当然还花在了jvm的启动上面。
跳出思维定式
假设题目没有上面所说的先读取target,再读取数组元素的bug。
仔细读题发现,已经限制了target的大小范围为(1<=t<=1000000),那么为何不开辟一个大数组作为map,统计某个数是否存在呢。然后用target作为key,直接查找,O(1)的时间复杂度。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
StreamTokenizer st = new StreamTokenizer(new BufferedReader(
new InputStreamReader(System.in)));
while (st.nextToken() != StreamTokenizer.TT_EOF) {
int m = (int)st.nval;
st.nextToken();
int n = (int)st.nval;
st.nextToken();
int target = (int)st.nval; // 我们假设target是最后才读入的
boolean[] map = new boolean[1000000 + 1]; // 下标最大为1000000
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
st.nextToken();
int num = (int)st.nval;
if (num >= 1 && num <= 1000000) {
map[num] = true;
}
}
}
if (map[target]) {
System.out.println("Yes");
} else {
System.out.println("No");
}
}
}
}
/**************************************************************
Problem: 1384
User: buptxxz
Language: Java
Result: Accepted
Time:1920 ms
Memory:75792 kb
****************************************************************/