主要内容:
- 顺序搜索
- 二分搜索
- 内插搜索
- 随机算法
接下来,进入主题。
顺序搜索
顺序搜索(线性搜索)是最基本的搜索算法。其将每一个数据结构中的元素和我们要找的元素做比较。顺序搜索是最低效的一种搜索算法。
图示:
代码实现:
/**
* 顺序搜索
* @param {array} array 查询数据源
* @param {function} equalsFn 比较函数
* @returns 符合条件的索引
*/
function linearSearch(array, equalsFn) {
const length = array.length;
for (let i = 0; i < length; i++) {
if (equalsFn(array[i])) return i;
}
return -1;
}
const list = [
{
name: "孙悟空",
age: 18,
},
{
name: "唐僧",
age: 19,
},
{
name: "猪八戒",
age: 20,
},
{
name: "沙僧",
age: 21,
},
];
const idx = linearSearch(list, (v) => {
return v.age === 18;
});
console.log(list[idx]);// {name: '孙悟空', age: 18}
二分搜索
二分搜索算法的原理和猜数字游戏类似,就是那个有人说“我正想着一个1~100的数”的游戏。我们每回应一个数,那个人就会说这个数是高了、低了还是对了。
这个算法要求被搜索的数据结构已排序。
图示:
算法实现步骤:
- 选择数组的中间值。
- 如果选中值是待搜索值,那么算法执行完毕(值找到了)。
- 如果待搜索值比选中值要小,则返回步骤1并在选中值左边的子数组中寻找(较小)。
- 如果待搜索值比选中值要大,则返回步骤1并在选中值右边的子数组中寻找(较大)。
代码实现:
/**
* 二分搜索
* @param {array} array 查询数据源
* @param {*} searchValue 查询值
* @param {string} key 对象键名
* @returns 符合条件的索引
*/
function binarySearch(array, searchValue, key) {
let low = 0,
high = array.length - 1;
// low比high小时,计算得到中间项索引并取得中间项的值
while (low < high) {
const mid = Math.floor((low + high) / 2);
const el = array[mid];
const value = key ? el[key] : el;
// 大于
if (value > searchValue) {
high = mid - 1;
} else if (value < searchValue) {
// 小于
low = mid + 1;
} else {
// 等于
return mid;
}
}
// low比high大,该搜索项不存在
return null;
}
// 已排序数组
const list = [
{ name: "A", idx: 1 },
{ name: "B", idx: 2 },
{ name: "C", idx: 3 },
{ name: "D", idx: 4 },
{ name: "E", idx: 5 },
{ name: "F", idx: 6 },
{ name: "G", idx: 7 },
{ name: "H", idx: 8 },
{ name: "I", idx: 9 },
];
console.log(list[binarySearch(list, 2, "idx")]);// {name: 'B', idx: 2}
const data =[1,2,3,4,5,6,7,8,9]
console.log(data[binarySearch(data, 2)]);// 2
内插搜索
内插搜索是二分搜索的改良版。二分搜索总是检查中间位置上的值,而内插搜索可能会根据要搜索的值检查数组中的不同地方。
前提被搜索的数据结构已排序。
实现步骤:
- 使用position公式选中一个值。
- 如果这个值是待搜索值,那么算法执行完毕(值找到了)。
- 如果待搜索值比选中值要小,则返回步骤1并在选中值左边的子数组中寻找(较小)。
- 如果待搜索值比选中值要大,则返回步骤1并在选中值右边的子数组中寻找(较大)。
代码实现:
const Compare = {
LESS_THAN: -1,
BIGGER_THAN: 1,
EQUALS: 0,
};
function defaultCompare(a, b) {
if (a === b) {
return Compare.EQUALS;
}
return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN;
}
function defaultEquals(a, b) {
return a === b;
}
function defaultDiff(a, b) {
return Number(a) - Number(b);
}
function lesserOrEquals(a, b, compareFn) {
const comp = compareFn(a, b);
return comp === Compare.LESS_THAN || comp === Compare.EQUALS;
}
function biggerOrEquals(a, b, compareFn) {
const comp = compareFn(a, b);
return comp === Compare.BIGGER_THAN || comp === Compare.EQUALS;
}
/**
* 内插搜索
* @param {array} array 查询数据源
* @param {*} searchValue 查询值
* @param {function} compareFn 比较大小
* @param {function} equalsFn 是否相等
* @param {function} diffFn 是否不同
* @returns 符合条件的索引
*/
function interpolationSearch(
array,
searchValue,
compareFn = defaultCompare,
equalsFn = defaultEquals,
diffFn = defaultDiff
) {
let low = 0,
high = array.length - 1,
position = -1,
delta = -1;
while (
low <= high &&
biggerOrEquals(searchValue, array[low], compareFn) &&
lesserOrEquals(searchValue, array[high], compareFn)
) {
delta =
diffFn(searchValue, array[low]) / diffFn(array[high], array[low]);
// 计算比较值的位置position。
// 如果查找的值更接近array[high]则查找position位置旁更大的值
// 如果查找的值更接近array[low]则查找position位置旁更小的值
position = low + Math.floor((high - low) * delta);
// 如果待搜索值找到了,则返回它的索引值
if (equalsFn(array[position], searchValue)) return position;
// 如果待搜索值小于当前位置的值,使用左边或右边的子数组重复这段逻辑
if (compareFn(array[position], (searchValue = Compare.LESS_THAN))) {
low = position + 1;
} else {
high = position - 1;
}
}
// 该搜索项不存在
return null;
}
// 已排序数组
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(data[interpolationSearch(data, 2)]); // 2
随机算法
原理: 迭代数组,从最后一位开始并将当前位置和一个随机位置进行交换。这个随机位置比当前位置小。这个算法可以保证随机过的位置不会再被随机一次(洗扑克牌的次数越多,随机效果越差)。
图示:
代码实现:
function swap(array, a, b) {
[array[a], array[b]] = [array[b], array[a]];
}
function shuffle(array) {
for (let i = array.length-1; i > 0; i--) {
const randomIdx = Math.floor(Math.random() * (i + 1));
swap(array, i, randomIdx);
}
return array;
}
console.log(shuffle([1, 2, 3, 4, 5]));// [4, 2, 1, 5, 3]