Algorithm 二进制搜索小于或等于搜索值的最近值

Algorithm 二进制搜索小于或等于搜索值的最近值,algorithm,search,binary-search,Algorithm,Search,Binary Search,我正在尝试编写一个算法,用于查找排序数组中小于或等于搜索值的最近值的索引。在数组[10,20,30]的示例中,以下搜索值应输出这些索引: 搜索值:9,索引:-1 搜索值:10,索引:0 搜索值:28,索引:1 搜索值:55555,索引:2 我想对对数运行时使用二进制搜索。我有一个C-esque伪代码的算法,但它有3个基本情况。这3个基本情况是否可以压缩为1以获得更优雅的解决方案 int function indexOfClosestLesser(array, searchValue, start

我正在尝试编写一个算法,用于查找排序数组中小于或等于搜索值的最近值的索引。在数组[10,20,30]的示例中,以下搜索值应输出这些索引:

  • 搜索值:9,索引:-1
  • 搜索值:10,索引:0
  • 搜索值:28,索引:1
  • 搜索值:55555,索引:2
  • 我想对对数运行时使用二进制搜索。我有一个C-esque伪代码的算法,但它有3个基本情况。这3个基本情况是否可以压缩为1以获得更优雅的解决方案

    int function indexOfClosestLesser(array, searchValue, startIndex, endIndex) {
      if (startIndex == endIndex) {
        if (searchValue >= array[startIndex]) {
          return startIndex;
        } else {
          return -1;
        }
      }
    
      // In the simplistic case of searching for 2 in [0, 2], the midIndex
      // is always 0 due to int truncation. These checks are to avoid recursing
      // infinitely from index 0 to index 1. 
      if (startIndex == endIndex - 1) {
        if (searchValue >= array[endIndex]) {
          return endIndex;
        } else if (searchValue >= array[startIndex]) {
          return startIndex;
        } else {
          return -1;
        }
      }
    
      // In normal binary search, this would be the only base case
      if (startIndex < endIndex) {
        return -1;
      }
    
      int midIndex = endIndex / 2 + startIndex / 2;
      int midValue = array[midIndex];
    
      if (midValue > searchValue) {
        return indexOfClosestLesser(array, searchValue, startIndex, midIndex - 1);
      } else if (searchValue >= midValue) {
        // Unlike normal binary search, we don't start on midIndex + 1.
        // We're not sure whether the midValue can be excluded yet
        return indexOfClosestLesser(array, searchValue, midIndex, endIndex);
      }
    }
    
    int函数indexOfClosestLesser(数组、searchValue、startIndex、endIndex){
    if(startIndex==endIndex){
    if(searchValue>=数组[startIndex]){
    返回startIndex;
    }否则{
    返回-1;
    }
    }
    //在[0,2]中搜索2的简单情况下,midIndex
    //由于int截断,始终为0。这些检查是为了避免递归
    //从索引0到索引1无限远。
    if(startIndex==endIndex-1){
    if(searchValue>=数组[endIndex]){
    返回端索引;
    }else if(searchValue>=数组[startIndex]){
    返回startIndex;
    }否则{
    返回-1;
    }
    }
    //在普通的二进制搜索中,这是唯一的基本情况
    if(开始索引<结束索引){
    返回-1;
    }
    int midIndex=endIndex/2+startIndex/2;
    int midValue=数组[midIndex];
    如果(中值>搜索值){
    返回indexOfClosestLesser(数组、searchValue、startIndex、midIndex-1);
    }else if(searchValue>=中值){
    //与普通的二进制搜索不同,我们不从midIndex+1开始。
    //我们还不确定是否可以排除中值
    返回indexOfClosestLesser(数组、searchValue、midIndex、endIndex);
    }
    }
    
    基于您的递归方法,我建议使用以下
    c++
    代码片段,将不同情况的数量减少一点:

    int search(int *array, int start_idx, int end_idx, int search_val) {
    
       if( start_idx == end_idx )
          return array[start_idx] <= search_val ? start_idx : -1;
    
       int mid_idx = start_idx + (end_idx - start_idx) / 2;
    
       if( search_val < array[mid_idx] )
          return search( array, start_idx, mid_idx, search_val );
    
       int ret = search( array, mid_idx+1, end_idx, search_val );
       return ret == -1 ? mid_idx : ret;
    }
    

    这是一个PHP版本,基于user0815的答案

    使其适应于一个函数,而不仅仅是一个数组,并通过避免对$mid_idx进行两次求值而使其更加高效

    function binarySearchLessOrEqual($start_idx, $end_idx, $search_val, $valueFunction)
    {
        //N.B. If the start index is bigger or equal to the end index, we've reached the end!
        if( $start_idx >= $end_idx )
        {
            return $valueFunction($end_idx) <= $search_val ? $end_idx : -1;
        }
    
        $mid_idx = intval($start_idx + ($end_idx - $start_idx) / 2);
    
        if ( $valueFunction($mid_idx) > $search_val )  //If the function is too big, we search in the bottom half
        {
            return binarySearchLessOrEqual( $start_idx, $mid_idx-1, $search_val, $valueFunction);
        }
        else //If the function returns less than OR equal, we search in the top half
        {
            $ret = binarySearchLessOrEqual($mid_idx+1, $end_idx, $search_val, $valueFunction);
    
            //If nothing is suitable, then $mid_idx was actually the best one!
            return $ret == -1 ? $mid_idx : $ret;
        }
    }
    
    测试:

    $array = [ 10, 20, 30 ];
    echo "0:  " . indexOfClosestLesser($array, 0)  . "<br>"; //-1
    echo "5:  " . indexOfClosestLesser($array, 5)  . "<br>"; //-1
    echo "10: " . indexOfClosestLesser($array, 10) . "<br>"; //0
    echo "15: " . indexOfClosestLesser($array, 15) . "<br>"; //0
    echo "20: " . indexOfClosestLesser($array, 20) . "<br>"; //1
    echo "25: " . indexOfClosestLesser($array, 25) . "<br>"; //1
    echo "30: " . indexOfClosestLesser($array, 30) . "<br>"; //2
    echo "35: " . indexOfClosestLesser($array, 35) . "<br>"; //2
    
    $array=[10,20,30];
    回声“0:”。indexOfClosestLesser($array,0)。“
    ”//-1. 回声“5:”。indexOfClosestLesser($array,5)。“
    ”//-1. 回声“10:”。indexOfClosestLesser($array,10)。“
    ”//0 回声“15:”。indexOfClosestLesser($array,15)。“
    ”//0 回声“20:”。indexOfClosestLesser($array,20)。“
    ”//1. 回声“25:”。indexOfClosestLesser($array,25)。“
    ”//1. 回声“30:”。indexOfClosestLesser($array,30)。“
    ”//2. 回声“35:”。indexOfClosestLesser($array,35)。“
    ”//2.
    尝试使用一对全局变量,然后在用于b搜索的比较函数中引用这些变量

    在RPGIV中,我们可以调用c函数

    带有全局变量的比较函数如下所示:

    dcl-proc compInvHdr;
      dcl-pi compInvHdr int(10);
        elmPtr1   pointer value;
        elmPtr2   pointer value;
      end-pi;
      dcl-ds elm1            based(elmPtr1) likeds(invHdr_t);
      dcl-ds elm2            based(elmPtr2) likeds(elm1);
      dcl-s  low             int(10) inz(-1);
      dcl-s  high            int(10) inz(1);
      dcl-s  equal           int(10) inz(0);
    
      select;
      when elm1.rcd.RECORDNO < elm2.rcd.RECORDNO;
        lastHiPtr = elmPtr2;
        return low;
      when elm1.rcd.RECORDNO > elm2.rcd.RECORDNO;
        lastLoPtr = elmPtr2;
        return high;
      other;
        return equal;
      endsl;
    end-proc;
    
    // lastLoPtr and LastHiPtr are global variables
    // basePtr points to the beginning of the array
    lastLoPtr = basePtr;  
    lastHiPtr = basePtr + ((numRec - 1) * sizRec));
    searchKey = 'somevalue'; 
    hitPtr = bsearch(%addr(searchkey)
                    :basePtr
                    :numRec
                    :sizRec
                    :%PADDR('COMPINVHDR'));
    if hitPtr <> *null;
    //? not found 
      hitPtr = lastLoPtr;             
    else;
    //? found
    endif;
    
    dcl程序compInvHdr;
    dcl pi compInvHdr int(10);
    elmPtr1指针值;
    elmPtr2指针值;
    末端pi;
    基于elm1(elmPtr1)的dcl ds likeds(invHdr\t);
    dcl ds基于elm2(elmPtr2)的likeds(elm1);
    dcl-s低int(10)inz(-1);
    dcl-s高int(10)inz(1);
    dcl-s等于int(10)inz(0);
    选择;
    当elm1.rcd.RECORDNOelm2.rcd.RECORDNO时;
    lastLoPtr=elmPtr2;
    高回报;
    其他;
    回报相等;
    endsl;
    结束进程;
    
    记住,在b搜索中,第一个元素是搜索键,第二个元素是数组/内存中的实际存储元素,这就是为什么比较过程使用elmPtr2

    对bsearch的调用如下所示:

    dcl-proc compInvHdr;
      dcl-pi compInvHdr int(10);
        elmPtr1   pointer value;
        elmPtr2   pointer value;
      end-pi;
      dcl-ds elm1            based(elmPtr1) likeds(invHdr_t);
      dcl-ds elm2            based(elmPtr2) likeds(elm1);
      dcl-s  low             int(10) inz(-1);
      dcl-s  high            int(10) inz(1);
      dcl-s  equal           int(10) inz(0);
    
      select;
      when elm1.rcd.RECORDNO < elm2.rcd.RECORDNO;
        lastHiPtr = elmPtr2;
        return low;
      when elm1.rcd.RECORDNO > elm2.rcd.RECORDNO;
        lastLoPtr = elmPtr2;
        return high;
      other;
        return equal;
      endsl;
    end-proc;
    
    // lastLoPtr and LastHiPtr are global variables
    // basePtr points to the beginning of the array
    lastLoPtr = basePtr;  
    lastHiPtr = basePtr + ((numRec - 1) * sizRec));
    searchKey = 'somevalue'; 
    hitPtr = bsearch(%addr(searchkey)
                    :basePtr
                    :numRec
                    :sizRec
                    :%PADDR('COMPINVHDR'));
    if hitPtr <> *null;
    //? not found 
      hitPtr = lastLoPtr;             
    else;
    //? found
    endif;
    
    //lastLoPtr和LastHiPtr是全局变量
    //basePtr指向数组的开头
    lastLoPtr=basePtr;
    lastHiPtr=basePtr+((numRec-1)*sizRec));
    searchKey='somevalue';
    hitPtr=b搜索(%addr(searchkey)
    :basePtr
    :numRec
    :sizRec
    :%PADDR('COMPINVHDR');
    如果hitPtr*为空;
    //? 找不到
    hitPtr=lastLoPtr;
    其他的
    //? 建立
    endif;
    
    因此,如果找不到密钥,则hitPtr将设置为最匹配的密钥,有效地存档“小于或等于密钥”

    如果想要相反的结果,则使用下一个更大的键。然后使用lastHiPtr引用大于搜索键的第一个键


    注意:保护全局变量不受竞争条件的影响(如果适用)。

    坦率地说,我发现查找大于给定数字的数字的逻辑比查找小于或等于给定数字的数字所需的逻辑容易得多。显然,这背后的原因是处理数组中存在的(给定num的)重复数所需的额外逻辑(形成边缘情况)

    public int justGreater(int[] arr, int val, int s, int e){
        // Returns the index of first element greater than val. 
        // If no such value is present, returns the size of the array.
        if (s >= e){
            return arr[s] <= N ? s+1 : s;
        }
        int mid = (s + e) >> 1;
        if (arr[mid] < val) return justGreater(arr, val, mid+1, e);
        return justGreater(arr, val, s, mid);
    } 
    

    这是一种使用循环的非递归方式,我在javascript中使用它,所以我将在javascript中发布:

    let left = 0
    let right = array.length
    let mid = 0
    
    while (left < right) {
        mid = Math.floor((left + right) / 2)
        if (searchValue < array[mid]) {
            right = mid
        } else {
            left = mid + 1
        }
    }
    
    return left - 1
    
    let left=0
    设right=array.length
    设mid=0
    while(左<右){
    中间=数学楼层((左+右)/2)
    if(搜索值<数组[mid]){
    右=中
    }否则{
    左=中+1
    }
    }
    向左返回-1
    

    由于一般指南告诉我们要看中间的指针,许多人看不到实际答案是左指针的最终值。

    想提供一种非二进制搜索方法,用C#实现这一点。下面查找最接近X的值,但不大于X,但可以等于X。My函数也不需要对列表进行排序。理论上它也比O(n)快,但只有在找到确切的目标数时,它才会提前终止并返回整数

        public static int FindClosest(List<int> numbers, int target)
        {
            int current = 0;
            int difference = Int32.MaxValue;
            foreach(int integer in numbers)
            {
                if(integer == target)
                {
                    return integer;
                }
                int diff = Math.Abs(target - integer);
                if(integer <= target && integer >= current && diff < difference)
                {
                    current = integer;
                    difference = diff;
                }
            }
            return current;
        }
    
    public static int FindClosest(列表编号,int目标)
    {
    int电流=0;
    int差=Int32.MaxValue;
    外汇
    
    let left = 0
    let right = array.length
    let mid = 0
    
    while (left < right) {
        mid = Math.floor((left + right) / 2)
        if (searchValue < array[mid]) {
            right = mid
        } else {
            left = mid + 1
        }
    }
    
    return left - 1
    
        public static int FindClosest(List<int> numbers, int target)
        {
            int current = 0;
            int difference = Int32.MaxValue;
            foreach(int integer in numbers)
            {
                if(integer == target)
                {
                    return integer;
                }
                int diff = Math.Abs(target - integer);
                if(integer <= target && integer >= current && diff < difference)
                {
                    current = integer;
                    difference = diff;
                }
            }
            return current;
        }
    
                List<int> values = new List<int>() {1,24,32,6,14,9,11,22 };
                int target = 21;
                int closest = FindClosest(values,target);
                Console.WriteLine("Closest: " + closest);
    
    /**
     * Binary Search of a sorted array but returns the closest smaller value if the
     * needle is not in the array.
     *
     * Returns null if the needle is not in the array and no smaller value is in
     * the array.
     *
     * @param haystack the sorted array to search @param needle the need to search
     * for in the haystack @param compareFn classical comparison function, return
     * -1 if a is less than b, 0 if a is equal to b, and 1 if a is greater than b
     */
    export function lessThanOrEqualBinarySearch<T>(
      haystack: T[],
      needle: T,
      compareFn: (a: T, b: T) => number
    ): T | null {
      let lo = 0;
      let hi = haystack.length - 1;
      let lowestFound: T | null = null;
    
      // iteratively search halves of the array but when we search the larger
      // half keep track of the largest value in the smaller half
      while (lo <= hi) {
        let mid = (hi + lo) >> 1;
        let cmp = compareFn(needle, haystack[mid]);
    
        // needle is smaller than middle
        // search in the bottom half
        if (cmp < 0) {
          hi = mid - 1;
          continue;
        }
    
        // needle is larger than middle
        // search in the top half
        else if (cmp > 0) {
          lo = mid + 1;
          lowestFound = haystack[mid];
        } else if (cmp === 0) {
          return haystack[mid];
        }
      }
      return lowestFound;
    }