簡體   English   中英

Javascript 中的二進制搜索

[英]Binary Search in Javascript

我正在嘗試在 JavaScript 中實現二進制搜索算法。 事情似乎還好,但我的返回語句似乎返回未定義? 誰能告訴這里有什么問題?

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

謝謝。

var a = [
    1,
    2,
    4,
    6,
    1,
    100,
    0,
    10000,
    3
];

a.sort(function (a, b) {
    return a - b;
});

console.log('a,', a);

function binarySearch(arr, i) {
    var mid = Math.floor(arr.length / 2);
    console.log(arr[mid], i);

    if (arr[mid] === i) {
        console.log('match', arr[mid], i);
        return arr[mid];
    } else if (arr[mid] < i && arr.length > 1) {
        console.log('mid lower', arr[mid], i);
        binarySearch(arr.splice(mid, Number.MAX_VALUE), i);
    } else if (arr[mid] > i && arr.length > 1) {
        console.log('mid higher', arr[mid], i);
        binarySearch(arr.splice(0, mid), i);
    } else {
        console.log('not here', i);
        return -1;
    }

}
var result = binarySearch(a, 100);
console.log(result);

以這樣的方式編寫搜索函數很有用,如果未找到元素,它返回一個負值,指示新元素的插入點。 此外,在二分查找中使用遞歸是多余且不必要的。 最后,通過提供比較器函數作為參數來使搜索算法通用是一個很好的做法。 下面是實現。

function binarySearch(ar, el, compare_fn) {
    var m = 0;
    var n = ar.length - 1;
    while (m <= n) {
        var k = (n + m) >> 1;
        var cmp = compare_fn(el, ar[k]);
        if (cmp > 0) {
            m = k + 1;
        } else if(cmp < 0) {
            n = k - 1;
        } else {
            return k;
        }
    }
    return -m - 1;
}

此代碼注釋和單元測試在這里

您沒有明確返回遞歸內部調用(即return binarySearch() ),因此調用堆棧展開而沒有返回值。 像這樣更新你的代碼:

// ...
if (arr[mid] === i) {
    console.log('match', arr[mid], i);
    return arr[mid];
} else if (arr[mid] < i && arr.length > 1) {
    console.log('mid lower', arr[mid], i);
    return binarySearch(arr.splice(mid, Number.MAX_VALUE), i);
} else if (arr[mid] > i && arr.length > 1) {
    console.log('mid higher', arr[mid], i);
    return binarySearch(arr.splice(0, mid), i);
} else {
// ...

看到一個工作小提琴

這個問題有很多可行的解決方案,但是一旦找到匹配項,所有解決方案都會提前返回。 雖然這可能對性能有很小的積極影響,但由於二分搜索的對數性質,這可以忽略不計,如果比較函數的計算成本很高,實際上可能會損害性能。

更重要的是,它阻止了二進制搜索算法的一個非常有用的應用:查找匹配元素的范圍,也稱為查找下界上界

以下實現返回的索引0iarray.length使得給定的謂詞是false對於array[i - 1]true對於array[i] 如果謂詞處處為false ,則返回array.length

 /** * Return 0 <= i <= array.length such that !pred(array[i - 1]) && pred(array[i]). */ function binarySearch(array, pred) { let lo = -1, hi = array.length; while (1 + lo < hi) { const mi = lo + ((hi - lo) >> 1); if (pred(array[mi])) { hi = mi; } else { lo = mi; } } return hi; }

為了論證的緣故,假設pred(array[-1]) === falsepred(array[array.length]) === true (當然,謂詞永遠不會在這些點上求值)。 循環保持不變!pred(array[lo]) && pred(array[hi]) 1 + lo === hi這意味着!pred(array[hi - 1]) && pred(array[hi]) ,即所需的后置條件時,算法終止。

如果陣列是sort()相對於一個比較函數編compare ,該函數返回一個最小插入位置item時援引為

binarySearch(array, j => 0 <= compare(item, j));

插入位置是一個索引,如果它存在於數組中,則可以在該索引處找到該項目。

可以很容易地以自然順序為數組實現下界上限,如下所示。

 /** * Return i such that array[i - 1] < item <= array[i]. */ function lowerBound(array, item) { return binarySearch(array, j => item <= j); } /** * Return i such that array[i - 1] <= item < array[i]. */ function upperBound(array, item) { return binarySearch(array, j => item < j); }

當然,當數組可以包含多個相同比較的元素時,這是最有用的,例如,元素包含不屬於排序標准的附加數據。

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

正確實現的二元搜索(不修改數組、制作數組的淺拷貝或其他荒謬)的平均復雜度約為O(k*log 2 (n)) (其中 k 是常數表示不必要的開銷)。 假設您有一個包含 1024 個元素的數組,在這種情況下常數 k 為 1。 使用線性搜索,平均復雜度為O(k*n/2)=O(1*1024/2)=O(512) 使用二分搜索,您的復雜度為O(k*log 2 (n))=O(1*log 2 (1024))=O(1*10)=O(10) 現在,假設你讓線性搜索算法快 25%,二分搜索算法快 25%。 現在,這兩種算法的 k 都是 0.75。 線性搜索的復雜度降低到O(384) (獲得 128 個性能點),而二分搜索降低到O(7.5) (僅獲得 2.5 個性能點)。 優化二分搜索方法的這種最小收益是因為二分搜索方法已經非常快了。 因此,任何理智的人都應該更傾向於在嘗試優化二進制搜索算法之前優化程序的其余部分。 盡管有這種清晰的推理,我最終還是屈服於誘惑,將二分搜索功能優化到 JavaScript 工程的絕對限制。

為了開始性能最大值,讓我們首先研究我開始使用的初始函數。 此函數可能比頁面下方顯示的函數慢得多(它仍然比此處發布的任何其他答案快得多),但它應該不那么令人困惑。

 const sArr = [0,4,5,6,9,13,14,21,27,44]; const s = sArr[Math.random() * sArr.length | 0]; document.body.innerHTML = s+" is at "+slowestBS(sArr, s); function slowestBS(array, searchedValue, ARG_start, ARG_len){ // Range of [start, start+len): only start is inclusive. It works // similarly to "...".substr(start, len).indexOf(sValue) // `void 0` is shorthand for `undefined` var start = ARG_start |0; var len = (ARG_len === void 0 ? (array.length|0)-start : ARG_len) | 0; len = len - 1 |0; for (let i=0x80000000; i; i >>>= 1) { if (len & i) { const withoutCurBit = len & ~(i-1); if (array[start + withoutCurBit] > searchedValue) { len = withoutCurBit - 1 |0; } } } if (array[start+len] !== searchedValue) { // remove this if-statement to return the next closest // element going downwards from the searched-for value // OR 0 if the value is less than all values in the // array. https://stackoverflow.com/a/44981570/5601591 return -1 - start - len |0; } return start + len |0; }

上述函數的返回值如下。

  • 如果找到該值,則返回該值的索引。
  • 如果未找到該值,則它返回-1 - nearestIndex其中,nearestIndex 是找到的索引,它是最接近的數字 <= index 並且上限為 0。
  • 如果數組沒有在指定范圍內排序,那么它將返回一些無意義的數字。

為了開始優化,讓我們首先刪除那個討厭的內部 if 分支。

 const sArr = [0,4,5,6,9,13,14,21,27,44]; const s = sArr[Math.random() * sArr.length | 0]; document.body.innerHTML = s+" is at "+compactBS(sArr, s); function compactBS(array, searchedValue, ARG_start, ARG_len){ // `void 0` is shorthand for `undefined` var start = ARG_start === void 0 ? 0 : ARG_start |0; var len = (ARG_len === void 0 ? (array.length|0) - start : ARG_len) |0; len = len - 1 | 0; for (let i=0x80000000; i; i >>>= 1) { if (len & i) { const noCBit = len & ~(i-1); // noCBits now contains all the bits in len that are // greater than the present value of i. len ^= ( (len ^ (noCBit-1)) & ((array[start+noCBit] <= searchedValue |0) - 1 >>>0) ); // works based on the logic that `(x^y)^x === y` is always true } } if (array[start+len] !== searchedValue) { // remove this if-statement to return the next closest // element going downwards from the searched-for value // OR 0 if the value is less than all values in the // array. https://stackoverflow.com/a/44981570/5601591 return -1 - start - len |0; } return start + len |0; }

現在,然后,展開它,預先計算它,使其快速、美觀和良好,就像這樣:

 const sArr = [0,4,5,6,9,13,14,21,27,44]; const s = sArr[Math.random() * sArr.length | 0]; document.body.innerHTML = s+" is at "+goodBinarySearch(sArr, s); function goodBinarySearch(array, sValue, ARG_start, ARG_len){ // Range of [start, start+len): only start is inclusive. It works // similarly to "...".substr(start, len).indexOf(sValue) // `void 0` is shorthand for `undefined` var start = (ARG_start === void 0 ? 0 : ARG_start) | 0; var len = (ARG_len === void 0 ? (array.length|0) - start : ARG_len) |0; len = len - 1 |0; if (len & 0x80000000) { const nCB = len & 0x80000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x40000000) { const nCB = len & 0xc0000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x20000000) { const nCB = len & 0xe0000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x10000000) { const nCB = len & 0xf0000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x8000000) { const nCB = len & 0xf8000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x4000000) { const nCB = len & 0xfc000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x2000000) { const nCB = len & 0xfe000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x1000000) { const nCB = len & 0xff000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x800000) { const nCB = len & 0xff800000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x400000) { const nCB = len & 0xffc00000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x200000) { const nCB = len & 0xffe00000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x100000) { const nCB = len & 0xfff00000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x80000) { const nCB = len & 0xfff80000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x40000) { const nCB = len & 0xfffc0000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x20000) { const nCB = len & 0xfffe0000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x10000) { const nCB = len & 0xffff0000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x8000) { const nCB = len & 0xffff8000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x4000) { const nCB = len & 0xffffc000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x2000) { const nCB = len & 0xffffe000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x1000) { const nCB = len & 0xfffff000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x800) { const nCB = len & 0xfffff800; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x400) { const nCB = len & 0xfffffc00; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x200) { const nCB = len & 0xfffffe00; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x100) { const nCB = len & 0xffffff00; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x80) { const nCB = len & 0xffffff80; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x40) { const nCB = len & 0xffffffc0; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x20) { const nCB = len & 0xffffffe0; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x10) { const nCB = len & 0xfffffff0; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x8) { const nCB = len & 0xfffffff8; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x4) { const nCB = len & 0xfffffffc; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x2) { const nCB = len & 0xfffffffe; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x1) { const nCB = len & 0xffffffff; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (array[start+len|0] !== sValue) { // remove this if-statement to return the next closest // element going downwards from the searched-for value // OR 0 if the value is less than all values in the // array. https://stackoverflow.com/a/44981570/5601591 return -1 - start - len |0; } return start + len |0; }

但是等等... 分裂的耳語是更高性能的前夕。 很可能,您正在緊密循環中調用二分查找。 在這種情況下,我們可以預先計算將實際處理的第一個值,並使用高性能整數索引 switch 語句直接跳到它。 但是,在使用它時,您必須確保在修改數組長度后永遠不會重用生成的快速函數,因為這樣只會搜索數組的一部分。

 const clz32 = Math.clz32 || (function(log, LN2){ return function(x) { return 31 - log(x >>> 0) / LN2 | 0; // the "| 0" acts like math.floor }; })(Math.log, Math.LN2); const sArr = [0,4,5,6,9,13,14,21,27,44]; const compFunc = fastestBS(sArr); for (var i=0,str="",len=sArr.length|0; i < len; i=i+1|0) str += sArr[i]+" is at "+compFunc(sArr[i])+"<br/>"; document.body.innerHTML = str; // show the result function fastestBS(array, ARG_start, ARG_initLen){ // Range of [start, start+len): only start is inclusive. It works // similarly to "...".substr(start, len).indexOf(sValue) // `void 0` is shorthand for `undefined` var start = ARG_start === void 0 ? 0 : ARG_start |0; var initLen = (ARG_initLen===void 0 ? (array.length|0)-start : ARG_initLen) |0; initLen = initLen - 1 |0; const compGoto = clz32(initLen) & 31; return function(sValue) { var len = initLen | 0; switch (compGoto) { case 0: if (len & 0x80000000) { const nCB = len & 0x80000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 1: if (len & 0x40000000) { const nCB = len & 0xc0000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 2: if (len & 0x20000000) { const nCB = len & 0xe0000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 3: if (len & 0x10000000) { const nCB = len & 0xf0000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 4: if (len & 0x8000000) { const nCB = len & 0xf8000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 5: if (len & 0x4000000) { const nCB = len & 0xfc000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 6: if (len & 0x2000000) { const nCB = len & 0xfe000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 7: if (len & 0x1000000) { const nCB = len & 0xff000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 8: if (len & 0x800000) { const nCB = len & 0xff800000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 9: if (len & 0x400000) { const nCB = len & 0xffc00000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 10: if (len & 0x200000) { const nCB = len & 0xffe00000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 11: if (len & 0x100000) { const nCB = len & 0xfff00000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 12: if (len & 0x80000) { const nCB = len & 0xfff80000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 13: if (len & 0x40000) { const nCB = len & 0xfffc0000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 14: if (len & 0x20000) { const nCB = len & 0xfffe0000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 15: if (len & 0x10000) { const nCB = len & 0xffff0000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 16: if (len & 0x8000) { const nCB = len & 0xffff8000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 17: if (len & 0x4000) { const nCB = len & 0xffffc000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 18: if (len & 0x2000) { const nCB = len & 0xffffe000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 19: if (len & 0x1000) { const nCB = len & 0xfffff000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 20: if (len & 0x800) { const nCB = len & 0xfffff800; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 21: if (len & 0x400) { const nCB = len & 0xfffffc00; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 22: if (len & 0x200) { const nCB = len & 0xfffffe00; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 23: if (len & 0x100) { const nCB = len & 0xffffff00; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 24: if (len & 0x80) { const nCB = len & 0xffffff80; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 25: if (len & 0x40) { const nCB = len & 0xffffffc0; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 26: if (len & 0x20) { const nCB = len & 0xffffffe0; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 27: if (len & 0x10) { const nCB = len & 0xfffffff0; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 28: if (len & 0x8) { const nCB = len & 0xfffffff8; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 29: if (len & 0x4) { const nCB = len & 0xfffffffc; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 30: if (len & 0x2) { const nCB = len & 0xfffffffe; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } case 31: if (len & 0x1) { const nCB = len & 0xffffffff; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } } if (array[start+len|0] !== sValue) { // remove this if-statement to return the next closest // element going downwards from the searched-for value // OR 0 if the value is less than all values in the // array. https://stackoverflow.com/a/44981570/5601591 return -1 - start - len |0; } return start + len |0; }; }

演示:

 (function(document){"use strict"; var textarea = document.getElementById('inputbox'), searchinput = document.getElementById('search'), searchStart = document.getElementById('start'), searchedLength = document.getElementById('length'), resultbox = document.getElementById('result'), timeoutID = -1; function doUpdate(){ try { 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 = refSort(txt, arr); textarea.value = textmtchs[0] + txt.join(',') + textmtchs[textmtchs.length-1]; arr = JSON.parse(textarea.value); resultbox.value = goodBinarySearch(arr, searchval, start, sub); } catch(e) { resultbox.value = 'Error'; } } textarea.oninput = searchinput.oninput = searchStart.oninput = searchedLength.oninput = textarea.onkeyup = searchinput.onkeyup = searchStart.onkeyup = searchedLength.onkeyup = textarea.onchange = searchinput.onchange = searchStart.onchange = searchedLength.onchange = function(e){ clearTimeout( timeoutID ); timeoutID = setTimeout(doUpdate, e.target === textarea ? 384 : 125); } function refSort(targetData, refData) { var indices = Object.keys(refData); indices.sort(function(indexA, indexB) { if (refData[indexA] < refData[indexB]) return -1; if (refData[indexA] > refData[indexB]) return 1; return 0; }); return indices.map(function(i){ return targetData[i] }) } function goodBinarySearch(array, sValue, ARG_start, ARG_len){ // Range of [start, start+len): only start is inclusive. It works // similarly to "...".substr(start, len).indexOf(sValue) // `void 0` is shorthand for `undefined` var start = (ARG_start === void 0 ? 0 : ARG_start) | 0; var len = (ARG_len === void 0 ? (array.length|0) - start : ARG_len) |0; len = len - 1 |0; if (len & 0x80000000) { const nCB = len & 0x80000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x40000000) { const nCB = len & 0xc0000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x20000000) { const nCB = len & 0xe0000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x10000000) { const nCB = len & 0xf0000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x8000000) { const nCB = len & 0xf8000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x4000000) { const nCB = len & 0xfc000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x2000000) { const nCB = len & 0xfe000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x1000000) { const nCB = len & 0xff000000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x800000) { const nCB = len & 0xff800000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x400000) { const nCB = len & 0xffc00000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x200000) { const nCB = len & 0xffe00000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x100000) { const nCB = len & 0xfff00000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x80000) { const nCB = len & 0xfff80000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x40000) { const nCB = len & 0xfffc0000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x20000) { const nCB = len & 0xfffe0000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x10000) { const nCB = len & 0xffff0000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x8000) { const nCB = len & 0xffff8000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x4000) { const nCB = len & 0xffffc000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x2000) { const nCB = len & 0xffffe000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x1000) { const nCB = len & 0xfffff000; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x800) { const nCB = len & 0xfffff800; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x400) { const nCB = len & 0xfffffc00; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x200) { const nCB = len & 0xfffffe00; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x100) { const nCB = len & 0xffffff00; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x80) { const nCB = len & 0xffffff80; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x40) { const nCB = len & 0xffffffc0; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x20) { const nCB = len & 0xffffffe0; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x10) { const nCB = len & 0xfffffff0; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x8) { const nCB = len & 0xfffffff8; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x4) { const nCB = len & 0xfffffffc; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x2) { const nCB = len & 0xfffffffe; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (len & 0x1) { const nCB = len & 0xffffffff; len ^= (len ^ (nCB-1)) & ((array[start+nCB|0] <= sValue |0) - 1 >>>0); } if (array[start+len|0] !== sValue) { // remove this if-statement to return the next closest // element going downwards from the searched-for value // OR 0 if the value is less than all values in the // array. https://stackoverflow.com/a/44981570/5601591 return -1 - start - len |0; } return start + len |0; } })(document);
 <h3 style="margin:.125em;width:100%;text-align:center">The Array (Must Be A Valid JSON Array)</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-layout:fixed;font-size:1.2em" width="100%"><tbody> <tr> <td colspan="3">Search Value: <input type="text" id="search" value="-12" style="width:8em;text-align:center;float:right" /></td> <td></td> <td colspan="3">Resulting Index: <input type="text" id="result" value="1" style="width:8em;text-align:center;float:right" readonly="" /> </tr> <tr> <td colspan="3">Start Index: <input type="text" id="start" value="" placeholder="(0)" style="width:8em;text-align:center;float:right" /></td> <td></td> <td colspan="3">Searched Length: <input type="text" id="length" value="" placeholder="(array length)" style="width:8em;text-align:center;float:right" /> </tr> </tbody></table>

您還可以在演示中使用字符串數組(用引號括起來),它應該可以正常工作。 要搜索字符串,您必須在搜索值周圍加上引號。

這是二進制搜索功能,您可以查看

   function bsearch (Arr,value){
        var low  = 0 , high = Arr.length -1 ,mid ;      
        while (low <= high){
            mid = Math.floor((low+high)/2);     
            if(Arr[mid]==value) return mid ; 
            else if (Arr[mid]<value) low = mid+1;
            else high = mid-1;          
        }
        return -1 ;
    }

二分搜索 (ES6)

自下而上:

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

  while (start <= end) {
    let 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);
}

做的有點不一樣。 看一看

function binarySearch(arr, n) {
  let min = 0;
  let max = arr.length - 1;
  let mid;
  while (min <= max) {
    mid = (min + max) >>> 1;
    if (arr[mid] === n) {
      return mid;
    } else if (arr[mid] < n) {
      min = mid + 1;
    } else {
      max = mid - 1;
    }
  }

  return -1;
}

binarySearch([1,2,3,5,56], 2);

如果有人想要,請發布答案

  1. 遞歸和非遞歸方式
  2. 直接評論

遞歸方式

 /** * Binary Search - With recursive * @param arr - Input Array * @param searchElement - Element to search * @param left - Left index * @param right - Right index */ function binarySearch(arr, searchElement, left, right) { let mid = Math.floor((left + (right + 1)) / 2); // using floor as we may get floating numbers if (left <= right) { // If it is not the last element, process further, else return -1 if (arr[mid] === searchElement) { // element found at mid return mid; // no need to process further } else if (searchElement < arr[mid]) { // element might be in first half right = mid - 1; // right is mid - 1 because we know that mid is not correct element } else { // element might be in second half left = mid + 1; // left is mid + 1 because we know that mid is not correct element } return this.binarySearch(arr, searchElement, left, right); // recursive } return -1; // element not found } // Invoking console.log(binarySearch([1,2,3,4,5], 2, 0, 4));

非遞歸方式

 /** * Binary Search - Without using recursive * @param arr - Input Array * @param searchElement - Element to search */ function binarySearch(arr, searchElement) { let left = 0, right = arr.length - 1; while (left <= right) { // Process until it is last element let mid = Math.floor((left + (right + 1)) / 2); // using floor as we may get floating numbers if (arr[mid] === searchElement) { // element found at mid return mid; // no need to process further } if (searchElement < arr[mid]) { // element might be in first half right = mid - 1; // right is mid - 1 because we know that mid is not correct element } else { // element might be in second half left = mid + 1; // left is mid + 1 because we know that mid is not correct element } } return -1; // element not found } // Invoking console.log(binarySearch([1, 2, 3, 4, 5], 2));

這是我的解決方案!

// perform a binarysearch to find the position in the array
function binarySearch(searchElement, searchArray) {
    'use strict';

    var stop = searchArray.length;
    var last, p = 0,
        delta = 0;

    do {
        last = p;

        if (searchArray[p] > searchElement) {
            stop = p + 1;
            p -= delta;
        } else if (searchArray[p] === searchElement) {
            // FOUND A MATCH!
            return p;
        }

        delta = Math.floor((stop - p) / 2);
        p += delta; //if delta = 0, p is not modified and loop exits

    }while (last !== p);

    return -1; //nothing found

};

如果沒有完全匹配,則此問題的一個變體是查找最接近搜索X的元素。

為此,我們調整@Alexander Ryzhov 的答案,使其始終返回“插入點”= 大於或等於X元素中最小元素的索引。

一旦我們得到結果索引I ,我們檢查I (可能是X或大於X )和I-1 (較小)的元素,並選擇兩者中最接近的。 不要忘記處理邊緣情況!

 function binarySearch(a, compare) { let le = 0, ri = a.length - 1; while (le <= ri) { let mid = (le + ri) >> 1, cmp = compare(a[mid]); if (cmp > 0) { le = mid + 1; } else if (cmp < 0) { ri = mid - 1; } else { return mid; } } return le; } function binaryClosest(a, compare) { let i = binarySearch(a, compare); if (i === 0) return a[0]; if (i === a.length) return a[i - 1]; let d1 = -compare(a[i]), d2 = compare(a[i - 1]); return d1 < d2 ? a[i] : a[i - 1]; } // input = [-3, -2, -1, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3] findX = x => binaryClosest(input, item => x - item) test = (exp, arg) => { let x = findX(arg) console.log(exp === x ? 'ok' : 'FAIL', arg, exp, x) }; test(-3, -99) test(+3, +99) test(0, +0.3) test(0, 0) test(0, -0.3) test(-1, -1.3) test(+1, +1.3) test(2, 2.2) test(2, 2.3) test(2, 2.5) test(3, 2.6)

一個遞歸二進制搜索函數,它將返回被搜索元素的index

function binarySearch(arr, target, idx=0){
  let full_len = arr.length;
  if(full_len === 0){
    return null;
  }
  let mid = Math.floor(full_len / 2);
  if(arr[mid] === target){
    return `INDEX of ${target} is: ${idx+mid}`;
  }else if(target > arr[mid]){
    let right = arr.slice(mid + 1, full_len);
    idx += (full_len - right.length);
    return binarySearch(right, target, idx);
  }else if(target < arr[mid]){
    let left = arr.slice(0, mid);
    return binarySearch(left, target, idx);
  }
}

//Testing:

var arr = [1, 27, 34, 42, 58, 69, 71, 85, 96, 151];
console.log(binarySearch(arr, 1)); //=> 0
console.log(binarySearch(arr, 27)); //=> 1
console.log(binarySearch(arr, 34)); //=> 2
console.log(binarySearch(arr, 42)); //=> 3
console.log(binarySearch(arr, 58)); //=> 4
console.log(binarySearch(arr, 69)); //=> 5
console.log(binarySearch(arr, 71)); //=> 6
console.log(binarySearch(arr, 85)); //=> 7
console.log(binarySearch(arr, 96)); //=> 8
console.log(binarySearch(arr, 151)); //=> 9
arr = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
console.log(binarySearch(arr, 10)); //=> 0
console.log(binarySearch(arr, 20)); //=> 1
console.log(binarySearch(arr, 30)); //=> 2
console.log(binarySearch(arr, 40)); //=> 3
console.log(binarySearch(arr, 50)); //=> 4
console.log(binarySearch(arr, 60)); //=> 5
console.log(binarySearch(arr, 70)); //=> 6
console.log(binarySearch(arr, 80)); //=> 7
console.log(binarySearch(arr, 90)); //=> 8
console.log(binarySearch(arr, 100)); //=> 9

var bigArr = [];
for(var i = 1; i <= 1000000; i++){
  bigArr.push(i);
}
console.log(binarySearch(bigArr, 5342)) //=> 5341
console.log(binarySearch(bigArr, 254369)) //=> 254368
console.log(binarySearch(bigArr, 2000000)) //=> null
console.log(binarySearch(bigArr, -1)) //=> null

這里有很多過於復雜的答案! 如果找到值,您想返回索引; 否則返回該值所在的索引的負數,如果它已經在數組中。 您還希望它足夠通用以處理除數字之外的語言表達式:

function BinarySearch(array, value) {
    let high = array.length - 1;
    let low = 0;

    if (value < array[low]) 
        return -1;
    if (value > array[high]) 
        return -(high + 1);

    while (high >= low) {
        var mid = (high + low) >> 1;

        if (value === array[mid])
            return mid;
        else if (value < array[mid])
            high = mid - 1;
        else
            low = mid + 1;
    }

    return -(mid + 1);
}

var c = ['alpha','apple','beta','delta','gamma','zebra'];
console.log(BinarySearch(c,'delta')); // return 3
console.log(BinarySearch(c,'beet')); // return -2
var a = [1,2,3,4,6,7,8,9,10];
var b = [1,2,3,4,5,6,7,8,9,10];
console.log(BinarySearch(a,5)); // return -4
console.log(BinarySearch(a,11)); // return -9
console.log(BinarySearch(b,5)); // return 4
console.log(BinarySearch(b,0)); // return -1

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

function BinarySearch(array, value) {
    let high = array.length - 1;
    let low = 0;

    if (value < array[low] || value > array[high]) 
        return false;

    while (high >= low) {
        let mid = (high + low) >> 1;

        if (value === array[mid])
            return true;
        else if (value < array[mid])
            high = mid - 1;
        else
            low = mid + 1;
    }

    return false;
}

這是一個函數式編程風格的 ES6 函數,如果沒有提供,則使用默認的比較函數:如果查找的值是數字類型,則假定比較數字,否則采用字符串比較。

 function binarySearch(arr, val, compFunc = (a, b) => typeof val == 'number' ? ab : a.localeCompare(b), i=0, j=arr.length) { return i >= j ? i : ( mid => ( cmp => cmp < 0 ? binarySearch(arr, val, compFunc, i, mid) : cmp > 0 ? binarySearch(arr, val, compFunc, mid+1, j) : mid ) (compFunc(val, arr[mid])) ) (i + j >> 1); } ///////// Tests /////////////////// function check(arr, val, compFunc) { var fmt = JSON.stringify; var result = binarySearch(arr, val); // default compFunc is assumed console.log(`binarySearch(${fmt(arr)}, ${fmt(val)}) == ${fmt(result)}`); if (result > arr.length || result < 0 || !arr.length && result || result < arr.length && compFunc(val, arr[result]) > 0 || result > 0 && compFunc(val, arr[result-1]) < 0) throw "Unexpected result!!!" } // Tests with numeric data: for (var val = 0; val < 12; val++) check([1, 2, 4, 6, 9, 9, 10], val, (a,b) => ab); // Test with empty array: check([], 12, (a,b) => ab); // Test with string data: check(['abc', 'deaf', 'def', 'g'], 'dead', (a, b) => a.localeCompare(b));

遞歸地將搜索范圍除以一半,直到達到合適的值或超出搜索范圍:

const binarySearch = (arr, item, [low=0, high=arr.length-1]=[]) => {
  const mid = Math.floor((low + high)/2)

  if (low > high) return -1 // is overshooting the range?
  if (arr[mid] === item) return mid // is item found?

  return binarySearch(arr, item, [
    item > arr[mid] ? mid+1 : low,
    item < arr[mid] ? mid-1 : high
  ])
}

讓我們測試一下:

// positive
console.log(binarySearch([2, 6, 9, 14, 21],  9)) // => 2
console.log(binarySearch([2, 6, 9, 14, 21], 21)) // => 4
console.log(binarySearch([2, 6, 9, 14, 21],  2)) // => 0

// negative
console.log(binarySearch([2, 6, 9, 14, 21],  0)) // => -1
console.log(binarySearch([2, 6, 9, 14, 21], -4)) // => -1
console.log(binarySearch([2, 6, 9, 14, 21], 40)) // => -1

您可以在排序數組中實現二進制搜索算法為

function binarySearch(sortedArray, searchNumber) {
  var lowNumberIndex = 0;
  var highNumberIndex = sortedArray.length - 1;
  var prevMiddleIndex = 0;

  while (lowNumberIndex <= highNumberIndex) {
    var middleIndex = Math.floor((lowNumberIndex + highNumberIndex) / 2);
    if (middleIndex === prevMiddleIndex) {
      middleIndex = middleIndex + 1;
    }
    prevMiddleIndex = middleIndex;
    if (sortedArray[middleIndex] === searchNumber) {
      return middleIndex;
    } else if (searchNumber > sortedArray[middleIndex]) {
      lowNumberIndex = middleIndex;
    } else {
      highNumberIndex = middleIndex;
    }
  }
  return -1;
}

console.log(binarySearch([1, 3, 5, 6, 7, 8, 9], 8));


如果在數組中找不到搜索元素,則返回 -1 ,否則返回數組中數字的索引。

這里我們使用prevMiddleIndex變量來跟蹤middleIndex以避免陷入同一循環。

例如:如果

lowerNumberIndex = 5;
higherNumberIndex = 6;

中間索引

middleIndex = Math.floor((lowerNumberIndex + higherNumberIndex)/2)

在上面的示例代碼中將始終返回 5,從而導致永無止境的 while 循環。

這是工作小提琴 - https://jsfiddle.net/ankit417/2fL4oyex/16/

沒有突變和遞歸

 let A = [...Array(100).keys() ].map((x) => Math.floor(Math.random() * 1000)).sort((a,b) => ab) const binarySearch = (A, a, b, k) => { const m = Math.floor((b + a)/ 2); console.log(a, m, b, k) if(A[m] === k) { return m; } if(A[a] <= k && k <= A[m]) { return binarySearch(A, a, m, k) } if(A[m] < k && k <= A[b]) { return binarySearch(A, m+1, b, k) } return -1; } console.log(`key ${A[30]}`); rez = binarySearch(A, 0, 100, A[30]) console.log(`rez is ${rez} and index of ${A[30]} is ${A.indexOf(A[30])}`); rez = binarySearch(A, 0, 100, 666) console.log(`rez is ${rez} and index of ${666} is ${A.indexOf(666)}`);

zigbinarySearch的端口:

  • 使用<而不是<=所以少了一個比較
  • 以上允許使用 unsigned int 來表示right - 對於 zig 或 rust 等語言
  • 計算中間而不溢出mid - 對於 zig 或 rust 等語言
const binarySearch = (key, items, compareFn) => {
  let left = 0;
  let right = items.length;
  while (left < right) {
    const mid = left + Math.floor((right - left) / 2);
    const cmp = compareFn(key, items[mid]);
    if (cmp === 0) return mid;
    if (cmp < 0) right = mid;
    else left = mid + 1;

    // happens when right = items.length - 1 and key = 2 and items = [3]
    if (right < 0) throw new Error("right is < 0");
  }
  return -1;
};

const compareFn = (a, b) => a - b;
console.log(binarySearch(33, new Int16Array([1, 3, 4, 2, 33, 20, 10, 12, 34]).sort(), compareFn));
console.log(binarySearch(2, new Int16Array([3]).sort(), compareFn));

 const SearchArray =(Listarr, Key)=>{ const midvalues = Math.floor(Listarr.length/2) if(Key===Listarr[midvalues]) return true else if(Listarr[midvalues]<=Key && midvalues>0 ) return SearchArray(Listarr.slice(midvalues,Listarr.length), Key) else if(Listarr[midvalues]>=Key && midvalues>0 ) return SearchArray(Listarr.slice(0, midvalues), Key) else return "No record found" } const 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))

假設一個排序數組,這里是一個遞歸二分查找:

function binSearch(needle, arr) {
  length = arr.length;
  while(length > 1) {
    midpoint = Math.floor(length/2);
    return (needle > arr[midpoint-1]) ? 
           binSearch(needle, arr.splice(midpoint, length)) :    
           binSearch(needle, arr.splice(0, midpoint));
  }
  return needle === arr[0] ? arr[0] : -1;
}

您還應該檢查“未找到值”的邊緣情況,並將其設為您的第一個基本條件,然后成功搜索。 因此,在遍歷數組時,您不需要檢查數組長度是否大於 1。 最后,既然你不返回數組,為什么不使用 Array.prototype.slice 方法呢?

var binarySearch = function(arr, val) {
  var half = Math.floor(arr.length / 2);

  if (arr.length === 0) {
    return -1;
  }
  else if (arr[half] === val) {
    console.log("val: ", val, "arr[half]: ", arr[half]);
    return val;
  }
  else if (val > arr[half]) {
    console.log("val: ", val, "arr[half]: ", arr[half]);
    return binarySearch(arr.slice(half, arr.length), val);
  }
  else {
    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(function(a, b) { return a - b });

console.log("Sorted array: " + arr);
console.log(binarySearch(arr, 76));
console.log(binarySearch(arr, 19));
console.log(binarySearch(arr, 0));

我想將我的searchArray二進制搜索函數以及工具實用程序函數insertSortedArrayremoveSortedArray到答案列表中,因為我認為它很干凈,它是全局使用的,而且我認為它的速度非常理想。

下午好,我知道這篇文章是在一段時間前開始的,但我認為我可以為討論做出貢獻。

function binarySearch(array, target, max, min) {

    //Find the Midpoint between forced max and minimum domain of the array
    var mid = ((max - min) >> 1) + min;
    //alert("Midpoint Number" + mid);
    console.log(mid);
    console.log(array[mid], "target: " + target);

    if (array[mid] === target) {
        //Target Value found
        console.log('match', array[mid], target);
        //alert('match', array[mid], target);
        return mid;
    } 
    else if (mid === 0)
    {
        //All Value have been checked, and none are the target value, return sentinel value
        return -1;
    }
    else if (array[mid] > target)
    {
        //Value at Midpoint is greater than target Value, set new maximum to current midpoint
        max = mid;
        console.log('mid lower', array[mid], target);
        //alert('mid lower', array[mid], target);
        //Call binarySearch with new search domain
        return binarySearch(array, target, max, min);
    } 

    else if (array[mid] < target)
    {
        // Value at Midpoint is less than the target Value, set new minimum to current midpoint
        min = mid;
        console.log('mid higher', array[mid], target);
        //alert('mid higher', array[mid], target);

        //Call binarySearch with new search domain
        return binarySearch(array, target, max, min);
    } 

我確信這里有改進的空間,但是這種方法可以防止必須執行數組的深層復制(在處理大型數據集時這可能是一項代價高昂的操作),同時它不會修改數組以任何方式。

希望有幫助! 謝謝,傑里米

function binarySearch(a = [], x) {
    let length = a.length;
    if (length === 0) {
        return false;
    } else {
        let m = Math.floor(length/2);
        let y = a[m];
        if (x === y) {
            return true;
        } else if (x < y) {
            return binarySearch(a.slice(0, m), x);
        } else {
            return binarySearch(a.slice(m + 1), x);
        }
    }
}

如果您仍然有興趣在此處查看更多實現,我在 github 上有一個實現,其中比較了二進制和線性以及測試頁面

我剛剛在 code review 中發現了這個splice實現,所以我覺得澄清這個實現有多糟糕很重要。

首先 BinarySearch 是一種算法,它在排序數組中的O(log(n))中找到一個索引,我們可以在其中插入我們的項目並且仍然有排序數組(我們也可以使用它來找到最接近的項目 - 所以在評論中回答一些問題- 有返回值的意義 - 它可以與您查詢的值不同)

splice實現中存在重大設計失敗 - 搜索算法修改了查詢的數組。 想象一下,你有一個數據庫,你查詢SELECT * FROM data WHERE id=1並且你的表的一半被刪除了。 在傳遞給 BinarySearch 之前克隆數組不是很有幫助,但我將在下一段解釋原因。

所以讓我們修復這個設計失敗並使用一個新的函數slice ,它的工作方式與splice相同,但它不會修改數組(只返回選定的元素)。 算法還是有很大的缺陷。 對於n=2^m數組,我們進行m測試。 首先我們將從slice n/2元素返回,下一次是n/4 ,然后是n/8等等。 如果我們總結一下,它將是n-1元素。 所以我們有O(n)算法,它和線性搜索一樣快,但要復雜得多(線性搜索甚至更快,因為它的平均成本是n/2並且slice BinarySearch 距離n-1 )。 原來的splice實現更糟糕——每個splice都需要額外移動表中的元素,所以在最壞的情況下,第一次將是nn/2第三n/4所以最后它會是2 * n - 1 -這就是為什么克隆數組不是很有幫助的原因(克隆是O(n)所以在傳遞給好的二進制搜索算法之前不要克隆你的數組)

 function binarySearch(arr, num, l, r){ if( arr instanceof Array ){ l = isNaN(l) ? 0 : l; r = isNaN(r) ? arr.length - 1: r; let mid = l + 1 + Math.round((r - l)/2 - 1); console.log(l, r, mid, arr[mid]); if( num == arr[mid] ){ console.log("found"); return mid; } if( typeof arr[mid] == "undefined" || l == r ){ console.log("not found"); return -1; } if( num < arr[mid] ){ console.log("take left"); return binarySearch(arr, num, l, r - mid); } console.log("take right"); return binarySearch(arr, num, mid, r); } } console.log( binarySearch([0, 0, 1 ,1, 2, 3, 5, 6, 11], 2) );

讓我們假設數組已排序(編寫您自己的排序算法或僅使用內置方法)

function bSearch(array,item){
  var start = 0,
  end = item.length - 1;
  var middle = Math.floor((end+start)/2);
  while(array[middle] !== item && start < end){
    if(item < array[middle]){
      end = middle-1;
     }
    else if(item > array[middle]){
      start = middle+1;
     }
     middle = Math.floor((end+start)/2);

  } 
  if(array[item]!== middle){
     console.log('notFoundMate);
     return -1;
  }
  else {
     console.log('FoundMate);
     return middle;
  }
}

只需 在此處檢查lodash實現

要使用它,只需按原樣復制粘貼即可,以使用局部變量來提高速度。 並修改搜索值,就像在子對象或數組中搜索一樣:

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

為了獲得更快的結果,請使用數組或數組數組或並行數組,將搜索到的數組復制到局部變量。 非局部變量或每次執行 obj.something 它都會變慢。

這是最快的版本是這樣的:

let array=[3,4,5,6]
let value=5; //searched value
let mid, low = 0,high = array.length;
while (low < high) {
    mid = low + high >>> 1; // fast divide by 2 and round down
    if (array[mid] < value) low = mid + 1;
    else high = mid;
}
//if (array[mid] != value) mid=-1;// this might not be required if you look for place to insert new value
mid;// contains the found value position or if not found one before where it would be if it was found

二分查找的工作原理是這樣的:

|           .           |     // find middle
//option 1:
|           .     v     |     // if value on right, middle is top
            |     .     |     // so then it is like this
//option 2:                    
|     v     .           |     // if value on left, middle is bottom
|     .     |                 // so then it is like this
//more loops of option 2 until not found
|  .  |                       // next time it will be like this
|. |                          // next time it will be like this
.                             // next time it will be like this

如果未找到,此實現將進入底部。 它可能總是被發現或不被發現。 它返回低於或等於搜索值的索引。 所以你需要檢查它是否等於。 驗證該值是否存在或者它只是下面的一個結果。 如果您正在尋找插入客棧訂單的地方,只需將其放在該位置,無需檢查是否等於

我認為下面的選項很容易在 JS 中實現二進制搜索。

arr = [1,2,3,4,5];
searchTerm=2;
function binarySearchInJS(start,end)
{
    isFound=false;
    if(end > start)
    {
        //console.log(start+"\t"+end)
        mid =  (end+start)/2;

        mid = Math.floor(mid)

        if(searchTerm==arr[mid])
        {                   
              isFound = true;             
        }
        else
        {   

            if(searchTerm < arr[mid])
            {               
                binarySearchInJS(start,mid);
            }
            if(searchTerm > arr[mid])
            {           
                binarySearchInJS(mid+1,end);
            }
        }
    }

    if(isFound)
    {
        return "Success";   
    }
    else{
            return "Not Found"; 
    }       
}

全功能二進制搜索:

  • 負值表示插入點
  • 允許搜索第一個和最后一個索引
  • 開始索引,獨占結束索引
  • 自定義比較功能

(此代碼和單元測試在這里

function defaultCompare(o1, o2) {
    if (o1 < o2) {
        return -1;
    }
    if (o1 > o2) {
        return 1;
    }
    return 0;
}

/**
 * @param array sorted array with compare func
 * @param item search item
 * @param start (optional) start index
 * @param end (optional) exclusive end index
 * @param compare (optional) custom compare func
 * @param bound (optional) (-1) first index; (1) last index; (0) doesn't matter
 */
function binarySearch(array, item, start, end, compare, bound) {
    if (!compare) {
        compare = defaultCompare;
    }
    let from = start == null ? 0 : start;
    let to = (end == null ? array.length : end) - 1;
    let found = -1;
    while (from <= to) {
        const middle = (from + to) >>> 1;
        const compareResult = compare(array[middle], item);
        if (compareResult < 0) {
            from = middle + 1;
        }
        else if (compareResult > 0) {
            to = middle - 1;
        }
        else if (!bound) {
            return middle;
        }
        else if (bound < 0) {
            // First occurrence:
            found = middle;
            to = middle - 1;
        }
        else {
            // Last occurrence:
            found = middle;
            from = middle + 1;
        }
    }
    return found >= 0 ? found : -from - 1;
}

開始了。

let a = [1, 2, 4, 6, 1, 100, 0, 10000, 3];

a.sort(function (a, b) {
    return a - b;
});

function binarySearch(arr,value){
        if(arr.length === 0) return -1;
        var low  = 0 , high = arr.length -1 ,mid ;      
        while (low <= high){
            mid = Math.floor((low+high)/2);     
            if(arr[mid]==value) return mid ; 
            else if (arr[mid]<value) low = mid+1;
            else high = mid-1;          
        }
        return -1 ;
}
console.log(binarySearch(a,1005))

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

您不能對未排序的數組使用二分搜索。 你應該有 [1, 4, 13, 77 ...] 但不是 [1, 2, 13, 5, 17 ...] ...我的錯,你做了 sort()

暫無
暫無

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

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