二维数组的二分查找 解题报告

【题目大意】

在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

【分析】

排好序的一维数组二分查找的效率是O(logN),本例中是二维数组,但是有序的性质可以使得在局部,即一维状况下继续使用二分查找,通过合理的组织查找区间可以再O(NlogM)的时间内找到是否存在该值key。

记二维数组(n行m列)为 

A11A12……A1n
……………………
An1An2……Anm

则满足 :

A[i][1] <= A[i][2] <= ... <= A[i][m] ( i ∈ [1,n] )

A[1][j] <= A[2][j] <= ... <= A[n][j]  ( j ∈ [1,m] )

设待查找值可能在第i行,则必有A[i][1] <= key <= A[i][m]

①查找key可能在的行,由于在每一列都递增,故可能的行必然连在一起,即为一个区间[top,down],在这个区间有A[i][1] <= key <= A[i][m] ( i ∈ [top,down] )

    通过二分查找可得出满足要求的[top,down]

②从第top行开始查找值key,一行一行往下搜,知道down的下一行或者搜到结果。采用如下搜法:

 即当前在第cur行,在cur行的搜索区间为[left,right]( A[cur][left] <= key <= A[cur][right] )。

找到cur行在[left,right]区间中<=key的最大值的位置pos,若其==key,则返回找到,否则key不可能在本行,只可能在下面若干行中。

比较 A[cur+1][pos] 和 key 

 若其>key,则搜索区间为第cur+1行的[left,pos-1]。

若其 <key,则搜索区间为第cur+1行的[pos+1,key]。

否则找到。

如此往下搜,直到找到或者cur==down+1终止。

  由上可知复杂度为O(nlogn)。

给个oj的链接:点击打开链接

附个代码:

#include <cstdlib>
#include <string.h>
#include <vector>
#include <set>
#include <iostream>
#include <stdio.h>
#include <queue>
 
using namespace std;
 
const int maxn = 1024 ;
int matrix[maxn][maxn] , n , m , key ;
 
inline bool get(int &t)
{
    bool flag = 0 ;
    char c;
    while(!isdigit(c = getchar())&&c!='-') if( c == -1 ) break ;
    if( c == -1 ) return 0 ;
    if(c=='-') flag = 1 , t = 0 ;
    else t = c ^ 48;
    while(isdigit(c = getchar()))    t = (t << 1) + (t << 3) + (c ^ 48) ;
    if(flag) t = -t ;
    return 1 ;
}
 
/*返回第一个<=key的元素*/
int bs(int array[],int l,int r)
{
    int mid ;
    while (l<=r)
    {
        mid = (l+r) >> 1 ;
        if( array[mid] > key ) r = mid-1 ;
        else l = mid+1 ;
    }
    return r ;
}
 
bool query()
{
    int topRow , downRow , i , j , l , r , mid , temp ;
    l = 0 ; r = n-1 ;
    /*找第一个<=key的*/
    while (l<=r)
    {
        mid = (l+r) >> 1 ;
        if( matrix[mid][0] > key ) r = mid-1 ;
        else l = mid+1 ;
    }
    //[0,r]
    if( r == -1 ) return false ;
    if( matrix[r][0] == key ) return true ;
    temp = r ;
 
    l = 0 ; r = n-1 ;
    /*找第一个>=key的*/
    while (l<=r)
    {
        mid = (l+r) >> 1 ;
        if( matrix[mid][m-1] >= key ) r = mid-1 ;
        else l = mid+1 ;
    }
    //[l,n-1]
    if( l == n ) return false ;
    if( matrix[l][m-1] == key ) return true ;
     
    r = temp ;
    /*[0,r]和[l,n-1]无交集*/
    if( r < l ) return false ;
 
    topRow = l ;
    downRow = r ;
    /*interval is[topRow,downRow]*/
     
    l = 0 ; r = m-1 ;
    for ( i = topRow ; i <= downRow ; i++)
    {
        j = bs(matrix[i],l,r);
        if( matrix[i][j] == key ) return true ;
        if( i < downRow )
        {
            if( matrix[i+1][j] > key ) r = j ;
            else if( matrix[i+1][j] < key ) l = j ;
            else return true ;
        }
    }
 
    return false ;
}
 
int main()
{
    int i , j , k ;
    while(get(n))
    {
        get(m);
        get(key);
        for ( i = 0 ; i < n ; i++)
            for( j = 0 ; j < m ; j++)
                get(matrix[i][j]);
        printf("%s\n",query()?"Yes":"No");
    }
}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值