76

我正在尝试用JavaScript实现二进制搜索算法。事情似乎还好,但我的返回语句似乎返回了未定义的值。有人能告诉我这里怎么了吗?

小提琴:http://jsfiddle.net/2mBdL/

变量a=[1,2,4,6,1,100,0,10000,];a.sort(函数(a,b){返回a-b;});控制台.log('a,',a);函数binarySearch(arr,i){var mid=数学地板(arr.length/2);console.log(arr[mid],i);如果(arr[mid]===i){控制台.log('match',arr[mid],i);返回arr[mid];}else if(arr[mid]<i&&arr.length>1){控制台.log('mid-lower',arr[mid],i);二进制搜索(arr.splice(mid,Number.MAX_VALUE),i);}否则如果(arr[mid]>i&&arr.length>1){控制台.log('mid higher',arr[mid],i);二进制搜索(arr.splice(0,mid),i);}其他{console.log('此处没有',i);返回-1;}}var result=二进制搜索(a,100);console.log(结果);

5
  • 1
    另外,为什么arr.splice要修改“a”? 评论 2014年3月27日19:58
  • 要返回递归状态,您需要return二进制搜索(…)在每种情况下。 评论 2014年3月27日20:01
  • 拼接修改原始阵列,请参见W3学校; 这包括通过引用传递它。一个好的开始点是nczonline.net/blog/2009/06/09/… 评论 2014年3月27日20:02
  • 它被标记为不太可能帮助未来的访问者,因为这只是一个小错误。 评论 2014年3月27日20:20
  • 2
    为什么要返回与目标匹配的值?这使得整个搜索变得多余!您应该返回索引 评论 2015年11月11日23:31

29个答案29

重置为默认值
124

编写搜索函数时,如果找不到元素,它会返回一个负值,指示新元素的插入点,这样做很有用。此外,在二进制搜索中使用递归是多余且不必要的。最后,通过提供比较器函数作为参数,使搜索算法通用是一种很好的实践。下面是实现。

函数binarySearch(arr,el,compare_fn){设m=0;设n=arr.length-1;而(m<=n){设k=(n+m)>>1;设cmp=compare_fn(el,arr[k]);如果(cmp>0){m=k+1;}否则,如果(cmp<0){n=k-1;}其他{返回k;}}返回~m;}

此代码带有注释和单元测试在这里.

14
  • 2
    您的算法返回-米-1但jsfiddle中的代码注释引用的返回值为-n-1个。可能的拼写错误? 评论 2015年10月8日0:13
  • 2
    作为对怀疑者的回应,我将单元测试扩展为一组随机测试。所有测试都通过了。看看小提琴jsfiddle.net/pkfst550/48 评论 2017年6月5日23:24
  • 0的返回值不明确。元素是在列表的开头,还是需要插入? 评论 2018年8月7日19:00
  • 2
    返回值0没有歧义,它明确表示“在索引0处找到元素”。为了表示“元素应该插入到索引0”,函数将返回-1(即按位补码(共0-“~x”==“-x-1”)。 评论 2019年12月18日13:04
  • 2
    @拉斐尔好奇你为什么喜欢>> 1? 评论 2020年1月7日8:58
51

二进制搜索(ES6)


这些函数返回元素在已排序数组(如果找到)或-1如果不是的话。

自下而上

函数binarySearch(arr,val){设start=0;设end=arr.length-1;while(开始<=结束){让mid=数学地板((开始+结束)/2);if(arr[mid]===val){返回中间;}if(val<arr[mid]){结束=中间-1;}其他{开始=中间+1;}}返回-1;}常数arr=[0,1,2,3,4,6100010000];console.log(二进制搜索(arr,100));//6

递归

函数binarySearch(arr,val,start=0,end=arr.length-1){const mid=数学地板((开始+结束)/2);如果(val===arr[mid]){返回中间;}if(开始>=结束){返回-1;}返回值<arr[mid]? 二进制搜索(arr,val,start,mid-1):二进制搜索(arr,val,mid+1,end);}常数arr=[0,1,2,3,4,6100010000];console.log(二进制搜索(arr,1000));//-1

37

这个问题有很多可行的解决方案,但是一旦找到匹配项,所有人都会提前返回。虽然这可能会对性能产生轻微的积极影响,但由于二进制搜索的对数性质,这可以忽略不计,并且如果比较函数的计算成本很高,这实际上会影响性能。

此外,它阻止了二进制搜索算法的一个非常有用的应用:查找一系列匹配元素,也称为查找降低上限.

以下实现返回索引0阵列长度这样给定的谓词是对于数组[i-1]真的对于数组[i]。如果谓词是无处不在,阵列长度返回。

/***返回0<=i<=array.length,这样!pred(数组[i-1])和&pred(数组[i])。*/函数binarySearch(数组,pred){设lo=-1,hi=array.length;而(1+lo<hi){常数mi=lo+((hi-lo)>>1);if(pred(数组[mi])){hi=英里;}其他{lo=mi;}}返回hi;}

为了论证,假设pred(数组[-1])===假pred(array[array.length])===真(当然,谓词从未在这些点上求值)。循环保持不变!pred(数组[lo])&&pred(阵列[hi])。算法在以下情况下终止1+lo===hi这意味着!pred(数组[hi-1])&&pred(阵列[hi]),所需的后置条件。

如果数组是排序()关于比较函数的ed比较,函数返回最小的 插入位置项目当调用为

二进制搜索(数组,j=>0<=比较(项,j));

插入位置是一个索引,如果数组中存在某个项,则会在该索引处找到该项。

易于实施降低上限对于自然排序的数组,如下所示。

/***返回i,使数组[i-1]<item<=array[i]。*/函数lowerBound(数组,项){return binarySearch(数组,j=>项<=j);}/***返回i,使数组[i-1]<=项<array[i]。*/函数upperBound(数组,项){return binarySearch(数组,j=>项<j);}

当然,当数组可以包含几个比较相同的元素时,这是最有用的,例如,元素包含不属于排序标准的附加数据。

14
  • 1
    虽然这并不能完全回答最初的问题(虽然我对最初的问题的意义感到困惑),但我还是投了赞成票,因为在SO上很难找到好的信息。有一个建议,如果生活允许,请为历史较长、活动较多的问题提供这样的答案。这样做将使更多的人更容易获得您的知识和经验。:-)
    – 诺洛
    评论 2017年3月14日8:41
  • 1
    @诺洛是的常量是语义的-但你的推理方式是错误的;)当然会起作用,因为它比较宽松,但这里有一个严格的常量就足够了
    – 乔基
    评论 2017年3月15日9:18
  • 1
    @Nolo在这种简单的情况下,编译器可以证明变量从未被重新赋值,我不希望它会有什么不同。我仍然认为这是一个好习惯;)
    – 乔基
    评论 2017年3月17日6:29
  • 1
    它的优点是什么lo+(高-低)>>1(hi+lo)>>1? 评论 2020年8月9日10:59
  • 1
    @多米尼克很清楚,问题很好!(嗨+洛)只要元素多于整数类型范围的一半,就会溢出。这是这些类型计算中的一个众所周知的问题,请参见示例ai.googleblog.com/2006/06/…
    – 乔基
    评论 2020年8月10日5:15
27

您没有显式返回递归内部调用(即。return binarySearch()),因此调用堆栈展开时没有返回值。按如下方式更新代码:

// ...如果(arr[mid]===i){控制台.log('match',arr[mid],i);返回arr[mid];}else if(arr[mid]<i&&arr.length>1){控制台.log('mid-lower',arr[mid],i);return binarySearch(arr.splice(mid,Number.MAX_VALUE),i);}else if(arr[mid]>i&&arr.length>1){控制台.log('mid higher',arr[mid],i);return binarySearch(arr.splice(0,mid),i);}其他{// ...

请参见工作小提琴

5
  • 1
    太棒了,谢谢。我想,可以拼接原始数组吗? 评论 2014年3月27日20:11
  • 1
    好吧,这取决于你。如果您想避免传递的数组发生变异,请事先克隆它(例如。var_arr=arr.slice(0)). 这里是一把小提琴. 评论 2014年3月27日20:19
  • 19
    为什么要返回与目标匹配的值?这使得整个搜索变得多余!您应该返回索引。 评论 2015年11月11日23:31
  • 2
    我建议编辑此答案以使用而不是剪接。人们不会期望搜索功能会改变干草堆。我会用二进制搜索(arr.slice(mid),i)二进制搜索(arr.slice(0,mid),i)而不是。
    – 猪脑
    评论 2019年8月12日12:46
  • 1
    @gb96,@MichaelBausano,我只是更新了OP的代码来解决他们指出的问题,返回值和剪接不纯似乎与这个问题无关,所以我把它们放在一边,以免把答案弄得乱七八糟 评论 2019年8月22日10:32
24

𝗬𝗼𝘂 𝘄𝗮𝗻𝘁 𝘀𝗽𝗲𝗲𝗱? 𝗥𝗲𝗮𝗱 𝘁𝗵𝗶𝘀.

正确实现的二进制搜索(不修改数组、对数组进行浅层复制或其他荒谬的操作)的平均复杂度约为O(k*log2(n) )(其中k是常数,表示不必要的开销)。假设您有一个1024个元素的数组,在本例中常数k是1。对于线性搜索,平均复杂度为O(k*n/2)=O(1*1024/2)=O(512)。使用二进制搜索,您的复杂性为O(k*log2(n) )=O(1*log2(1024))=O(1*10)=0(10)现在,假设线性搜索算法和二进制搜索算法都快了25%。现在,这两种算法的k都是0.75。线性搜索的复杂性降低到O(384)号机组(获得128个性能点),而二进制搜索减少到O(7.5)(仅获得2.5个性能点)。优化二进制搜索方法的最小收益是因为二进制搜索方法已经很快了。因此,任何理智的人在尝试优化二进制搜索算法之前,都应该更倾向于优化程序的其余部分。尽管有这种清晰的推理,我最终还是屈服于诱惑,优化了二进制搜索功能,达到了JavaScript工程的绝对极限。

首先,让我们研究一下我开始使用的初始函数。这个函数可能比页面后面显示的要慢得多(它仍然比这里发布的任何其他答案快得多),但应该不会太令人困惑。

常数Arr=[0,4,5,6,9,13,14,21,27,44];const s=sArr[Math.random()*sArr.length|0];document.body.innerHTML=s+“is at”+压缩BS(sArr,s);函数compactBS(数组,searchedValue,ARG_start,ARG_len){//`void 0`是`undefined的缩写`//[start,start+len)的范围:只有start包含在内。它有效//类似于“…”.substr(start,len).indexOf(sValue)//`void 0`是`undefined的缩写`var开始=ARG_start |0;var len=ARG_len===无效0?array.length|0:开始+(ARG_len|0)|0;var偏移=0;对于(var i=30;i>=0;i=i-1|0){偏移量=开始+((1<<i)-1|0)|0;if(偏移<长度){开始=开始+((数组[offset]<searchedValue)<<i)|0;}}if(数组[start|0]!==searchedValue){//删除此if-statement以返回下一个最接近的//元素从searched-for值向下//如果值小于//阵列。https://stackoverflow.com/a/44981570/5601591return-1-开始|0;}返回开始|0;}

上述函数的返回值如下。

  • 如果找到该值,则返回该值的索引。
  • 如果找不到该值,则返回-1-最近指数其中nearestIndex是找到的索引,它是最接近的数字<=索引,上限为0。
  • 如果数组没有在指定范围内排序,那么它将返回一些无意义的数字。

现在,然后,展开它,预计算它,使它快速、好、好,就像这样:

常数Arr=[0,4,5,6,9,13,14,21,27,44];const s=sArr[Math.random()*sArr.length|0];document.body.innerHTML=s+“位于索引”+goodBinarySearch(sArr,s);函数goodBinarySearch(数组、sValue、ARG_start、ARG_len){//范围[start,start+len):只有start包含在内。它可以工作//类似于“…”.substr(start,len).indexOf(sValue)//`void 0`是`undefined的缩写`var开始=ARG_start |0;var len=ARG_len===无效0?(array.length|0)-开始|0:ARG_len|0;var偏移=0;if((偏移=开始+0x3fffffff|0)<len){start=start+((array[offset]<sValue)<<30)|0;}if((偏移=开始+0x1fffffff|0)<len){start=开始+((数组[offset]<sValue)<<29)|0;}if((offset=start+0x0fffffff |0)<len){start=开始+((数组[offset]<sValue)<<28)|0;}if((偏移=开始+0x07ffffff|0)<len){开始=开始+((数组[offset]<sValue)<<27)|0;}if((偏移=开始+0x03ffffff|0)<len){start=start+((array〔offset〕<sValue)<<26)|0;}if((偏移=开始+0x01ffffff|0)<len){开始=开始+((数组[offset]<sValue)<<25)|0;}if((偏移=开始+0x00ffffff|0)<len){start=开始+((数组[offset]<sValue)<<24)|0;}if((offset=start+0x007fffff |0)<len){start=开始+((数组[offset]<sValue)<<23)|0;}如果((偏移量=开始+0x003fffff|0)<len){开始=开始+((数组[offset]<sValue)<<22)|0;}if((offset=start+0x001fffff |0)<len){开始=开始+((数组[offset]<sValue)<<21)|0;}if((offset=start+0x000fffff |0)<len){start=开始+((数组[offset]<sValue)<<20)|0;}if((偏移=开始+0x0007ffff|0)<len){开始=开始+((数组[offset]<sValue)<<19)|0;}if((偏移=开始+0x0003ffff|0)<len){开始=开始+((数组[offset]<sValue)<<18)|0;}if((偏移=开始+0x0001ffff|0)<len){开始=开始+((数组[offset]<sValue)<<17)|0;}if((偏移=开始+0x0000ffff|0)<len){开始=开始+((数组[offset]<sValue)<<16)|0;}if((偏移=开始+0x00007fff|0)<len){开始=开始+((数组[offset]<sValue)<<15)|0;}if((偏移=开始+0x00003fff|0)<len){开始=开始+((数组[offset]<sValue)<<14)|0;}if((offset=start+0x00001fff |0)<len){开始=开始+((数组[offset]<sValue)<<13)|0;}if((offset=start+0x00000fff |0)<len){开始=开始+((数组[offset]<sValue)<<12)|0;}if((偏移量=开始+0x000007ff|0)<len){start=start+((array〔offset〕<sValue)<<11)|0;}if((偏移量=开始+0x000003ff|0)<len){开始=开始+((数组[offset]<sValue)<<10)|0;}if((偏移量=开始+0x000001ff|0)<len){开始=开始+((数组[offset]<sValue)<<9)|0;}if((偏移=开始+0x000000ff |0)<len){start=开始+((数组[offset]<sValue)<<8)|0;}if((偏移=开始+0x0000007f |0)<len){start=start+((array〔offset〕<sValue)<<7)|0;}if((偏移=开始+0x0000003f |0)<len){开始=开始+((数组[offset]<sValue)<<6)|0;}if((偏移=开始+0x0000001f |0)<len){开始=开始+((数组[offset]<sValue)<<5)|0;}if((偏移量=开始+0x0000000f|0)<len){开始=开始+((数组[offset]<sValue)<<4)|0;}如果((偏移量=开始+0x00000007|0)<len){start=开始+((数组[offset]<sValue)<<3)|0;}if((偏移=开始+0x00000003|0)<len){start=开始+((数组[offset]<sValue)<<2)|0;}if((偏移量=开始+0x00000001|0)<len){start=开始+((数组[offset]<sValue)<<1)|0;}if(开始<len){开始=开始+((数组[开始]<sValue)<<0)|0;}if(数组[start|0]!==sValue){//删除此if-statement以返回下一个最接近的//元素从searched-for值向下//如果值小于//数组。https://stackoverflow.com/a/44981570/5601591return-1-开始|0;}返回开始|0;}

为了进一步优化性能,我们可以交错if语句,并使用按位操作使最后三个检查无分支,如下所示:

常数Arr=[0,4,5,6,9,13,14,21,27,44];const s=sArr[Math.random()*sArr.length|0];document.body.innerHTML=s+“位于索引”+fastBinarySearch(sArr,s);函数fastBinarySearch(数组、sValue、ARG_start、ARG_len){//范围[start,start+len):只有start包含在内。它可以工作//类似于“…”.substr(start,len).indexOf(sValue)//“void 0”是“undefined”的简写`var开始=ARG_start |0;var len=ARG_len===无效0?(array.length|0)-开始|0:ARG_len|0;var偏移=0,nCB=0;if((开始+0x00007fff |0)<len){if((开始+0x007fffff |0)<len){if((开始+0x07ffffff|0)<len){如果((起始+0x1fffff|0)<len){if((偏移=开始+0x3fffffff|0)<len){开始=开始+((数组[offset]<sValue)<<30)|0;}if((偏移=开始+0x1fffffff|0)<len){start=开始+((数组[offset]<sValue)<<29)|0;}}if((offset=start+0x0fffffff |0)<len){start=开始+((数组[offset]<sValue)<<28)|0;}如果((偏移量=开始+0x07fffff|0)<len){开始=开始+((数组[offset]<sValue)<<27)|0;}}if((开始+0x01ffffff|0)<len){if((偏移=开始+0x03ffffff|0)<len){开始=开始+((数组[offset]<sValue)<<26)|0;}if((偏移=开始+0x01ffffff|0)<len){开始=开始+((数组[offset]<sValue)<<25)|0;}}if((偏移=开始+0x00ffffff|0)<len){start=开始+((数组[offset]<sValue)<<24)|0;}if((offset=start+0x007fffff |0)<len){start=开始+((数组[offset]<sValue)<<23)|0;}}if((开始+0x0007ffff|0)<len){if((开始+0x001fffff |0)<len){if((offset=start+0x003fffff |0)<len){开始=开始+((数组[offset]<sValue)<<22)|0;}if((offset=start+0x001fffff |0)<len){开始=开始+((数组[offset]<sValue)<<21)|0;}}if((offset=start+0x000fffff |0)<len){start=开始+((数组[offset]<sValue)<<20)|0;}if((偏移=开始+0x0007ffff|0)<len){开始=开始+((数组[offset]<sValue)<<19)|0;}}if((开始+0x0001ffff|0)<len){if((偏移=开始+0x0003ffff|0)<len){start=start+((array〔offset〕<sValue)<<18)|0;}if((偏移=开始+0x0001ffff|0)<len){开始=开始+((数组[offset]<sValue)<<17)|0;}}if((偏移=开始+0x0000ffff|0)<len){开始=开始+((数组[offset]<sValue)<<16)|0;}if((偏移=开始+0x00007fff|0)<len){开始=开始+((数组[offset]<sValue)<<15)|0;}}if((开始+0x0000007f |0)<len){如果((起始+0x000007ff|0)<len){if((开始+0x00001fff |0)<len){if((偏移=开始+0x00003fff|0)<len){开始=开始+((数组[offset]<sValue)<<14)|0;}if((offset=start+0x00001fff |0)<len){开始=开始+((数组[offset]<sValue)<<13)|0;}}if((offset=start+0x00000fff |0)<len){start=start+((array[offset]<sValue)<<12)|0;}if((偏移量=开始+0x000007ff|0)<len){开始=开始+((数组[offset]<sValue)<<11)|0;}}if((开始+0x000001ff|0)<len){if((偏移量=开始+0x000003ff|0)<len){开始=开始+((数组[offset]<sValue)<<10)|0;}if((偏移量=开始+0x000001ff|0)<len){开始=开始+((数组[offset]<sValue)<<9)|0;}}if((偏移=开始+0x000000ff |0)<len){start=开始+((数组[offset]<sValue)<<8)|0;}if((偏移=开始+0x0000007f |0)<len){开始=开始+((数组[offset]<sValue)<<7)|0;}}if((开始+0x00000007|0)<len){if((开始+0x0000001f |0)<len){if((偏移=开始+0x0000003f |0)<len){开始=开始+((数组[offset]<sValue)<<6)|0;}if((偏移=开始+0x0000001f |0)<len){开始=开始+((数组[offset]<sValue)<<5)|0;}}if((偏移量=开始+0x0000000f|0)<len){开始=开始+((数组[offset]<sValue)<<4)|0;}if((偏移=开始+0x00000007|0)<len){start=开始+((数组[offset]<sValue)<<3)|0;}}偏移量=开始+0x00000003|0;nCB=-(偏移量<len |0)|0;start=开始+(((数组[offset&nCB]<sValue)<<2)&nCB)|0;偏移量=开始+0x00000001|0;nCB=-(偏移量<长度|0)|0;start=开始+(((数组[offset&nCB]<sValue)<<1)&nCB)|0;偏移=开始;nCB=-(偏移量<长度|0)|0;start=开始+((数组[offset&nCB]<sValue)&nCB)|0;if(array[start|0]!==sValue){//删除此if-statement以返回下一个最接近的//元素从searched-for值向下//如果值小于//数组。https://stackoverflow.com/a/44981570/5601591return-1-开始|0;}返回开始|0;}

但是等等。。。asunder低语着更精彩的演出前夕。很可能,您是在一个严密的循环中调用二进制搜索。在这种情况下,我们可以预先计算实际将要处理的第一个值,并使用高性能整数索引开关语句直接跳到该值。然而,在使用此函数时,必须确保在修改数组长度后永远不要重用生成的快速函数,因为这样只会搜索数组的一部分。

const clz32=数学.clz32 | |(函数(log,LN2){返回函数(x){返回31-log(x>>>0)/LN2|0;//“0”的作用类似于math.floor};})(数学日志,数学LN2);const ArrayProtoSlice=数组原型.slice;常数Arr=[0,4,5,6,9,13,14,21,27,44];const compFunc=最快BS(sArr);for(var i=0,str=“”,len=sArr.length|0;i<len;i=i+1|0)str+=sArr[i]+“位于”+compFunc(sArr]i)+“<br/>”;document.body.innerHTML=str;//显示结果函数fastestBS(数组、ARG_start、ARG_len){//范围[start,start+len):只有start包含在内。它可以工作//类似于“…”.substr(start,len).indexOf(sValue)//`void 0`是`undefined的缩写`var init_start=ARG_start |0;var len=ARG_len===无效0?array.length |0:init_start+(ARG_len |0)|0;常量compGoto=clz32(长度)&31;var arrPadded=数组;var relLen=len-初始化开始|0;if(relLen&(relLen-1)){//如果不是2的幂var sizeUp=relLen;sizeUp|=sizeUp>>>16;sizeUp |=sizeUp>>8;sizeUp|=sizeUp>>>4;sizeUp|=sizeUp>>>2;sizeUp|=sizeUp>>>1;sizeUp=sizeUp+1-relLen|0;arrPadded=ArrayProtoSlice.call(数组);var accCur=[数组[len-1|0]];while(1<sizeUp&&accCur.length<65536){if(sizeUp&1)arrPadded.push.apply(arrPaded,accCur);sizeUp>>>=1;accCur.推送应用(accCur,accCur);}for(var i=0;i<sizeUp;i=i+1|0){arrPadded.push.apply(arrPaded,accCur);}}返回函数(sValue){var开始=init_start |0,偏移=0;开关(compGoto){案例1:开始=开始+((arrPadded[start+0x3fffffff|0]<sValue)<<30)|0;案例2:开始=开始+((arrPadded[start+0x1fffffff|0]<sValue)<<29)|0;案例3:start=开始+((arrPadded[start+0x0fffffff|0]<sValue)<<28)|0;案例4:start=开始+((arrPadded[start+0x07ffffff|0]<sValue)<<27)|0;案例5:start=开始+((arrPadded[start+0x03ffffff|0]<sValue)<<26)|0;案例6:start=开始+((arrPadded[start+0x01ffffff|0]<sValue)<<25)|0;案例7:start=开始+((arrPadded[start+0x00ffffff|0]<sValue)<<24)|0;情况8:start=开始+((arrPadded[start+0x007fffff|0]<sValue)<<23)|0;案例9:start=start+((arrPadded[start+0x003fffff |0]<sValue)<<22)|0;案例10:start=开始+((arrPadded[start+0x001fffff |0]<sValue)<<21)|0;案例11:start=start+((arrPadded[start+0x000fffff |0]<sValue)<<20)|0;案例12:start=开始+((arrPadded[start+0x0007ffff|0]<sValue)<<19)|0;案例13:start=开始+((arrPadded[start+0x0003ffff|0]<sValue)<<18)|0;案例14:start=开始+((arrPadded[start+0x0001ffff|0]<sValue)<<17)|0;案例15:start=start+((arrPadded[start+0x0000fffff|0]<sValue)<<16)|0;案例16:start=开始+((arrPadded[start+0x00007fff|0]<sValue)<<15)|0;案例17:start=开始+((arrPadded[start+0x00003fff|0]<sValue)<<14)|0;案例18:start=开始+((arrPadded[start+0x00001fff|0]<sValue)<<13)|0;案例19:start=开始+((arrPadded[start+0x00000fff|0]<sValue)<<12)|0;案例20:start=开始+((arrPadded[start+0x000007ff|0]<sValue)<<11)|0;案例21:start=开始+((arrPadded[start+0x000003ff|0]<sValue)<<10)|0;案例22:start=开始+((arrPadded[start+0x000001ff|0]<sValue)<<9)|0;案例23:start=开始+((arrPadded[start+0x000000ff|0]<sValue)<<8)|0;案例24:start=开始+((arrPadded[start+0x0000007f|0]<sValue)<<7)|0;案例25:start=开始+((arrPadded[start+0x0000003f|0]<sValue)<<6)|0;案例26:start=开始+((arrPadded[start+0x0000001f|0]<sValue)<<5)|0;案例27:开始=开始+((arrPadded[start+0x0000000f|0]<sValue)<<4)|0;案例28:开始=开始+((arrPadded[start+0x00000007|0]<sValue)<<3)|0;案例29:start=开始+((arrPadded[start+0x00000003|0]<sValue)<<2)|0;案例30:start=开始+((arrPadded[start+0x00000001|0]<sValue)<<1)|0;案例31:start=开始+((arrPadded[start]<sValue)<<0)|0;}if(arrPadded[开始|0]!==sValue){if(len<=start)start=len-1 |0;//因为我们将阵列的功率增加到2的下一次方//删除此if-statement以返回下一个最接近的//元素从searched-for值向下//如果该值小于//数组。https://stackoverflow.com/a/44981570/5601591return-1-开始|0;}返回启动;};}

演示:

(函数(文档){“使用严格”;var textarea=document.getElementById(“输入框”),searchinput=文档.getElementById('search'),searchStart=文档.getElementById('start'),searchedLength=文档.getElementById('length'),resultbox=文档.getElementById('result'),超时ID=-1;函数doUpdate(){尝试{var txt=textarea.value.replace(/\s*\[|\]\s*/g,'').split(',');var arr=JSON.parse(textarea.value);var searchval=JSON.parse(searchinput.value);var textmtchs=textarea.value.match(/\s*\[|\]\s*/g);var start=searchStart.value | | void 0;var sub=searchedLength.value | | void 0;txt=参考排序(txt,arr);textarea.value=textmtchs[0]+txt.join(',')+textmtchs[textmtchs.length-1];arr=JSON.parse(textarea.value);resultbox.value=fastBinarySearch(arr,searchval,start,sub);}捕捉(e){resultbox.value=“错误”;}}textarea.oninput=搜索输入.oninput=searchStart.oninput=搜索长度.oninpout=textarea.onkeyup=searchinput.onkeyup=searchStart.onkeyup=搜索长度.onkeyup=text区域.onchange=搜索输入.onchange=searchStart.onchange=搜索长度.onchange=函数(e){清除超时(超时ID);timeoutID=setTimeout(doUpdate,e.target===textarea?384:125);}函数refSort(targetData,refData){var索引=Object.keys(refData);indexs.sort(函数(indexA,indexB){if(refData[indexA]<refData[indexB])返回-1;if(refData[indexA]>refData[indexB])返回1;返回0;});return-indexs.map(函数(i){return-targetData[i]})}函数fastBinarySearch(数组、sValue、ARG_start、ARG_len){//范围[start,start+len):只有start包含在内。它可以工作//类似于“…”.substr(start,len).indexOf(sValue)//`void 0`是`undefined的缩写`var开始=ARG_start |0;var len=ARG_len===无效0?(array.length|0)-开始|0:ARG_len|0;var偏移=0,nCB=0;if((开始+0x00007fff |0)<len){if((开始+0x007fffff |0)<len){if((开始+0x07ffffff|0)<len){if((开始+0x1fffffff|0)<len){if((偏移=开始+0x3fffffff|0)<len){start=开始+((数组[offset]<sValue)<<30)|0;}if((偏移=开始+0x1fffffff|0)<len){start=开始+((数组[offset]<sValue)<<29)|0;}}if((offset=start+0x0fffffff |0)<len){start=开始+((数组[offset]<sValue)<<28)|0;}if((偏移=开始+0x07ffffff|0)<len){start=start+((array〔offset〕<sValue)<<27)|0;}}if((开始+0x01ffffff|0)<len){if((偏移=开始+0x03ffffff|0)<len){开始=开始+((数组[offset]<sValue)<<26)|0;}if((偏移=开始+0x01ffffff|0)<len){开始=开始+((数组[offset]<sValue)<<25)|0;}}if((偏移=开始+0x00ffffff|0)<len){start=开始+((数组[offset]<sValue)<<24)|0;}如果((偏移量=开始+0x007fffff|0)<len){start=开始+((数组[offset]<sValue)<<23)|0;}}if((开始+0x0007ffff|0)<len){if((开始+0x001fffff |0)<len){if((offset=start+0x003fffff |0)<len){开始=开始+((数组[offset]<sValue)<<22)|0;}if((offset=start+0x001fffff |0)<len){start=start+((array〔offset〕<sValue)<<21)|0;}}if((offset=start+0x000fffff |0)<len){start=开始+((数组[offset]<sValue)<<20)|0;}if((偏移=开始+0x0007ffff|0)<len){开始=开始+((数组[offset]<sValue)<<19)|0;}}if((开始+0x0001ffff|0)<len){if((偏移=开始+0x0003ffff|0)<len){开始=开始+((数组[offset]<sValue)<<18)|0;}if((偏移=开始+0x0001ffff|0)<len){开始=开始+((数组[offset]<sValue)<<17)|0;}}if((偏移=开始+0x0000ffff|0)<len){开始=开始+((数组[offset]<sValue)<<16)|0;}if((偏移=开始+0x00007fff|0)<len){开始=开始+((数组[offset]<sValue)<<15)|0;}}if((开始+0x0000007f |0)<len){if((开始+0x000007ff|0)<len){if((开始+0x00001fff |0)<len){if((偏移=开始+0x00003fff|0)<len){开始=开始+((数组[offset]<sValue)<<14)|0;}if((offset=start+0x00001fff |0)<len){开始=开始+((数组[offset]<sValue)<<13)|0;}}if((offset=start+0x00000fff |0)<len){开始=开始+((数组[offset]<sValue)<<12)|0;}如果((偏移量=开始+0x000007ff|0)<len){开始=开始+((数组[offset]<sValue)<<11)|0;}}if((开始+0x000001ff|0)<len){if((偏移量=开始+0x000003ff|0)<len){开始=开始+((数组[offset]<sValue)<<10)|0;}if((偏移量=开始+0x000001ff|0)<len){开始=开始+((数组[offset]<sValue)<<9)|0;}}if((偏移=开始+0x000000ff |0)<len){start=start+((array〔offset〕<sValue)<<8)|0;}if((偏移=开始+0x0000007f |0)<len){开始=开始+((数组[offset]<sValue)<<7)|0;}}if((开始+0x00000007|0)<len){if((开始+0x0000001f |0)<len){if((偏移=开始+0x0000003f |0)<len){开始=开始+((数组[offset]<sValue)<<6)|0;}如果((偏移量=起始+0x0000001f|0)<len){开始=开始+((数组[offset]<sValue)<<5)|0;}}if((偏移量=开始+0x0000000f|0)<len){开始=开始+((数组[offset]<sValue)<<4)|0;}if((偏移=开始+0x00000007|0)<len){start=开始+((数组[offset]<sValue)<<3)|0;}}偏移量=开始+0x00000003|0;nCB=-(偏移量<长度|0)|0;start=开始+(((数组[offset&nCB]<sValue)<<2)&nCB)|0;偏移量=开始+0x00000001|0;nCB=-(偏移量<长度|0)|0;start=开始+(((数组[offset&nCB]<sValue)<<1)&nCB)|0;偏移=开始;nCB=-(偏移量<长度|0)|0;start=开始+((数组[offset&nCB]<sValue)&nCB)|0;if(数组[start|0]!==sValue){//删除此if-statement以返回下一个最接近的//元素从searched-for值向下//如果值小于//数组。https://stackoverflow.com/a/44981570/5601591return-1-开始|0;}返回开始|0;}})(文件);
数组(必须是有效的JSON数组)</h3><textarea placeholder=“[-24,-12,0,0,9,16,18,64,80]”type=“text”rows=6 style=“width:calc(100%-.5em);display:block”id=“inputbox”>[-24、-12、0、0、9、16、18、64、80]</textarea><table style=“table-layat:fixed;font-size:1.2em”width=“100%”><tbody><tr>搜索值:<input type=“text”id=“Search”Value=“-12”style=“width:8em;text-align:center;float:right”/></td><td></td>结果索引:<input type=“text”id=“result”value=“1”style=“width:8em;text-align:center;float:right”readonly=“”/></tr><tr>开始索引:<input type=“text”id=“Start”value=“”placeholder=“(0)”style=“width:8em;text-align:center;float:right”/></td><td></td>搜索长度:<input type=“text”id=“Length”value=“”placeholder=“(array Length)”style=“width:8em;text-align:center;float:right”/></tr></tbody></table>

您还可以在演示中使用字符串数组(用引号括起来),它应该可以正常工作。要搜索字符串,必须在搜索值周围加引号。

9
  • 2
    我只想指出,我的版本被指定返回阵列长度对于空数组。你可以返回lo如果你愿意!pred(array[i])&pred(数组[1+i]),不需要额外的(昂贵的)比较。这个嗨+洛代码中的术语可能溢出,这是习惯用法所避免的lo+((hi-lo)>>1).我认为如果…否则可读性比持续+标签;)。我的版本支持任意比较函子,除其他外,它只能比较部分数据。;)
    – 乔基
    评论 2017年7月29日12:21
  • 关于上述代码的警告:如果使用空数组调用,它将访问最后一行的第一个元素数组[0],这可能会导致不良行为…
    – 乔基
    评论 2017年7月29日15:31
  • @Joki是的,如果你搜索未定义在空数组中([]),然后它会返回0而不是-1然而,我怀疑有很多人(如果有的话)使用未定义存储值。因此,我怀疑额外检查所需的额外开销是否会被使用。 评论 2017年8月4日20:56
  • @joki任意比较函数在javascript中速度较慢(除非是本机实现的),因为每次调用任何函数时,都必须将函数的上下文推送到堆栈上,然后在函数完成执行后从堆栈中弹出。这种每一步都不停地推动/弹出的方式效率相当低。 评论 2018年7月15日0:29
  • 2
    ……这就是为什么二进制搜索比线性搜索性能更好的原因——前者只进行对数数量的比较,而后者则会在比较成本较高的情况下产生很大差异。在不改变渐进复杂性的情况下,固定搜索算法中的比较会使它的通用性大大降低,因此对大量数据的预期加速比要小得多。
    – 乔基
    评论 2018年7月15日8:27
12

这是二进制搜索函数,您可以检查

函数bsearch(Arr,value){var低=0,高=Arr.长度-1,中间;while(低<=高){mid=数学地板((低+高)/2);如果(Arr[mid]==值)返回mid;否则,如果(Arr[mid]<值)低=mid+1;其他高=中等1;}返回-1;}
0
5

做得有点不同。看一看

函数binarySearch(arr,n){设min=0;设最大值=arr.length-1;让中间;while(最小值<=最大值){mid=(最小值+最大值)>>>1;如果(arr[mid]==n){返回中间;}else if(arr[mid]<n){最小值=中间值+1;}其他{最大值=中间值-1;}}返回-1;}二进制搜索([1,2,3,5,56],2);
1
  • 1
    这个答案要好得多。如果对这段代码和选定的答案进行了速度分析,我猜想在大型数组上会快100倍。 评论 2020年12月10日1:57
2

此问题的一个变体是查找距离搜索最近的元素X(X)如果没有完全匹配的话。

为此,我们进行了调整@亚历山大·里佐夫的回答因此它总是返回“插入点”=大于或等于这些元素中最小元素的索引X(X).

一旦我们得到结果索引,我们在以下位置检查元素(可能是X(X)或大于X(X))和识别1(较小的),并在两者中选择最接近的。不要忘记处理边缘情况!

函数binarySearch(a,compare){设le=0,ri=a.length-1;while(le<=ri){设mid=(le+ri)>>1,cmp=比较(a[mid]);如果(cmp>0){le=中间+1;}否则,如果(cmp<0){ri=中间-1;}其他{返回中间;}}返回le;}函数binaryClosest(a,compare){设i=二进制搜索(a,比较);如果(i===0)返回[0];如果(i===a.length)返回a[i-1];设d1=-比较(a[i]),d2=比较(a[i-1]);返回d1<d2?a[i]:a[i-1];}//输入=[-3,-2,-1,0,1,1,2,2,2,3,3,3]findX=x=>binaryClosest(输入,项=>x-item)测试=(exp,arg)=>{设x=findX(arg)console.log(exp===x?“确定”:“失败”,arg,exp,x)};测试(-3,-99)测试(+3,+99)测试(0,+0.3)测试(0,0)测试(0,-0.3)测试(-1,-1.3)测试(+1,+1.3)测试(2,2.2)测试(2,2.3)测试(2,2.5)测试(3,2.6)

2
  • 你怎么知道的a[i+1]不是很近吗?我们走吧[1, 10, 15]9,它不会离开吗101然后返回1? 我错过了什么? 评论 2019年4月6日12:22
  • @乔纳斯·威尔姆斯:尽管如此返回le,此bsearch实际上返回了正确的界限,因此可以保证a[i-1]<X<=a[i]。在您的示例中,bsearch进行了两个移动:L=0,R=2--向左移动-L=0,R=0--向右移动-L=1,R=0--故障,返回1。注意,这是索引1,而不是值“1”!
    – 乔治
    评论 2019年4月6日12:46
2

发布答案,以防有人想要

  1. 递归和非递归方式
  2. 直接发表评论

递归方式

/***二进制搜索-使用递归*@param arr-输入数组*@param searchElement-要搜索的元素*@param left-左索引*@param right-右索引*/函数binarySearch(arr,searchElement,left,right){let mid=数学地板((左+(右+1))/2);//使用floor,因为我们可能会得到浮点数if(left<=right){//如果它不是最后一个元素,请进一步处理,否则返回-1if(arr[mid]===searchElement){//在mid处找到元素返回中间;//无需进一步处理}else if(searchElement<arr[mid]){//元素可能位于上半部分右=中间-1;//右边是mid-1,因为我们知道mid不是正确的元素}else{//元素可能在后半部分左=中间+1;//左边是mid+1,因为我们知道mid不是正确的元素}return this.binarySearch(arr,searchElement,left,right);//递归的}返回-1;//找不到元素}//正在调用console.log(二进制搜索([1,2,3,4,5],2,0,4));

非递归方式

/***二进制搜索-不使用递归*@param arr-输入数组*@param searchElement-要搜索的元素*/函数binarySearch(arr,searchElement){设left=0,右=arr.length-1;while(left<=right){//处理直到它是最后一个元素let mid=数学地板((左+(右+1))/2);//使用floor,因为我们可能会得到浮点数if(arr[mid]===searchElement){//在mid找到元素返回中间;//无需进一步处理}if(searchElement<arr[mid]){//元素可能位于上半部分右=中间-1;//右边是mid-1,因为我们知道mid不是正确的元素}else{//元素可能在后半部分左=中间+1;//左边是mid+1,因为我们知道mid不是正确的元素}}返回-1;//找不到元素}//正在调用console.log(二进制搜索([1,2,3,4,5],2));

1

这是我的解决方案!

//执行二进制搜索以查找数组中的位置函数binarySearch(searchElement,searchArray){'使用严格';var stop=搜索数组长度;var last,p=0,增量=0;做{最后一个=p;if(searchArray[p]>searchElement){停止=p+1;p-=增量;}else if(searchArray[p]===searchElement){//找到匹配项!返回p;}delta=数学下限((stop-p)/2);p+=增量//如果delta=0,则p不被修改,循环退出}while(last!==p);返回-1//找不到任何东西};
1

递归二进制搜索函数,它将返回指数正在搜索的元素的:

函数binarySearch(arr,target,idx=0){设full_len=arr.length;if(full_len===0){返回null;}让mid=数学地板(full_len/2);if(arr[mid]===目标){return `${target}的索引是:${idx+mid}`;}否则如果(目标>arr[mid]){设right=arr.slice(mid+1,full_len);idx+=(full_len-right.length);return binarySearch(右,目标,idx);}else if(目标<arr[mid]){设left=arr.slice(0,mid);return binarySearch(left,target,idx);}}//测试:var arr=[1,27,34,42,58,69,71,85,96,151];console.log(二进制搜索(arr,1));//=>0console.log(二进制搜索(arr,27));//=>1console.log(二进制搜索(arr,34));//=>2console.log(二进制搜索(arr,42));//=>console.log(二进制搜索(arr,58));//=>4console.log(二进制搜索(arr,69));//=>5console.log(二进制搜索(arr,71));//=>6console.log(二进制搜索(arr,85));//=>7console.log(二进制搜索(arr,96));//=>8console.log(二进制搜索(arr,151));//=>9arr=[10、20、30、40、50、60、70、80、90、100];console.log(二进制搜索(arr,10));//=>0console.log(二进制搜索(arr,20));//=>1console.log(二进制搜索(arr,30));//=>2console.log(二进制搜索(arr,40));//=>console.log(二进制搜索(arr,50));//=>4console.log(二进制搜索(arr,60));//=>5console.log(二进制搜索(arr,70));//=>6console.log(二进制搜索(arr,80));//=>7console.log(二进制搜索(arr,90));//=>8console.log(二进制搜索(arr,100));//=>9var bigArr=[];对于(var i=1;i<=1000000;i++){bigArr.push(i);}console.log(二进制搜索(bigArr,5342))//=>5341console.log(二进制搜索(bigArr,254369))//=>254368console.log(binarySearch(bigArr,2000000))//=>nullconsole.log(binarySearch(bigArr,-1))//=>null
1

这里有很多过于复杂的答案!如果找到值,则需要返回索引;否则,如果该值位于数组中,则返回该值所在的索引的负值。除数字外,您还希望它具有足够的通用性,能够处理语言表达式:

函数BinarySearch(数组,值){设high=数组长度-1;设low=0;if(值<array〔low〕)返回-1;if(值>数组[高])返回-(高+1);while(高>=低){var中值=(高+低)>>1;if(值===数组[mid])返回中间;else if(值<数组[mid])高=中-1;其他的低=中等+1;}返回-(中间+1);}var c=['alpha','apple','beta','delta','γ','zebra'];console.log(二进制搜索(c,'delta'));//返回3console.log(BinarySearch(c,'beet'));//返回-2变量a=[1,2,3,4,6,7,8,9,10];变量b=[1,2,3,4,5,6,7,8,9,10];console.log(二进制搜索(a,5));//返回-4console.log(二进制搜索(a,11));//返回-9console.log(二进制搜索(b,5));//返回4console.log(二进制搜索(b,0));//返回-1

或者,如果您只想在找到时返回true,否则返回false:

函数BinarySearch(数组,值){设high=数组长度-1;设low=0;if(值<数组[低]|值>数组[高])返回false;而(高>=低){设mid=(高+低)>>1;if(值===数组[mid])返回true;else if(值<数组[mid])高=中-1;其他的低=中等+1;}返回false;}
2
  • 1
    你的代码对我来说是最容易阅读的——所以我投票了。但我无法轻松读取-++,IMHO将其替换为反转所有位且等于-++的~:n=-1*(p+1)=~p 评论 2020年6月7日20:29
  • 谢谢!可读性是编写代码的关键。你对“-++”部分提出了合理的观点。我对此不太满意,但我选择了它而不是其他选择。我接受了你的建议并进行了修改。 评论 2020年6月7日23:05
0

以下是中的ES6功能函数式编程样式,如果没有提供,则使用默认的比较函数:如果查找的值是数字类型,则假定是数值比较,否则假定是字符串比较。

函数binarySearch(arr,val,compFunc=(a,b)=>类型val==“数字”?a-b:a.locale比较(b),i=0,j=arr.长度){返回i>=j?:(中间=>(cmp=>cmp<0?二进制搜索(arr,val,compFunc,i,mid):cmp>0?二进制搜索(arr,val,compFunc,mid+1,j):中间)(compFunc(val,arr[mid])))(i+j>>1);}/////////测试///////////////////功能检查(arr、val、compFunc){var fmt=JSON.stringify;var result=二进制搜索(arr,val);//假定为默认compFunc控制台.log(`binarySearch(${fmt(arr)},${fmt(val)})==${fpt(result)}`);if(结果>arr.length | |结果<0 | |!arr.lendth&&result||结果<arr.length&&compFunc(val,arr[result])>0||result>0&compFunc(val,arr[result-1])<0)throw“意外结果!!!”}//使用数字数据进行测试:对于(var val=0;val<12;val++)检查([1,2,4,6,9,9,10],val,(a,b)=>a-b);//使用空数组进行测试:检查([],12,(a,b)=>a-b);//使用字符串数据进行测试:检查(['abc','deaf','def','g'],'dead',(a,b)=>a.localeCompare(b));

  • 最好简化代码,以便初学者能够理解。 评论 2017年2月17日19:15
  • 我想给出一个基于ES6特性的版本。你对它有什么不明白的?
    – 三角帆
    评论 2017年2月17日19:43
  • 2
    具有讽刺意味。这可能是最优雅的解决方案,但详尽的函数调用开销和过度扩展的分支使得这一过程非常缓慢,而且到目前为止内存效率最高,因为它必须经过的每个内部层都会创建许多新变量。 评论 2017年7月7日16:27
0

递归地将搜索范围除以一半,直到达到适当的值或超出搜索范围:

const二进制搜索=(arr,item,[低=0,高=arr.length-1]=[])=>{const mid=数学地板((低+高)/2)如果(低>高)返回-1//是否超出范围?如果(arr[mid]===项)返回mid//是否找到项?return binarySearch(arr,item[项目>arr[mid]?中+1:低,项目<arr[mid]?中1:高])}

让我们测试一下:

//积极的,积极的console.log(二进制搜索([2,6,9,14,21],9))//=>2console.log(二进制搜索([2,6,9,14,21],21))//=>4console.log(binarySearch([2,6,9,14,21],2))//=>0//阴性console.log(二进制搜索([2,6,9,14,21],0))//=>-1console.log(二进制搜索([2,6,9,14,21],-4))//=>-1console.log(二进制搜索([2,6,9,14,21],40)//=>-1
1
  • 更改if(低>高)返回-1如果(低>高)返回-低1,您得到了一个二进制搜索,它可以返回项目未找到时。:)
    – 生殖器
    评论 2019年9月11日5:24
0

无突变和递归

让A=[…数组(100).keys()].map((x)=>Math.floor(Math.random()*1000)).sort((A,b)=>A-b)const二进制搜索=(A,A,b,k)=>{const m=数学地板((b+a)/2);控制台.log(a,m,b,k)如果(A[m]===k){返回m;}如果(A[A]<=k&&k<=A[m]){return binarySearch(A,A,m,k)}如果(A[m]<k&&k<=A[b]){返回二进制搜索(A,m+1,b,k)}返回-1;}console.log(`key${A[30]}`);rez=二进制搜索(A,0,100,A[30])log(`rez是${rez},${A[30]}的索引是${A.indexOf(A[30])}`);rez=二进制搜索(A,0,100,666)console.log(`rez是${rez},${666}的索引是${A.indexOf(666)});

0

港口之字形二进制搜索:

  • 使用<而不是<=所以少一个比较
  • 上面允许对正确的-对于zig或rust之类的语言
  • 计算中间值而不溢出中间-对于zig或rust之类的语言
const-binarySearch=(key,items,compareFn)=>{设left=0;让right=items.length;while(左<右){const mid=左+数学地板((右-左)/2);const cmp=比较Fn(键,项[mid]);如果(cmp===0)返回mid;如果(cmp<0)right=mid;否则左=中间+1;//当right=items.length-1,key=2,items=[3]时发生如果(right<0)抛出新错误(“rightis<0”);}返回-1;};常数比较Fn=(a,b)=>a-b;控制台.log(binarySearch(33,新的Int16Array([1,3,4,2,33,20,10,12,34]).sort(),compareFn));console.log(binarySearch(2,新的Int16Array([3]).sort(),compareFn));
0

const SearchArray=(Listarr,键)=>{const midvalues=数学下限(Listarr.length/2)if(Key===Listarr[midvalues])返回trueelse if(Listarr[midvalues]<=键和midvalues>0)return SearchArray(Listarr.slice(中值,Listarr.length),键)else if(Listarr[midvalues]>=键和midvalues>0)return SearchArray(Listarr.slice(0,中值),Key)其他的return“未找到记录”}常量arr=[11,24,26,37,40,43,56,75,87];console.log(SearchArray(arr,75))console.log(SearchArray(arr,87))console.log(SearchArray(arr,11))console.log(SearchArray(arr,26))

0

您可以简单地考虑使用线性搜索具有查找索引(或查找最后索引找到查找最后一个). 对于大型数组,速度可能较慢,但编码更少。。。

变量a=[0,1,1,2,3,4,6,100,10000];i=a.find指数(x=>x>=4)控制台.log(`a[${i}]=${a[i]}`)

当然,数组必须排序。

-1

假设是一个排序数组,下面是一个递归二进制搜索:

功能binSearch(针、角){长度=arr.length;而(长度>1){中点=数学地板(长度/2);返回(针>棱[中点-1])?binSearch(针、棱缝(中点、长度)):binSearch(针、棱缝(0,中点));}回针===arr[0]?arr[0]:-1;}
4
  • 递归函数调用=慢代码;每天一整天(几乎没有例外)。 评论 2017年7月7日16:31
  • 1
    @杰克·吉芬:更不用说了剪接(已解释在这里为什么不好)。仍然以简洁和说教的品质获得好评。
    – 7月6日
    评论 2018年6月29日17:04
  • @求求你帮帮我。我看不出有什么了不起的品质。在这个代码片段中,拼接的使用违背了直觉,因为它用于剪切不需要的部分,而不是在搜索数组的需要部分上进行分区。 评论 2018年7月3日23:10
  • @杰克·吉芬:在没有技术混乱的情况下演示原理。清晰简洁。人可读。工作的最小代码。难忘的。为了清晰起见,牺牲效率。让学生可以自由地为实际应用程序进行改进。让我看得更清楚些——你做不到。P.S.您的剪接参数是“杯子是半空的”-层。
    – 7月6日
    评论 2018年7月4日16:26
-1

您还应该检查“value not found”边缘情况,并将其作为第一个基本条件,然后进行成功搜索。因此,在递归数组时,不需要检查数组长度是否大于1。最后,既然不返回数组,为什么不使用array.prototype.slice方法?

var binarySearch=函数(arr,val){var half=数学地板(arr.length/2);如果(arr.length===0){返回-1;}else if(arr[half]===val){console.log(“val:”,val,“arr[half]:”,arr[half]);返回值;}else if(val>arr[half]){console.log(“val:”,val,“arr[half]:”,arr[half]);return binarySearch(arr.slice(half,arr.length),val);}其他{console.log(“val:”,val,“arr〔half〕:”,arr〔half〕);return binarySearch(arr.slice(0,half),val);}}var-arr=[1,5,20,58,76,8,19,41].sort(函数(a,b){return a-b});console.log(“排序数组:”+arr);console.log(二进制搜索(arr,76));console.log(二进制搜索(arr,19));console.log(二进制搜索(arr,0));
2
  • 我喜欢你的代码的呈现方式,但你的基本情况逻辑是错误的。尝试通过搜索数字“9”来运行实现,您将看到函数超出了调用堆栈。您需要检查数组长度是否等于1而不是0,还需要检查该值是否等于“val”,否则在某些情况下,您的函数将无限递归。
    – 用户2117153
    评论 2015年6月22日3:02
  • 为什么要返回与目标匹配的值?这使得整个搜索变得多余!您应该返回索引。 评论 2015年11月11日23:32
-1
函数binarySearch(a=[],x){让长度=a.length;如果(长度===0){返回false;}其他{设m=数学楼层(长度/2);设y=a[m];如果(x===y){返回true;}否则,如果(x<y){return binarySearch(a.slice(0,m),x);}其他{返回二进制搜索(a.slice(m+1),x);}}}
1
  • 这个片段可能很优雅,但它是非常不必要的,因此在这种情况下表现不太好。 评论 2017年7月7日16:31
-1

下午好,我理解这篇帖子是在不久前开始的,但我认为我可能会对讨论有所贡献。

函数binarySearch(数组、目标、最大值、最小值){//找到数组强制最大域和最小域之间的中点var mid=((最大值-最小值)>>1)+最小值;//警报(“中点编号”+mid);console.log(中);console.log(数组[mid],“target:”+目标);if(数组[mid]===目标){//找到目标值console.log('match',数组[mid],目标);//警报(“匹配”,数组[mid],目标);返回中间;} else if(mid===0){//已检查所有值,没有目标值,返回哨兵值返回-1;}else if(数组[mid]>目标){//中点的值大于目标值,将新的最大值设置为当前中点最大值=中间值;console.log('mid-lower',array[mid],target);//警报('mid lower',数组[mid],目标);//使用新的搜索域调用binarySearchreturn binarySearch(数组,目标,最大值,最小值);} else if(数组[mid]<目标){//中点处的值小于目标值,将新的最小值设置为当前中点最小值=中间值;console.log('mid higher',array[mid],target);//警报('mid higher',数组[mid],目标);//使用新的搜索域调用binarySearchreturn binarySearch(数组,目标,最大值,最小值);}

我确信这里有改进的空间,但此方法避免了必须执行阵列的深层拷贝(在处理大型数据集时,这可能是一个代价高昂的操作),同时,它不会以任何方式修改阵列。

希望能有所帮助!谢谢,杰里米

2
  • 为什么要返回与目标匹配的值?这使得整个搜索变得多余!您应该返回索引。 评论 2015年11月11日23:33
  • 感谢gb96,我编辑了我发布的实际解决方案,去掉了一些特定情况的信息,显然在接触这篇文章时没有太多注意。根据您非常必要的更正进行编辑。 评论 2016年3月7日15:33
-1

函数binarySearch(arr,num,l,r){if(arr instanceof Array){l=NaN(l)?0:l;r=NaN(r)?棱长-1:r;设mid=l+1+数学四舍五入((r-l)/2-1);控制台.log(l,r,mid,arr[mid]);如果(num==arr[mid]){console.log(“已找到”);返回中间;}if(类型arr[mid]==“未定义”|l==r){console.log(“找不到”);返回-1;}if(num<arr[mid]){console.log(“向左移动”);return binarySearch(arr,num,l,r-mid);}console.log(“获取权限”);return binarySearch(arr,num,mid,r);}}console.log(二进制搜索([0,0,1,1,2,3,5,6,11],2));

1
  • 无声错误自掘坟墓(注意函数是如何执行的if(arr instanceof Array)). 当然,一开始看起来不错:文件较小。但是,无声的错误慢慢融合成一条毒蛇。突然有一天,这条隐喻性的蛇会让你的程序无法运行,从而猛烈抨击你。但是,最糟糕的情况还在后头:隐喻性的毒液渗入你的血管,让你病入膏肓,阻止你在代码中找到问题的根源。当你在痛苦和绝望中喘着最后一口气时,你现在后悔自己犯下的所有无声错误 评论 2018年11月27日22:14
-1

假设数组是排序的(要么编写自己的排序算法,要么只使用内置方法)

函数bSearch(数组,项){var启动=0,end=项目长度-1;var middle=数学地板((结束+开始)/2);while(数组[middle]!==项&&start<end){if(项<数组[中间]){end=中间1;}else if(项>数组[middle]){开始=中间+1;}中间=数学地板((结束+开始)/2);} if(array[item]!==中间){console.log('notFoundMate);返回-1;}其他{console.log('FoundMate);返回中间;}}
-1

要使用它,只需按原样复制粘贴即可,使用局部变量来提高速度。并修改搜索值,如在子对象或数组中搜索:

if(数组[mid][0]<值[0])低=mid+1;if(array[mid].val<value.val)low=mid+1;

为了获得更快的结果,请使用数组或数组数组或并行数组,将搜索的数组复制到局部变量。非局部变量或每次执行obj操作时,速度都会减慢。

这是最快的版本,如下所示:

设数组=[3,4,5,6]设值=5//搜索的值设mid,low=0,high=array.length;while(低<高){中等=低+高>>>1;//快速除以2并四舍五入if(数组[mid]<值)低=mid+1;其他高=中等;}//if(array[mid]!=值)mid=-1;//如果您寻找插入新值的位置,则可能不需要这样做中间;//包含找到的值位置,如果找不到,则在找到它之前

二进制搜索的工作原理如下:

|           .           |     // 找到中间位置//选项1:|           .     v |//如果值位于右侧,则中间为顶部|     .     |     // 那么就是这样//选项2:|版本|//如果值在左边,中间是底部|     .     |                 // 那么就是这样//选项2的更多循环,直到找不到为止|  .  |                       // 下次会是这样|. |                          // 下次会是这样.//下次会是这样

如果找不到该实现,则会转到底部。它可以被发现,也可以不被发现。它返回低于或等于搜索值的索引。所以你需要检查它是否相等。验证该值是否存在,或者它只是下面的一个结果。如果您正在寻找插入订单的位置,只需将订单放在该位置,无需检查是否等于

-1

我认为下面的选项很容易在JS中实现二进制搜索。

arr=[1,2,3,4,5];searchTerm=2;函数binarySearchInJS(开始,结束){isFound=false;if(结束>开始){//console.log(开始+“\t”+结束)mid=(结束+开始)/2;mid=数学地板(中间)if(searchTerm==arr[mid]){                   isFound=true;}其他的{   if(searchTerm<arr[mid]){               binarySearchInJS(开始,中间);}if(searchTerm>arr[mid]){           binarySearchInJS(中间+1,结束);}}}if(找到){return“成功”;}其他{return“找不到”;}       }
-1

功能齐全的二进制搜索:

  • 负值表示插入点
  • 允许搜索第一个和最后一个索引
  • 开始索引,独占结束索引
  • 自定义比较功能

(此代码和单元测试在这里)

功能默认比较(o1,o2){如果(o1<o2){返回-1;}如果(o1>o2){返回1;}返回0;}/***@param数组带比较函数的排序数组*@param项搜索项*@param start(可选)开始索引*@param-end(可选)独占结束索引*@param compare(可选)自定义比较函数*@param bound(可选)(-1)第一个索引;(1) 最后指标;(0)无关紧要*/函数binarySearch(数组、项、开始、结束、比较、绑定){if(!compare){compare=defaultCompare;}让from=start==null?0:启动;让to=(end==null?数组长度:end)-1;让发现=-1;while(从<=到){常数中间=(从+到)>>>1;const compareResult=比较(数组[middle],项);如果(compareResult<0){从=中间+1;}else if(compareResult>0){至=中间-1;}else if(!绑定){回归中间;}else if(绑定<0){//第一次出现:发现=中间;至=中间-1;}其他{//上次出现时间:发现=中间;从=中间+1;}}找到返回>=0?发现:-来自-1;}
-1

我们开始吧。

设a=[1,2,4,6,1100,0,10000,3];a.sort(函数(a,b){返回a-b;});函数binarySearch(arr,value){如果(arr.length==0)返回-1;var低=0,高=arr.长度-1,中等;while(低<=高){mid=数学地板((低+高)/2);如果(arr[mid]==值)返回mid;否则,如果(arr[mid]<value)low=mid+1;其他高=中等1;}返回-1;}console.log(二进制搜索(a,1005))

这是工作小提琴-https://jsfiddle.net/avadhutthorat/6Lq1r582/

-2

我想添加我的searchArray(搜索阵列)二进制搜索功能以及工具实用程序功能插入排序数组删除排序数组对于答案列表,我认为它是干净的,它是全球通用的,而且我认为它的速度非常理想。

4
  • 1
    然后添加?:我很确定这个问题的答案应该包含在SO的答案中,不是吗? 评论 2017年8月6日2:59
  • 它在github主旨中。我不仅将其用于stackoverflow,还将其用作一般解决方案。请在那里查找代码并决定如何处理它。 评论 2017年9月4日11:00
  • 你是说它很可能会改变,因此链接是更好的解决方案,因为读者会看到最新的版本? 评论 2017年9月5日7:12
  • 它不太可能改变,因为该算法是非常基本的标准,我想我以一种非常简单、易懂和快速的方式实现了它。这是我在javascript中遗漏的一个漏洞的主要参考点。 评论 2017年10月5日15:30

不是你想要的答案吗?浏览标记的其他问题问你自己的问题.