Javascript 二分查找/插入性能

标签 javascript arrays binary-search

function binarySearch(value)
{
    var startIndex = 0,
        stopIndex = words.length - 1,
        middle = Math.floor((stopIndex + startIndex) / 2);

    while (words[middle] != value && startIndex < stopIndex) {
        // adjust search area
        if (value < words[middle]) {
            stopIndex = middle - 1;
        } else if (value > words[middle]) {
            startIndex = middle + 1;
        }

        // recalculate middle
        middle = Math.floor((stopIndex + startIndex) / 2);
    }
}

我正在以数组的格式制作一个大的单词列表:

例如["a","ab","abc","b"]

按字母顺序排列。我遇到的问题是修改我的二分搜索算法以将单词添加到正确的位置然后更新?

从性能 Angular 来看,将单词添加到有序数组中的最佳方法是什么?为什么这是最好的方法?

最佳答案

为了实现高效的二分搜索插入,您需要让二分搜索返回一些内容,以指示如果未找到该字符串,该字符串应属于数组中的位置。

在其他语言中执行此操作的可接受方法是返回字符串所属索引的按位补码。 0的按位补是-1,1的按位补是-2,2的按位补是-3,依此类推。要在 JavaScript 中获取数字的按位补码,请使用 ~运算符。

示例代码:

/* 
    target: the object to search for in the array
    comparator: (optional) a method for comparing the target object type
    return value: index of a matching item in the array if one exists, otherwise the bitwise complement of the index where the item belongs
*/
Array.prototype.binarySearch = function (target, comparator) {
    var l = 0,
        h = this.length - 1,
        m, comparison;
    comparator = comparator || function (a, b) {
        return (a < b ? -1 : (a > b ? 1 : 0)); /* default comparison method if one was not provided */
    };
    while (l <= h) {
        m = (l + h) >>> 1; /* equivalent to Math.floor((l + h) / 2) but faster */
        comparison = comparator(this[m], target);
        if (comparison < 0) {
            l = m + 1;
        } else if (comparison > 0) {
            h = m - 1;
        } else {
            return m;
        }
    }
    return~l;
};

然后您可以使用binarySearch方法编写自己的binaryInsert函数:

/*
    target: the object to insert into the array
    duplicate: (optional) whether to insert the object into the array even if a matching object already exists in the array (false by default)
    comparator: (optional) a method for comparing the target object type
    return value: the index where the object was inserted into the array, or the index of a matching object in the array if a match was found and the duplicate parameter was false 
*/
Array.prototype.binaryInsert = function (target, duplicate, comparator) {
    var i = this.binarySearch(target, comparator);
    if (i >= 0) { /* if the binarySearch return value was zero or positive, a matching object was found */
        if (!duplicate) {
            return i;
        }
    } else { /* if the return value was negative, the bitwise complement of the return value is the correct index for this object */
        i = ~i;
    }
    this.splice(i, 0, target);
    return i;
};

一旦将这些方法原型(prototype)化到数组对象上,您就可以直接使用它们,如下所示:

var arr = [];
arr.binaryInsert("Zebra");
arr.binaryInsert("Aardvark");
arr.binaryInsert("Mongoose");
alert(arr);
/* [ "Aardvark", "Mongoose", "Zebra" ] */

随着项目数量的增加,这将比调用 Array.sort() 快得多

数组属性键污染

请注意,如上面的代码所示,将方法原型(prototype)化到 Array 对象上会导致这些方法显示为数组的可枚举属性,这可能会干扰您枚举 for(var i in arr) 中所有属性的任何逻辑。环形。以 for(var i=0; i<arr.length; i++) 格式编写的循环仍将按设计工作。

如果您不需要支持Internet Explorer 8或更低版本,则可以避免调用Array.prototype直接使用 Object.defineProperty如下例所示。

Object.defineProperty(Array.prototype, "binarySearch", {
value: function (target, comparator) {
    var l = 0,
        h = this.length - 1,
        m, comparison;
    comparator = comparator || function (a, b) {
        return (a < b ? -1 : (a > b ? 1 : 0));
    };
    while (l <= h) {
        m = (l + h) >>> 1;
        comparison = comparator(this[m], target);
        if (comparison < 0) {
            l = m + 1;
        } else if (comparison > 0) {
            h = m - 1;
        } else {
            return m;
        }
    }
    return~l;
}
});

Object.defineProperty(Array.prototype, "binaryInsert", {
value: function (target, duplicate, comparator) {
    var i = this.binarySearch(target, comparator);
    if (i >= 0) {
        if (!duplicate) {
            return i;
        }
    } else {
        i = ~i;
    }
    this.splice(i, 0, target);
    return i;
}
});

这种方法将避免污染可枚举键,因此 for(var i in arr)循环仍将按预期工作。

关于Javascript 二分查找/插入性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12369824/

相关文章:

javascript - 键名未知时无法访问 JSON 中的元素

javascript - 是否有任何跨浏览器的 javascript 使 vh 和 vw 单位工作

c# - VB 中的 COM 对象数组

java - 字符数组比较忽略大小写

javascript - PHP 多个查询字符串值到数组

c - 在单调递增然后递减的序列cera中找到一个数字

algorithm - 二进制搜索双调序列中的最大元素

javascript - React Native/Firebase 格式错误

javascript - null 是一个有效的 javascript 属性名称吗?

java - 如何在 Entry 类上调用 binarySearch() ?