行、列递增的二维数组数字查找

二维数组四分查找

问题:
一个二维数组,但行递增、单列递增,编写程序,在数组中查找某个数字(key),要求时间复杂度小于O(N)。
示例数组
1 3 4
2 4 5
4 5 6

分析:
对这个问题,首先想到的就是二分查找,对一位数组效率最高的查找方法,能不能用类似的方法来处理这个问题呢?
先看一下二分查找
这里写图片描述
根据mid与key的关系确定下次查找的范围或输出结束
那么二维数组呢
_ 绿色表示比较的数字位置
_ 浅灰色区域表示该区域小于key
_ 深灰色区域表示该区域大于key
_ 浅蓝色表示key可能出现的区域
_ 深蓝色表示下次搜索的区域
这里写图片描述
中心值大于key的情况与小于时类似

初始代码
(注释见优化后的代码)

#include<stdio.h>

void act_search(int a[][3], int key, int left, int right, int top, int bot)
{
    if ((left <= right) && (top <= bot))
    {
        int mid_row = (top + bot) >> 1;
        int mid_col = (left + right) >> 1;
        if (a[mid_row][mid_col] != key)
        {
            if (a[mid_row][mid_col] > key)
            {
                if (a[top][mid_col] >= key)
                {
                    act_search(a, key, left, mid_col - 1, top, mid_row - 1);
                    if (a[top][mid_col] == key)
                    {
                        printf("a[%d][%d] = %d\n", top, mid_col, a[top][mid_col]);
                    }
                }
                else//a[top][mid_col] < key
                {
                    act_search(a, key, mid_col, right, top, mid_row - 1);
                }

                if (a[mid_row][left] >= key)
                {
                    if (a[top][mid_col] <= key)
                    {
                        act_search(a, key, left, mid_col - 1, top, mid_row - 1);
                    }
                    if (a[mid_row][left] == key)
                    {
                        printf("a[%d][%d] = %d\n", top, mid_col, a[top][mid_col]);
                    }
                }
                else//a[mid_row][left]<key
                {
                    act_search(a, key, left, mid_col - 1, mid_row, bot);
                }
            }
            else    //(a[mid_row][mid_col] < key)
            {
                if (a[mid_row][right] <= key)
                {
                    act_search(a, key, mid_col + 1, right, mid_row + 1, bot);
                    if (a[mid_row][bot] == key)
                    {
                        printf("a[%d][%d] = %d\n", mid_col, right, a[mid_col][right]);
                    }
                }
                else   //a[mid_row][bot] > key
                {
                    act_search(a, key, mid_col + 1, right, top, mid_row);
                }

                if (a[bot][mid_col] <= key)
                {
                    if (a[mid_row][right] > key)
                    {
                        act_search(a, key, mid_col + 1, right, mid_row + 1, bot);
                    }
                    if (a[bot][mid_col] == key)
                    {
                        printf("a[%d][%d] = %d\n", bot, mid_col, a[bot][mid_col]);
                    }
                }
                else  //a[bot][mid_col] > key
                {
                    act_search(a, key, left, mid_col, mid_row + 1, bot);
                }
            }
        }
        else     //a[mid_row][mid_col] != key
        {
            printf("a[%d][%d] = %d\n", mid_row, mid_col, a[mid_row][mid_col]);
            act_search(a, key, left, mid_col - 1, mid_row + 1, bot);
            act_search(a, key, mid_col + 1, right, top, mid_row - 1);
        }
    }
}

void search(int a[][3], int key, int rol, int cow)
{
    int left = 0;
    int right = cow - 1;
    int top = 0;
    int bot = rol - 1;
    act_search(a, key, left, right, top, bot);
}

int main()
{
    int a[][3]={{ 1, 3, 4 },{2, 4, 5},{4, 5, 6}};
    int rol = sizeof(a) / sizeof(a[0]);
    int cow = sizeof(a[0]) / sizeof(a[0][0]);
    int key = 4;
    search(a, key, rol, cow);
    return 0;
}

优化
有一个很大的问题
这里写图片描述
在函数内部写死了列数让函数局限性变得非常大
而事实上我们知道,二维数组在内存中的存储是连续的,二维数组可以用一维数组来表示和处理,p为首元素指针,COL为二维数组列数,则a[row][col]等价于p[row*COL+col],那么search函数只需接收数组首元素地址而不需接收整个数组,act_search也只需接收首元素地址,另外加列数就好了。然后再将二维数组一维表示就OK了。
这里写图片描述

#include<stdio.h>

void act_search(int *a, int key, int left, int right, int top, int bot, int col)
{
    if ((left <= right) && (top <= bot))  //判别条件,左值不大于右值,顶值不大于底值
    {
        int mid_row = (top + bot) >> 1;  //列中心
        int mid_col = (left + right) >> 1;  //行中心
        if (a[mid_row*col+mid_col] != key)  //数组中心不等于key
        {
            if (a[mid_row*col + mid_col] > key)  // 数组中心大于key,排除右下区
            {
                if (a[top*col + mid_col] >= key)    //顶行中心大于等于key,排除右上区域,剩余左上区
                {
                    act_search(a, key, left, mid_col - 1, top, mid_row - 1, col);  搜索左上区
                    if (a[top*col + mid_col] == key)    //找到key则输出
                    {
                        printf("a[%d][%d] = %d\n", top, mid_col, a[top*col + mid_col]);
                    }
                }
                else   //a[top][mid_col] < key  //顶行中心小于key,搜索包括所在列的右上区
                {
                    act_search(a, key, mid_col, right, top, mid_row - 1, col);
                }

                if (a[mid_row*col + left] >= key)   //左边中心不小于key,排除左下区
                {
                    if (a[top*col + mid_col] <= key)   //左上区之前没搜索就搜索
                    {
                        act_search(a, key, left, mid_col - 1, top, mid_row - 1, col);
                    }
                    if (a[mid_row*col + left] == key)
                    {
                        printf("a[%d][%d] = %d\n", top, mid_col, a[top*col + mid_col]);
                    }
                }
                else   //a[mid_row][left]<key  排除左上区,搜索包括所在行的左下区
                {
                    act_search(a, key, left, mid_col - 1, mid_row, bot, col);
                }
            }
            else    //(a[mid_row][mid_col] < key)   中心值小于key,排除左上区
            {
                if (a[mid_row*col + right] <= key)    //右边中心值不大于key,排除右上区,检索右下区
                {
                    act_search(a, key, mid_col + 1, right, mid_row + 1, bot, col);
                    if (a[mid_row*col + bot] == key)
                    {
                        printf("a[%d][%d] = %d/n", mid_col, right, a[mid_col*col + right]);
                    }
                }
                else   //a[mid_row][bot] > key 右上区可能有可以,检索
                {
                    act_search(a, key, mid_col + 1, right, top, mid_row, col);
                }

                if (a[bot*col + mid_col] <= key)  // 底行中心值不大于key排除左下区
                {
                    if (a[mid_row*col + right] > key)   //右下区之前未检索就检索
                    {
                        act_search(a, key, mid_col + 1, right, mid_row + 1, bot, col);
                    }
                    if (a[bot*col + mid_col] == key)
                    {
                        printf("a[%d][%d] = %d\n", bot, mid_col, a[bot*col + mid_col]);
                    }
                }
                else  //a[bot][mid_col] > key  左下下区可能存在key
                {
                    act_search(a, key, left, mid_col, mid_row + 1, bot, col);
                }
            }
        }
        else  //a[mid_row][mid_col] == key  中心值为key,排除左上右下区,搜索不包括所在行列的右上左下区
        {
            printf("a[%d][%d] = %d\n", mid_row, mid_col, a[mid_row*col + mid_col]);
            act_search(a, key, left, mid_col - 1, mid_row + 1, bot, col);
            act_search(a, key, mid_col + 1, right, top, mid_row - 1, col);
        }
    }
}

void search(int *a, int key, int rol, int cow)
{
    int left = 0;           //左边界列下标
    int right = cow - 1;    //右边界列下标 
    int top = 0;            //上边界行下标
    int bot = rol - 1;      //下边界行下标
    act_search(a, key, left, right, top, bot, cow);   //注意传值列数cow
}

int main()
{
    int a[][3] = { { 1, 3, 4 }, { 2, 4, 5 }, { 4, 5, 6 } };   //初始化数组
    int rol = sizeof(a) / sizeof(a[0]);                       //求行数
    int cow = sizeof(a[0]) / sizeof(a[0][0]);                 //求列数
    int key = 4;                                              //设要找的数
    search(a[0], key, rol, cow);                             // 传参,搜索
    return 0;
}

运行结果相同
这里写图片描述
测试下稍复杂的数组
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值