线性查找 -- 存在性问题
问题描述
输入:数组 arr= [10, 25, 7, 42, 18],目标元素 target=42
输出:true 还是 false
示例1:
- 数组
arr= [10, 25, 7, 42, 18],目标元素target=42 - 输出:true
示例2:
- 数组
arr= [10, 25, 7, 42, 18],目标元素target=50 - 输出:false
状态定义
hasRecursive(arr, target, i) 表示目标元素target是否在数组arr的前i个元素中。
基准情况:
hasRecursive(arr, target, 0) 表示目标元素target是否在数组arr的前 0 个元素中,即数组arr中没有要考虑的元素了,即要查找的数组为空,显然返回false。
推导递推关系
要计算 hasRecursive(arr, target, i), 让我们考虑数组arr 的 第i个元素,即arr[i-1]。
有两种情况:
情况 1:arr[i-1] 等于 target ,就返回 true。
情况 2:arr[i-1] 不等于 target,则需要计算目标元素target是否在数组arr的前i-1个元素中。
请总结下递推关系:
txt
hasRecursive(arr, target, i) = (arr[i-1] == target) or hasRecursive(arr, target, i-1)
朴素递归解
java
public static boolean hasRecursive(int[] arr, int target){
return hasRecursiveHelper(arr, target, arr.length);
}
private static boolean hasRecursiveHelper(int[] arr, int target, int i) {
if (i == 0) {
return false;
}
if (arr[i-1] == target) {
return true;
}
return hasRecursiveHelper(arr, target, i-1);
}
迭代解
java
public static boolean hasIterative(int[] arr, int target){
for (int i = 0; i < arr.length; i++) {
if (arr[i] == target) {
return true;
}
}
return false;
}
优化 1:存在热点数据
当数据存在非均匀访问模式(某些元素的被访问频率高于其它元素)时,可以对数组使用 "交换相邻元素法"。
java
public static boolean hasIterative(int[] arr, int target){
for (int i = 0; i < arr.length; i++) {
if (arr[i] == target) {
if (i > 0) {
// "交换相邻元素法"
int temp = arr[i];
arr[i] = arr[i-1];
arr[i-1] = temp;
}
return true;
}
}
return false;
}
优化 2:有序性
假设数组是有序的,依然使用线性查找。那么,可以进行提前终止优化。
对从右向左的递归方向来说,如果数组元素比目标元素小,那么就可以说明目标元素不存在。
解释:
情况 3:
arr[i-1] < target,说明数组的前i个元素都小于target,即表示不用再递归到前i-1个元素中去查找了,直接可判断目标元素不存在。
示例代码
java
private static boolean hasRecursiveHelper(int[] arr, int target, int i) {
if (i == 0) {
return false;
}
if (arr[i-1] == target) {
return true;
}
// 优化:针对有序数组
if (arr[i-1] < target) {
return false;
}
return hasRecursiveHelper(arr, target, i-1);
}
对从左向右的迭代方向来说,如果数组元素比目标元素大,那么就可以说明目标元素不存在。
解释:
对从左向右的迭代方向来说,若
arr[i] > target,由于数组有序且arr[i]是尚未检查区间[i, n-1]中的最小元素,它已经大于target,因此target不可能出现在后续任何位置,可以直接判断不存在。
示例代码
java
public static boolean hasIterative(int[] arr, int target){
for (int i = 0; i < arr.length; i++) {
if (arr[i] == target) {
if (i > 0) {
// 优化:"交换相邻元素法"。
int temp = arr[i];
arr[i] = arr[i-1];
arr[i-1] = temp;
}
return true;
}
// 优化:针对有序数组
if(arr[i] > target) {
return false;
}
}
return false;
}
注意:
- 实际工程中,这两种优化通常不会同时使用,因为前者会破坏数组的有序性。