了解二分查找
二分查找大家应该都比较熟悉,每次去除一半的的查找范围,平均时间复杂度O(log n);
搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半;
二分查找的数组一定得是有序的,所以这次我的标题是基于快排的二分查找,当然你也可以基于插入排序、计数排序、选择排序等等,这里我们用经典的快排,
步骤:
(1) 将数组的第一个位置设置为下边界(0)。
(2) 将数组最后一个元素所在的位置设置为上边界(数组的长度减 1)。
(3) 若下边界等于或小于上边界,则做如下操作。
a. 将中点设置为(上边界加上下边界)除以 2。
b. 如果中点的元素小于查询的值,则将下边界设置为中点元素所在下标加 1。
c. 如果中点的元素大于查询的值,则将上边界设置为中点元素所在下标减 1。
d. 否则中点元素即为要查找的数据,可以进行返回。
快排的实现我往期文章里有,这里就不细说了;
实现二分查找
有时候我们不希望改变原数组的排列顺序,所以我加了个参数orderly,为true的时候,传入的原始数组就会变为有序数组,为false的时候,就不会改变原数组,一样为进行查找,但是得到的结果index会有问题,因为它是基于有序数组的,我打算的是orderly为false时接入顺序查找;
// 快排
function quickSort(array) {
function swap(array, m, n) {
let temp = array[m];
array[m] = array[n];
array[n] = temp;
}
function getPivot(array, left, right) {
let center = Math.floor((left + right) / 2);
// 排序
if (array[left] > array[center]) {
swap(array, left, center);
}
if (array[left] > array[right]) {
swap(array, left, right);
}
if (array[center] > array[right]) {
swap(array, center, right);
}
swap(array, center, right - 1);
return array[right - 1];
}
function recursion_quickSort(array, left, right) {
// 结束条件
if (left >= right) return;
// 获取枢纽
let pivot = getPivot(array, left, right);
// 记录开始位置
let bigIndex = left;
let smallIndex = right - 1;
// 循环查找位置
while (true) {
while (array[++bigIndex] < pivot) {}
while (array[--smallIndex] > pivot) {}
if (bigIndex < smallIndex) {
// 表示没重叠,交换两指针指向的元素
swap(array, bigIndex, smallIndex);
} else {
break;
}
}
if (bigIndex != right) swap(array, bigIndex, right - 1);
// 递归——分而治之
recursion_quickSort(array, left, bigIndex - 1);
recursion_quickSort(array, bigIndex + 1, right);
}
recursion_quickSort(array, 0, array.length - 1);
return array;
}
// 使用二分查找,找82
function binSearch(array, num, orderly = true) {
// 如果orderly为true说明,希望序列发生改变成为有序数组
newArry = orderly ? quickSort(array) : quickSort([...array]);
let upperBound = newArry.length - 1;
let lowerBound = 0;
while (upperBound >= lowerBound) {
let mid = ~~((upperBound + lowerBound) / 2);
if (num > newArry[mid]) {
lowerBound = mid + 1;
} else if (num < newArry[mid]) {
upperBound = mid - 1;
} else {
return mid;
}
}
return -1;
}
有没有细心的兄弟发现我这个二分查找的问题,那就是 当查找的是有相同的值的元素时,这只会返回一个索引,那怎么办呢?
but我们得到是索引是在其他相同值的左边或右边,换句话说,从返回的索引向左向右遍历收集相同值的索引,最后返回索引数组即可;
所以这里封装了个indexArray函数,如果没有要查询的值返回-1,有一个或多个返回值都返回索引数组:
function indexArray(array, num) {
let indexArray = [];
let position = binSearch(array, num);
if (position == -1) return -1;
indexArray.push(position);
for (let i = position - 1; i >= 0; i--) {
if (array[i] === num) {
indexArray.push(i);
} else {
break;
}
}
for (let i = position + 1; i < array.length; i++) {
if (array[i] === num) {
indexArray.push(i);
} else {
break;
}
}
return indexArray;
}