簡體   English   中英

無限遞歸:二進制搜索和斷言

[英]Infinite recursion: binary search & asserts

我正在用C語言編寫二進制搜索的實現,並且在沒有明顯(對我而言)原因的情況下獲得無限遞歸。 這是我的代碼:

/*Orchestrate program*/
int main(int argc, char* argv){
    int array[] = {1,2,3,3,3,6,7,8,9,9,20,21,22};
    int length = 13;
    int key = 23;
    binary_search(key, array, 0, length - 1);
    return 0;
}

int binary_search(int key, int array[], int first_index, int last_index){
    int middle;
    middle = (first_index + last_index)/2;
    if (first_index == last_index){
        if (array[middle] == key){
            printf("%d has been found at position %d\n", key, middle+1);
        }
        printf("item not found");
    }
    else if (key > array[middle]){
        binary_search(key, array, middle, last_index);
    }
    else if (key < array[middle]){
        binary_search(key, array, first_index, middle);
    }
}

根據main中密鑰的值,我猜問題出在第一個問題上,但是我不確定為什么。 如果我要刪除first_index == last_index行,則該算法可以正常工作,但僅當該項目位於數組中時才有效。 如果該項不在數組中,我自然會得到無限遞歸。

另外,我嘗試通過刪除first_index == last_index行並放置return -1;來解決此問題return -1; 在函數的末尾,但是我遇到的問題與我現在遇到的問題相同。

編輯:

匯總從幾個不同用戶那里獲得的建議,我得出了以下解決方案(由於一個錯誤和未套用的決定而被修正):

void binary_search(int key, int array[], int first_index, int last_index){
    int middle;
    middle = (first_index + last_index)/2;

    if (array[middle] == key){
        printf("%d has been found at position %d\n", key, middle+1);
    }
    if (first_index == last_index){
        printf("item not found");
    }
    else if (key > array[middle]){
        binary_search(key, array, middle + 1, last_index);
    }
    else if (key < array[middle]){
        binary_search(key, array, first_index, middle - 1);
    }
}

我有一個后續問題:是否可以使用斷言來幫助我自己找到解決方案? (我只是在學習斷言,所以我想知道在哪里可以應用它們)

您搜索的數組范圍越來越小。 您的陣列中的所有部件均包含在內。

遞歸的基本情況是:如果范圍為空,則找不到鍵。 或者,在代碼中:

if (first_index > last_index){
    printf("Not found\n");
}

僅在確定范圍不為空之后,才應計算並比較范圍的中間元素。 在這種情況下,您將獲得三個結果:

  • 中間的要素是關鍵:賓果游戲!
  • 中間元素小於鍵:搜索數組的右半部分,並排除我們已經檢查過的中間元素。
  • 中間元素大於關鍵點:同上,但左半部分。

全部放在一起:

void binary_search(int key, int array[], int first_index, int last_index)
{
    if (first_index > last_index){
        printf("Not found\n");
    } else {
        int middle = (first_index + last_index) / 2;

        if (array[middle] == key) printf("%d at index %d\n", key, middle);

        if (key > array[middle]){
            binary_search(key, array, middle + 1, last_index);
        } else {
            binary_search(key, array, first_index, middle - 1);
        }
    }
}

此功能還有兩點困擾我:

  • 打印索引的功能幾乎沒有實際用途。 打印應該由客戶代碼完成,即由調用函數的代碼完成。 而是返回找到的索引或“找不到”的特殊值。
  • 該范圍具有包容性范圍。 那不是很像C。 在C語言中,范圍通常由包含性下限和排除性上限來描述。 這就是數組索引和for循環的工作方式。 遵循此約定意味着您的客戶端代碼不必執行笨拙的length - 1計算。

因此,這是一個變體,如果鍵不在數組中,則返回索引或-1:

int binary_search1(int key, int array[], int first_index, int last_index)
{
    if (first_index < last_index){
        int middle = (first_index + last_index) / 2;

        if (array[middle] == key) return middle;

        if (key > array[middle]){
            return binary_search1(key, array, middle + 1, last_index);
        } else {
            return binary_search1(key, array, first_index, middle);
        }
    }

    return -1;
}

並使用以下命令進行測試:

int main()
{
    int arr[6] = {3, 4, 6, 8, 12, 13};
    int i;

    for (i = 0; i < 20; i++) {
        int ix = binary_search(i, arr, 0, 6);

        if (ix < 0) {
            printf("%d not found.\n", i);
        } else {
            printf("%d at index %d.\n", i, ix);
        }
    }

    return 0;
}

請注意,原始數組具有重復的條目。 可以,但是您將獲得任何重復值的索引,而不必是第一個。

您的函數應如下所示:

void binary_search(int key, int array[], int first_index, int last_index){
    int middle;
    middle = (first_index + last_index)/2;
    if (array[middle] == key){
        printf("%d has been found at position %d\n", key, middle+1);
    }
    else if (first_index == last_index) {
         printf("item not found");
    }
    else if (key > array[middle]){
        binary_search(key, array, middle + 1, last_index);
    }
    else {
        //assert (key < array[middle]); // feel free to uncomment this one and include the assert library if you want
        binary_search(key, array, first_index, middle - 1);
    }
}

換句話說,在遞歸調用中適當增加或減少中間值。

這很重要,因為,例如,當您縮小到搜索大小2且中間是您的第一個元素時,那么實際上您就沒有在遞歸調用中更改數組的維數。

我也將您的函數更改為void因為您什么都不返回。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM