簡體   English   中英

為什么這種二分搜索實現會導致 Ruby 中的堆棧溢出,但不會導致 Java 中的堆棧溢出?

[英]Why does this binary search implementation cause stack overflow in Ruby but not Java?

Ruby

def binary_search(arr, l, r, x) 
  if r >= 1 then
    mid = l + (r - 1) / 2

    if arr[mid] == x then
      return mid
    end

    if arr[mid] > x then
      return binary_search(arr, l, mid - 1, x)
    end
    return binary_search(arr, mid + 1, r, x)
  end

  return -1
end

Java

int binarySearch(int arr[], int l, int r, int x) 
{ 
    if (r >= l) { 
        int mid = l + (r - l) / 2; 

        // If the element is present at the 
        // middle itself 
        if (arr[mid] == x) 
            return mid; 

        // If element is smaller than mid, then 
        // it can only be present in left subarray 
        if (arr[mid] > x) 
            return binarySearch(arr, l, mid - 1, x); 

        // Else the element can only be present 
        // in right subarray 
        return binarySearch(arr, mid + 1, r, x); 
    } 

    // We reach here when element is not present 
    // in array 
    return -1; 
} 

當 x(目標元素)位於排序數組的右半部分時,發生堆棧溢出,我在 Ruby 中收到此錯誤

SystemStackError (stack level too deep)

為什么這發生在 ruby 而不是 java? 我正在 irb 中運行程序。 java 實現直接來自這里https://www.geeksforgeeks.org/binary-search/

首先,讓我們重構您的代碼,使其更加清晰。 注意:此代碼中的行為變化為零,主要是通過一些重構重新格式化。

def binary_search(arr, l, r, x) 
  return -1 unless r >= 1

  mid = l + (r - 1) / 2

  case arr[mid] <=> x
  when  0 then mid
  when -1 then binary_search(arr, mid + 1, r, x)
  when  1 then binary_search(arr, l, mid - 1, x)
  end
end

二進制搜索有兩個主要問題。

首先是“找不到元素”的終止條件。 二進制搜索的工作方式是將左側“柵欄”移動到右側或將右側“柵欄”移動到左側,具體取決於所需元素在數組中的位置。 這樣,搜索區域變得越來越小。

現在,當兩個“柵欄”相遇(甚至相互擦肩而過)時,就沒有更多的“搜索區域”了,這意味着沒有找到該元素。 However, you are not checking whether the two "fences" meet ( l == r ) or even overtake each other ( l >= r ), you are checking whether r meets the left boundary of the original array ( r == 1 ) .

這意味着您將有很多很多無用的遞歸,直到您最終在找不到元素時放棄。

其實也沒有那么簡單,因為當lr通過時,你的中點計算也是錯誤的,因為現在突然r小於l ,或者換句話說,右柵欄在左邊左側柵欄的一側!

好吧......除了你的中點計算無論如何都被打破了。 例如,如果l = 10r = 12 ,兩者之間的中點顯然是mid = 11 ,但根據你的公式,它是:

10 + (12 - 1) / 2
10 + (  11  ) / 2
10 + 5
15

哎呀! 左右之間的中間實際上是向右的右側,這意味着,根據數組中的位置,您的搜索值實際上是在使搜索區域更大而不是更小:您正在將r回對,這樣您就可以再次搜索您已經搜索過的數組的一部分。 這意味着您在 Ruby 版本中需要比在 Java 版本中更多的遞歸。

lr之間的當前距離公式為r - l 現在我們需要一半的距離,即(r - l) / 2 如果我們想找到lr之間的中間點,我們需要從l r的一半距離,所以我們需要將上述距離添加到ll + (r - l) / 2

這是固定代碼的樣子:

def binary_search(arr, l, r, x) 
  return -1 if r < l

  mid = l + (r - l) / 2

  case arr[mid] <=> x
  when  0 then mid
  when -1 then binary_search(arr, mid + 1, r, x)
  when  1 then binary_search(arr, l, mid - 1, x)
  end
end

暫無
暫無

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

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