行、列遞增的二維數組數字查找

二維數組四分查找

問題:
一個二維數組,但行遞增、單列遞增,編寫程序,在數組中查找某個數字(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;
}

運行結果相同
這裏寫圖片描述
測試下稍複雜的數組
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章