二维数组中的查找

九度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
****************************************************************/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值