100道前端面试题(十二): 2-15 用 JS 实现二分查找(TS+单元测试)

146 阅读1分钟

前言

(11)-1.webp

今天我们来看一下经典查找元素方式——二分法


样例输出

使用二分法,对元素进行查找(输入要排查的有有序数组和该元素,输出该元素在数组中索引号)

示例1:

输入:

arr = [10, 20, 30, 40, 50]

target = 40

输出:

indx = 3

示例2:

输入:

arr = [10, 20, 30, 40, 50]

target = 400

输出:

indx = -1

解题思路

方法一(循环)

代码实现

export function binarySearch1(arr: number[], target: number): number {
    const length = arr.length

    if (length === 0) return -1
    let startIndex = 0
    let endIndex = length - 1
    let midIndex = 0
    let  midValue = 0
    while (startIndex <= endIndex) {
        midIndex = Math.floor((startIndex + endIndex) / 2)
        midValue = arr[midIndex]
         if (target < midValue) {
            // 小于预期值说明应该向左排查
            endIndex = midIndex - 1
         }  else if (target > midValue) {
            // 大于预期值说明应该向右排查
            startIndex = midIndex + 1
         }  else {
            // 相等输出结果
            return midIndex
         }

    }

    return -1
}

方法二(递归)

代码实现

export function binarySearch2(arr: number[], target: number, startIndex?: number, endIndex?: number): number {
    const length = arr.length
    if (length === 0) return -1

    // 开始和结束的范围
    if (startIndex == null) startIndex = 0
    if (endIndex == null) endIndex = length - 1

    // 如果 start 和 end 相遇,则结束
    if (startIndex > endIndex) return -1

    // 中间位置
    const midIndex = Math.floor((startIndex + endIndex) / 2)
    const midValue = arr[midIndex]

    if (target < midValue) {
        // 目标值较小,则继续在左侧查找
        return binarySearch2(arr, target, startIndex, midIndex - 1)
    } else if (target > midValue) {
        // 目标值较大,则继续在右侧查找
        return binarySearch2(arr, target, midIndex + 1, endIndex)
    } else {
        // 相等,返回
        return midIndex
    }
}

性能测试

通过性能测试我们可以判断出,循环的性能更好。

// // 性能测试
// console.time('binarySearch1')
// for (let i = 0; i < 100 * 10000; i++) {
//     binarySearch1(arr, target)
// }
// console.timeEnd('binarySearch1') // 17ms
// console.time('binarySearch2')
// for (let i = 0; i < 100 * 10000; i++) {
//     binarySearch2(arr, target)
// }
// console.timeEnd('binarySearch2') // 34ms

为什么循环的性能会更好,因为递归每次都要重新调用一遍原函数,所以性能偏低

单元测试

describe('二分查找', () => {
    it('正常情况', () => {
        const arr = [10, 20, 30, 40, 50]
        const target = 40
        const index = binarySearch1(arr, target)
        expect(index).toBe(3)
    })

    it('空数组', () => {
        expect(binarySearch1([], 100)).toBe(-1)
    })

    it('找不到 target', () => {
        const arr = [10, 20, 30, 40, 50]
        const target = 400
        const index = binarySearch1(arr, target)
        expect(index).toBe(-1)
    })
})