常用的查询算法:
- 顺序查找
- 二分查找
- 插值查找
- 斐波那契查找
- 树表查找
- 分块查找
- 哈希查找
- 索引查询
- 深度搜索&&广度搜索
一、 二分查找
条件:数组必须是有序的
优点:比较次数少;查找速度快;平均性能好
缺点:待查表为有序数组(若为无序数组,分成两份查找无意义,排序本身也耗费时间);插入删除困难(增删需要移动大量的节点)
时间复杂度为O(lgN);
(一)基本方法
int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1; // 注意
while(left <= right) { // 注意
int mid = left+(right - left) / 2;
if(nums[mid] == target)
return mid;
else if (nums[mid] < target)
left = mid + 1; // 注意
else if (nums[mid] > target)
right = mid - 1; // 注意
}
return -1;
}
代码的注意点:
1.停止的条件为:left<=right。如果left<right的话,当left和right相等的值是检查不到的。
2.left=mid+1;right=mid-1。因为mid已经检查过了,所以可以剔除掉。
3.left+(right - left) / 2。不用(left+right)/2是为了防止溢出。
3.局限性:对于有序数组 nums = [1,2,2,2,3],target = 2,此算法返回的索引是 2,没错。但是如果我想得到 target 的左侧边界,即索引 1,或者我想得到 target 的右侧边界,即索引 3,这样的话此算法是无法处理的。
(二)查找左边界的目标
int left_bound(int[] nums, int target) {
if (nums.length == 0) return -1;
int left = 0;
int right = nums.length; // 注意
while (left < right) { // 注意
int mid = left+(right-left) / 2;
if (nums[mid] == target) {
right = mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid; // 注意
}
}
if (left == nums.length) return -1;
// 类似之前算法的处理方式
return nums[left] == target ? left : -1;
}
修改的地方:
1.并不是nums[mid] == target时直接返回,而是将right值赋给它,因为我们要找最右侧的这个点。因此这个操作相当于是,将范围定位在这个值的左侧范围内。
2. int right = nums.length。相当于在这个值的右侧加了一个点 ,但是这个点是不能取到的。同样的,right=mid,但是mid点是不用取的,所以我们相当于是[left, right)范围内搜索的。因此,left < right的结束条件是left=right,此时搜索范围为空。
(三)查找右边界的目标
int right_bound(int[] nums, int target) {
if (nums.length == 0) return -1;
int left = 0, right = nums.length;
while (left < right) {
int mid = (left + right) / 2;
if (nums[mid] == target) {
left = mid + 1; // 注意
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid;
}
}
if (left == 0) return -1;
return nums[left-1] == target ? (right-1) : -1;
修改的地方:
1.当 nums[mid] == target 时,不要立即返回,而是增大「搜索区间」的下界 left,使得区间不断向右收缩,达到锁定右侧边界的目的。
二、插值查找
二分查找选取中间位置,插值查找则通过查找值判定大概位于序列的哪个位置比例。
插入查找:选择下标 mid= left + (right - left) * (key - arr[left])/(arr[right] - arr[left])
(1)序列为有序序列
(2)根据公式选取比较值,相等返回下标,不相等则根据公式继续查找,未找到返回-1
用插值寻找时,需要这个有序序列中的数字大小排序整体均匀。例如[1,2,3,4,5],而非[1,5,6,15,39]。
三、斐波那契查找
斐波那契查找同样是不断修改mid值,令mid=low+F(k-1)-1,F(k-1)是斐波那契函数值。
计算过程依据的是F(k) = F(k-1) + F(k-2),初始的两个值是1,1,因此可以递归。