【问题标题】:Binary Search in Array数组中的二分查找
【发布时间】:2010-09-19 22:45:07
【问题描述】:

我将如何仅使用数组来实现二进制搜索?

【问题讨论】:

  • 查看link
  • 对我来说非常有意义。除了数组,你还可以使用二叉树。

标签: algorithm arrays language-agnostic search binary-search


【解决方案1】:

确保您的数组已排序,因为这是二进制搜索的关键。

任何索引/随机访问的数据结构都可以进行二分搜索。所以当你说使用“只是一个数组”时,我会说数组是使用二进制搜索的最基本/常见的数据结构。

您可以递归(最简单)或迭代地执行此操作。二进制搜索的时间复杂度是 O(log N),这比在 O(N) 处检查每个元素的线性搜索要快得多。以下是来自Wikipedia: Binary Search Algorithm 的一些示例:

递归:

BinarySearch(A[0..N-1], value, low, high) {  
    if (high < low)  
        return -1 // not found  
    mid = low + ((high - low) / 2) 
    if (A[mid] > value)  
        return BinarySearch(A, value, low, mid-1)  
    else if (A[mid] < value)  
        return BinarySearch(A, value, mid+1, high)  
    else
       return mid // found
    }

迭代:

  BinarySearch(A[0..N-1], value) {
   low = 0
   high = N - 1
   while (low <= high) {
       mid = low + ((high - low) / 2)
       if (A[mid] > value)
           high = mid - 1
       else if (A[mid] < value)
           low = mid + 1
       else
           return mid // found
   }
   return -1 // not found
}

【讨论】:

  • 在计算 mid 时记得注意溢出。 (见googleresearch.blogspot.com/2006/06/…
  • @Firas Assad,谢谢,更新了代码以显示与防止溢出相关的修复
  • mid = low + ((high - low) / 2)mid = (low + high) / 2 相同。不确定是否使用整数除法,但该算法仍然适用于两个公式。
  • @NiklasR 除非low+high 产生整数溢出。
  • 中值 =(高 - 低)/2 可以吗?
【解决方案2】:

Javascript 中的二分搜索 (ES6)

(如果有人需要)

自下而上:

function binarySearch (arr, val) {
    let start = 0;
    let end = arr.length - 1;
    let mid;

    while (start <= end) {
        mid = Math.floor((start + end) / 2);

        if (arr[mid] === val) {
            return mid;
        }
        if (val < arr[mid]) {
            end = mid - 1;
        } else {
            start = mid + 1;
        }
    }
    return -1;
}

递归

function binarySearch(arr, val, start = 0, end = arr.length - 1) {
    const mid = Math.floor((start + end) / 2);

    if (val === arr[mid]) {
        return mid;
    }
    if (start >= end) {
        return -1;
    }
    return val < arr[mid]
        ? binarySearch(arr, val, start, mid - 1)
        : binarySearch(arr, val, mid + 1, end);
}

【讨论】:

    【解决方案3】:

    这取决于您是否在数组中重复了一个元素,以及您是否关心多个发现。我在这个实现中有两种方法。其中一个只返回第一个发现,但另一个返回键的所有发现。

    import java.util.Arrays;
    
    public class BinarySearchExample {
    
        //Find one occurrence
        public static int indexOf(int[] a, int key) {
            int lo = 0;
            int hi = a.length - 1;
            while (lo <= hi) {
                // Key is in a[lo..hi] or not present.
                int mid = lo + (hi - lo) / 2;
                if      (key < a[mid]) hi = mid - 1;
                else if (key > a[mid]) lo = mid + 1;
                else return mid;
            }
            return -1;
        }
    
        //Find all occurrence
        public static void PrintIndicesForValue(int[] numbers, int target) {
            if (numbers == null)
                return;
    
            int low = 0, high = numbers.length - 1;
            // get the start index of target number
            int startIndex = -1;
            while (low <= high) {
                int mid = (high - low) / 2 + low;
                if (numbers[mid] > target) {
                    high = mid - 1;
                } else if (numbers[mid] == target) {
                    startIndex = mid;
                    high = mid - 1;
                } else
                    low = mid + 1;
            }
    
            // get the end index of target number
            int endIndex = -1;
            low = 0;
            high = numbers.length - 1;
            while (low <= high) {
                int mid = (high - low) / 2 + low;
                if (numbers[mid] > target) {
                    high = mid - 1;
                } else if (numbers[mid] == target) {
                    endIndex = mid;
                    low = mid + 1;
                } else
                    low = mid + 1;
            }
    
            if (startIndex != -1 && endIndex != -1){
                System.out.print("All: ");
                for(int i=0; i+startIndex<=endIndex;i++){
                    if(i>0)
                        System.out.print(',');
                    System.out.print(i+startIndex);
                }
            }
        }
    
        public static void main(String[] args) {
    
            // read the integers from a file
            int[] arr = {23,34,12,24,266,1,3,66,78,93,22,24,25,27};
            Boolean[] arrFlag = new Boolean[arr.length];
            Arrays.fill(arrFlag,false);
    
            // sort the array
            Arrays.sort(arr);
    
            //Search
            System.out.print("Array: ");
            for(int i=0; i<arr.length; i++)
                if(i != arr.length-1){
                    System.out.print(arr[i]+",");
                }else{
                    System.out.print(arr[i]);
                }
    
            System.out.println("\nOnly one: "+indexOf(arr,24));
            PrintIndicesForValue(arr,24);
    
        }
    
    }
    

    欲了解更多信息,请访问https://github.com/m-vahidalizadeh/foundations/blob/master/src/algorithms/BinarySearchExample.java。希望对你有帮助。

    【讨论】:

      【解决方案4】:

      单对比版快速简洁

      int bsearch_double(const double a[], int n, double v) {
        int low = 0, mid;
        while (n - low > 1) {
          mid = low + (n - low) / 2;
          if (v < a[mid]) n   = mid;
          else            low = mid;
        }
        return (low < n && a[low] == v) ? low : -1;
      }
      

      【讨论】:

      • 这是一个if 声明,如果这是一个要求的话。
      • 最后你也应该检查 a[n]。否则找不到例如1 个来自 {0,1}。
      • @jarno 与 C 的惯例一样,n 是(从 0 开始的)数组的长度,因此 a[n] 无效。同时bsearch_double((double[]){0,1}, 2, 1)有效,返回1
      【解决方案5】:

      用Java实现了下面的代码,简单快速 /** * 使用递归的二分搜索 * @作者阿沙达 * */ 公共类 BinSearch {

        /**
         * Simplistic BInary Search using Recursion
         * @param arr
         * @param low
         * @param high
         * @param num
         * @return int
         */
        public int binSearch(int []arr,int low,int high,int num)
        {
          int mid=low+high/2;
          if(num >arr[high] || num <arr[low])
          {
            return -1;
          }
      
          while(low<high)
          {
            if(num==arr[mid])
            {
              return mid;
      
            }
            else  if(num<arr[mid])
            {
             return  binSearch(arr,low,high-1, num);
            }
      
            else  if(num>arr[mid])
            {
              return binSearch(arr,low+1,high, num);
            }
      
          }//end of while
      
          return -1;
        }
      
        public static void main(String args[])
        {
          int arr[]= {2,4,6,8,10};
          BinSearch s=new BinSearch();
          int n=s.binSearch(arr, 0, arr.length-1, 10);
          String result= n>1?"Number found at "+n:"Number not found";
          System.out.println(result);
        }
      }
      

      【讨论】:

      • s/int mid=low+high/2;/int mid=(low+high)/2;/s/return binSearch(arr,low,high-1, num);/return binSearch(arr,low,mid-1, num);/s/return binSearch(arr,low+1,high, num);/return binSearch(arr,mid+1,high, num);/
      【解决方案6】:

      这是python编程语言的简单解决方案:

      def bin(search, h, l):
          mid = (h+l)//2
          if m[mid] == search:
              return mid
          else:
              if l == h:
                  return -1
              elif search > m[mid]:
                  l = mid+1
                  return bin(search, h, l)
              elif search < m[mid]:
                  h = mid-1
                  return bin(search, h, l)
          
      m = [1,2,3,4,5,6,7,8]
      tot = len(m)
      print(bin(10, len(m)-1, 0))
      

      流程如下:

      • 获取中点
      • 如果中点 == 搜索返回中点
      • 否则,如果高点和低点相同,则返回 -1
      • 如果搜索值大于中点,则将中点+1设为较低值
      • 如果搜索值小于中点,则将中点 1 设为较高值

      【讨论】:

        【解决方案7】:

        二分查找的短循环:

        function search( nums, target){    
            for(let mid,look,p=[0,,nums.length-1]; p[0]<=p[2]; p[look+1]=mid-look){
                mid = (p[0] + p[2])>>>1
                look = Math.sign(nums[mid]-target)
                if(!look) 
                    return mid
            }
            return -1
        }
        

        想法正在替换:

        if(nums[mid]==target)
            return mid
        else if(nums[mid]>target)
            right = mid - 1
        else //here must nums[mid]<target
            left  = mid + 1
        

        如果观察的话,会有更多的默契(并且可能需要更少的计算) 前者相等:

        switch(dir=Math.sign(nums[mid]-target)){
            case -1: left  = mid - dir;break;
            case  0: return  mid
            case  1: right = mid - dir;break;
        }
        

        所以如果左、中、右变量按顺序排列,我们可以在 C 指针意义上对所有这些变量分别抛出 &mid[-1,0,1] :

        dir=Math.sign(nums[mid]-target)
        &mid[dir] = mid - dir
        

        现在我们得到了循环体,所以我们可以构造二分搜索:

        while(dir && left <= right){
            mid = (left + right)>>>2
            dir=Math.sign(nums[mid]-target)
            &mid[dir] = mid - dir
        }
        

        我们刚刚:

        return [dir,mid]
        

        具有语义

        for dir == -1 then nums[mid]<target<nums[mid+1] // if nums[mid+1 ] in start seaching domain
        for dir ==  0 then mid is place of target in array 
        for dir ==  1 then nums[mid-1]<target<nums[mid] // if nums[mid-1 ] in start seaching domain        
        

        所以在一些更人性化的伪代码 javascript 函数中等效:

            function search( nums, target){
                let dir=!0,[left, mid, right]=[0, , nums.length-1]
                while(dir && left <=right){
                    mid = (left + right)>>>1
                    dir = Math.sign(nums[mid]-target)
                    &mid[dir]=mid - dir
                 }
                 return [dir, mid]
            }
        

        对于 js sintax,我们需要使用 q={'-1':0,1:nums.length-1} 其中左名称为 q[-1],mid 为 q[0],右为 q[1]或者对于所有 3 的 q 是 q[dir]

        对于从 0 开始的数组索引或相同:

        我们可以使用 p=[0,,nums.length-1] 其中左边是 p[0] 的昵称,中间是 p[1],右边是 p[2],这三个都是 p [1+目录]

        。 :)

        【讨论】:

        • 感谢您提供答案。您能否编辑您的答案以包括对您的代码的解释?这将有助于未来的读者更好地了解正在发生的事情,尤其是那些刚接触该语言并难以理解概念的社区成员。
        【解决方案8】:

        假设数组已排序,这是一个具有 O(log n) 运行时复杂度的 Pythonic 答案:

        def binary_search(nums: List[int], target: int) -> int:
            n = len(nums) - 1
            left = 0
            right = n
            
            
            while left <= right:
                mid = left + (right - left) // 2
                if target == nums[mid]:
                    return mid
                elif target < nums[mid]:
                    right = mid - 1
                else:
                    left = mid + 1
                
            
            return -1
        

        【讨论】:

          猜你喜欢
          • 2023-03-12
          • 2011-06-08
          • 1970-01-01
          • 2011-12-25
          相关资源
          最近更新 更多