四种查找算法实现
顺序查找
特点:不需要对序列进行排序
/**
* @param arr 要查找的无序数组
* @param findVal 要查找的值
* @return 值在数组的位置下标
*/
public static ArrayList<Integer> seqSearch(int[] arr, int findVal) {
ArrayList<Integer> sub = new ArrayList<>();
for (int i = 0; i < arr.length; i++) {
if (findVal == arr[i]) {
sub.add(i);
}
}
if (!sub.isEmpty()) {
return sub;
} else
throw new IndexOutOfBoundsException("未找到");
}
二分查找
特点:针对有序序列,递归
/*
* 二分查找法:针对有序序列
* left=right=mid时,不能跳出循环,此时相当于遍历到最边缘的数
* 要比较findVal与边缘数的值
*/
/**
* @param arr 要查找的有序数组
* @param left 该有序数组的左索引
* @param right 该有序数组的右索引
* @param findVal 要查找的值
* @return 值在数组的位置下标
*/
public static ArrayList<Integer> binarySearch(int[] arr, int left, int right, int findVal) {
ArrayList<Integer> sub = new ArrayList<Integer>();
int mid = (left + right) / 2;
int temp = 0;
if (left > right) {
throw new IndexOutOfBoundsException("未找到");
}
// 递归调用有返回值的方法时,要记得逐层返回值
if (findVal > arr[mid]) {
// binarySearch(arr, mid + 1, right, findVal);
return binarySearch(arr, mid + 1, right, findVal);
} else if (findVal < arr[mid]) {
// binarySearch(arr, left, mid - 1, findVal);
return binarySearch(arr, left, mid - 1, findVal);
} else {
temp = mid - 1;
while (temp >= 0 && arr[temp] == findVal) {
sub.add(temp);
temp--;
}
sub.add(mid);
temp = mid + 1;
while (temp < arr.length && arr[temp] == findVal) {
sub.add(temp);
temp++;
}
return sub;
}
}
插值查找
特点:递归,是二分查找的升级。
思想:基于二分查找算法,将查找点的选择改进为自适应选择,提高查找效率。对于分布比较均匀的有序序列查找比较快。
相较于二分查找,主要改变:
m i d = l e f t + r i g h t − l e f t 2 → m i d = l e f t + f i n d V a l − a r r [ l e f t ] a r r [ r i g h t ] − a r r [ l e f t ] ( r i g h t − l e f t ) . mid=left+\frac {right-left} 2 \rightarrow mid=left+\frac {findVal-arr[left]}{arr[right]-arr[left]}(right-left). mid=left+2right−left→mid=left+arr[right]−arr[left]findVal−arr[left](right−left).
/*
* 插值查找法:针对有序序列
*/
/**
* @param arr 要查找的有序数组
* @param left 该有序数组的左索引
* @param right 该有序数组的右索引
* @param findVal 要查找的值
* @return 值在数组的位置下标
*/
public static ArrayList<Integer> insertValueSearch(int[] arr, int left, int right, int findVal) {
ArrayList<Integer> sub = new ArrayList<Integer>();
if (left > right || findVal < arr[left] || findVal > arr[right]) {
throw new IndexOutOfBoundsException("未找到");
}
int temp = 0;
int mid = left + (findVal - arr[left]) / (arr[right] - arr[left]) * (right - left);
System.out.println("~~");
// 递归调用有返回值的方法时,要记得逐层返回值
if (findVal > arr[mid]) {
return insertValueSearch(arr, mid + 1, right, findVal);
} else if (findVal < arr[mid]) {
return insertValueSearch(arr, left, mid - 1, findVal);
} else {
temp = mid - 1;
while (temp >= 0 && arr[temp] == findVal) {
sub.add(temp);
temp--;
}
sub.add(mid);
temp = mid + 1;
while (temp < arr.length && arr[temp] == findVal) {
sub.add(temp);
temp++;
}
return sub;
}
}
斐波那契查找
特点:是二分查找的升级。
思想:运用黄金比例的概念在有序数列中选择查找点进行查找,提高查找效率。
- 相较于二分查找,主要改变:
m i d = l e f t + r i g h t − l e f t 2 → m i d = l e f t + f i b ( k − 1 ) − 1. mid=left+ \frac{right-left}2 \rightarrow mid=left+fib(k-1)-1. mid=left+2right−left→mid=left+fib(k−1)−1.
其中 f i b ( k − 1 ) fib(k-1) fib(k−1)为 fibnoacci 数列,当 k → ∞ k \rightarrow \infty k→∞时, f i b ( k − 1 ) f i b ( k ) \frac {fib(k-1)}{fib(k)} fib(k)fib(k−1) 满足黄金分割比例 ≈ 0.618. \approx 0.618. ≈0.618.
- 采用 f i b ( k ) − 1 = f i b ( k − 1 ) − 1 + f i b ( k − 2 ) − 1 + 1 fib(k)-1=fib(k-1)-1+fib(k-2)-1+1 fib(k)−1=fib(k−1)−1+fib(k−2)−1+1代替传统 f i b ( k ) = f i b ( k − 1 ) + f i b ( k − 2 ) fib(k)=fib(k-1)+fib(k-2) fib(k)=fib(k−1)+fib(k−2)的原因:
为了便于将长度为 f i b ( k ) − 1 fib(k)-1 fib(k)−1的有序数列划分为 f i b ( k − 1 ) − 1 , m i d , f i b ( k − 2 ) − 1 fib(k-1)-1,mid,fib(k-2)-1 fib(k−1)−1,mid,fib(k−2)−1三部分。
// 生成fibnoacci数列,k>=0
public static int[] fibonacci(int k) {
int[] fib = new int[k + 1];
fib[0] = 1;
if (k > 0) {
fib[1] = 1;
for (int i = 2; i <= k; i++) {
fib[i] = fib[i - 1] + fib[i - 2];
}
}
return fib;
}
/*
* fibnoacci查找 对有序序列采用非递归方法
* 每次循环都更新左、右、中索引(left,right,mid),
* 直至找到findVal或者超出索引才退出循环
*/
/**
* @param arr 要查找的有序序列
* @param findVal 要查找的值
* @return 要查找的值在有序序列的位置下标
*/
public static ArrayList<Integer> fibonacciSearch(int[] arr, int findVal) {
int left = 0, right = arr.length - 1;
if (findVal < arr[left] || findVal > arr[right]) {
throw new IndexOutOfBoundsException("未找到");
}
ArrayList<Integer> sub = new ArrayList<>();
int k = 0;
int[] fib = fibonacci(k);
// 确定fibnoacci最大值与待查找数组的长度
while (fib[k] < arr.length) {
k++;
fib = fibonacci(k);
}
// 将数组补充长度至fib[k],防止起初mid跳到原数组外,补充值为arr的最大值
int[] a = Arrays.copyOf(arr, fib[k]);
for (int i = arr.length; i < a.length; i++) {
a[i] = arr[arr.length - 1];
}
int mid = 0, temp = 0;
while (true) {
if (left > right || k < 1) {
throw new IndexOutOfBoundsException("超出索引");
}
mid = left + fib[k - 1] - 1;
if (findVal < a[mid]) {
right = mid - 1;
k--;
} else if (findVal > a[mid]) {
left = mid + 1;
k -= 2;
} else {
if (mid < right) {
temp = mid - 1;
while (temp >= 0 && a[temp] == findVal) {
sub.add(temp);
temp--;
}
sub.add(mid);
temp = mid + 1;
while (temp < arr.length && a[temp] == findVal) {
sub.add(temp);
temp++;
}
} else {
// 当找到值但在原数组外,说明为最大值,则返回最大值在原数组的下标
temp = right - 1;
while (temp >= 0 && a[temp] == findVal) {
sub.add(temp);
temp--;
}
sub.add(right);
}
return sub;
}
}
}