注:如果代码出现了段错误问题,可能是没有考虑到空数组(至少包括[][[]]两种空的二维数组),健壮性也是算法的一部分

一、暴力法

1.分析:遍历数组,如果找到就返回 true
2.代码

    // Solution 1: 运行时间:15ms 占用内存:1764k
    bool Find(int target, vector > array) {
        // for循环代码可以处理空数组,不需单独处理
        int rows = array.size();
        int columns = array[0].size();
        for (int i = 0; i < rows; ++i)
            for (int j = 0; j < columns; ++j)
            {
                if (array[i][j] == target)
                    return true;
                else
                    continue;
            }
        return false;
    }

3.复杂度
时间复杂度:O(n2)
空间复杂度:O(1)

二、N行折半查找法

1.分析:依次对每一行(列)都执行折半查找
2.代码

// Solution 2: 运行时间:15ms 占用内存:1764k
bool Find(int target, vector<vector<int> > array) {
    if (array.empty())    return false;
    if (array[0].empty()) return false; //处理空数组
    int length = array.size();

    for (int i = 0; i < length; ++i)
        // 对每一列二分查找
        for (int begin=0, end=length-1, j=length/2; begin<=end;)
        {
            if (array[i][j] == target)
                return true;
            else if (array[i][j] > target)
                end = j-1;
            else 
                begin =j+1;
            j = begin + (end-begin)/2;
        }
    return false;
}

3.复杂度
时间复杂度:O(n*lgn)
空间复杂度:O(1)

4.拓展:双折半查找

二维数组分为上下左右四个边界top,bottom,left,right:
对上边界top进行折半查找,假设终止点为E,则可以将二维数组位于终止点E右边的矩形Rr排除,因为终止点E小于其右边相邻的元素E+1,而E+1是右边矩形Rr的最小元素(左上元素)
同理,对下边界bottom折半,可以排除二维数组位于终止点E左边的矩形Rl排除,
对左边界left折半,可以排除二维数组位于终止点E下边的矩形Rb排除,
对右边界right折半,可以排除二维数组位于终止点E上边的矩形Rt排除,
一轮过去,边界范围缩小,对由新边界组成的矩形重复以上操作,直到范围缩小为只有一个元素。

三、十字分割法

我首先想到的是这种方法,不过要注意这种方法无论是从思维还是实现过程都比较麻烦,实战慎用

1.分析:在主对角线方向上进行查找操作,直到一个元素大于目标,该终止点的左上区域与右下区域都可以排除,再递归剩下的左下、右上两个区域。
2.代码

// Solution 2: 运行时间:12ms 占用内存:1624k
bool Find_2(int target, vector<vector<int> > array) {
    if (array.empty())return false;
    if (array[0].empty())return false; //处理空数组

    //初始化栈
    typedef struct {
        int xmin=0,ymin=0,xmax,ymax;
    } stack_elem;
    stack_elem temp, newTemp;
    stack<stack_elem> s;
    temp.xmax = temp.ymax = array.size()-1;
    s.push(temp); 

    while (!s.empty()){
        newTemp = temp = s.top();
        s.pop();
        int x=temp.xmin, y=temp.ymin;


        while (true){
            // 首先确定区域是否可能包含目标值
            if (array[temp.xmin][temp.ymin]>target || array[temp.xmax][temp.ymax]<target) break; 
            if (temp.xmin>temp.xmax || temp.ymin>temp.ymax) break; 

            // 遍历过程节点分为几类
            if (array[x][y] == target) return true; // 情况1: 找到目标值
            if (array[x][y] > target){ //情况2:找到大于目标值的节点,缩小大区域为两个小区域
                newTemp.xmax = x-1;
                newTemp.ymin = y;
                if (newTemp.xmin<=newTemp.xmax && newTemp.ymin<=newTemp.ymax)
                    s.push(newTemp);
                temp.ymax = y-1;
                temp.xmin = x; 
                y = temp.ymin;
            }
            else if (x == temp.xmax){ //情况3:处理非正方形区域某一边界先溢出的情况
                temp.ymin = y+1;
                x = temp.xmin;
                ++y;
            }
            else if (y == temp.ymax){
                temp.xmin = x+1;
                y = temp.ymin;
                ++x;
            }
            else {++x,++y;} // 情况4:继续查找
        } 
    }
    return 0;
}

3.复杂度
时间复杂度:采用二分查找时为O(lgn)、采用顺序查找时为O(nlgn)
空间复杂度:O(1)

四、左下/右上元素移动法

1.分析:依次对每一行(列)都执行折半查找
2.代码

// Solution 4: 运行时间:10ms 占用内存:1504k
bool Find_3(int target, vector<vector<int> > array) {
    if (array.empty())return false;
    if (array[0].empty())return false;

    int length = array.size();
    int row=0, col=length-1;
    while (row<length && col>-1){
        if (array[row][col]==target) return true;
        if (array[row][col]>target) --col;
        else ++row;
    }
    return false;
} 

3.复杂度
时间复杂度:O(n)
空间复杂度:O(1)