有序数据集合的二分查找
-
定义
- 二分查找针对的是一个有序的数据集合。每次都通过跟区间的中间元素对比,将待查找的区间缩小为之前的一半,直到找到要查找的元素,或者区间被缩小为 0。
-
时间复杂度分析:
- 数据大小n,每次查找,缩小1/2
- 最多查找k次。n/2k=1
- k=O(logn)
-
应用场景
- 二分查找依赖顺序表结构(下标访问)
- 二分查找针对的是有序数据(不适用于排序变化的场景)
- 数据量太小不适合二分查找
- 数据量太大也不适合二分查找(需要连续内存)
-
二分查找的循环实现
let arr = []
//随机数组元素
for (let i = 0; i < 40; i++) {
arr.push(Math.round(Math.random() * 40))
}
//排序
arr.sort((a, b) => a - b)
console.log(String(arr))
//随机查找值
const value = Math.round(Math.random() * 40)
console.log(value, bsearch(arr, value))
function bsearch(arr, value) {
//指针low从左向右,指针high从右向左
let low = 0
let high = arr.length
/**
* 实现
* 1. low大于high时,跳出循环
* 2. 取中间值,中间值等于value,则查找成功
* 3. 中间值小于value,则low指向mid+1
* 4. 中间值大于value,则high指向mid-1
*/
/**
* 注意
* 1. low等于high,也是一种情况
* 2. 避免溢出:mid = low + ((high-low)>>1)
* 3. low和high更新,low = mid或high=mid,可能死循环
*/
while (low <= high) {
let mid = (low + high) >> 1
if (arr[mid] == value) {
return mid
} else if (arr[mid] < value) {
low = mid + 1
} else {
high = mid - 1
}
}
return -1
}
- 二分查找的递归实现
let arr = []
//随机数组元素
let num = 10
for (let i = 0; i < num; i++) {
arr.push(Math.round(Math.random() * num))
}
//排序
arr.sort((a, b) => a - b)
console.log(String(arr))
//随机查找值
const value = Math.round(Math.random() * num)
console.log(value, bsearch(arr, 0, arr.length, value))
function bsearch(arr, low, high, value) {
/**
* 递归公式:判断中间值
* 1. 等于value则返回
* 2. 小于value则向右查找
* 3. 大于value则向左查找
* 终止条件:low > high或已找到
*/
if (low > high) return -1
//优先级:+ 大于 >>
let mid = low + ((high - low) >> 1)
if (arr[mid] == value) {
return mid
} else if (arr[mid] < value) {
return bsearch(arr, mid + 1, high, value)
} else {
return bsearch(arr, low, mid - 1, value)
}
}
- 求一个数的平方根
//平方根位数
var weishu = 6
//测试数据
for (let i = 0; i < 1000; i++) {
let data = Math.random() * 100
let diff = Math.pow(10, weishu) * (fun(data) - Math.sqrt(data))
console.log(fun(data), diff <= 0 && diff > -1)
}
function fun(num) {
//返回结果
let res
if (num == 1) {
res = 1
} else {
res = 0
for (let i = 0; i <= weishu; i++) {
recursive(Math.pow(10, -1 * i))
}
//舍入误差解决
let expend = Math.pow(10, weishu)
res = Math.round(res * expend) / expend
}
//小数点后weishu位
return res.toFixed(weishu)
/**
* 0. 循环,每次cur增加digit
* 1. 退出循环,cur方 <= num < (cur + digit*n)方,查询成功
*/
function recursive(digit) {
let end = 10
//整数部分
if (digit == 1) {
end = num
}
for (let i = 0; i < end; i++) {
if (num >= powValue(res + i * digit) && num < powValue(res + (i + 1) * digit)) {
res = res + i * digit
return
}
}
}
function powValue(value, param = 2) {
return Math.pow(value, param)
}
}