【问题标题】:Javascript Binary Search/Insertion PreformanceJavascript 二进制搜索/插入性能
【发布时间】:2012-09-04 09:21:50
【问题描述】:
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"]

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

在性能方面将单词添加到有序数组中的最佳方法是什么?为什么这是最好的方法?

【问题讨论】:

    标签: javascript arrays binary-search


    【解决方案1】:

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

    在其他语言中执行此操作的公认方法是返回字符串所属索引的按位补码。 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;
    };
    

    一旦将这些方法原型化到数组对象上,您就可以像这样直接使用它们:

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

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

    数组属性键污染

    请注意,在上面的代码中将方法原型化到 Array 对象会导致方法显示为数组的可枚举属性,这可能会干扰您在 for(var i in arr) 循环中枚举所有属性的任何逻辑。以for(var i=0; i&lt;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) 循环仍将按预期工作。

    【讨论】:

      【解决方案2】:

      最好的方法是改用树,因为对于数组,这样的操作很可能具有线性算法复杂度。

      如果您想坚持使用数组,我建议您使用splice 方法进行插入。

      【讨论】:

      • 你能指点我任何将数组转换为树的地方吗?它是一个二元的bascily?超过 600,000 字..
      • 我知道如何插入,如果您阅读了有关按字母顺序将其插入正确位置的问题
      • @LiamMcCann 你到底想用这些词做什么?
      • 自定义拼写检查器,将在下个月左右移动到树格式,但现在必须使用数组
      • @LiamMcCann 为什么不使用关联数组呢?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-01
      • 1970-01-01
      • 2021-02-20
      相关资源
      最近更新 更多