java中常用的查找算法有4种:
顺序(线性)查找只需要数组的顺序依次进行查找,找到后返回数组的下标即可。顺序查找适用于任何数据。缺点是当查找越后面的数花费的时间越长。
二分查找的前提条件是这个数组是有序的。
二分查找思路:
public int binarySearch(int[] arr, int left, int right, int searchValue){
if (left > right){ // 没有查找到
return -1;
}
int mid = (left + right) / 2; // 中间值索引
int midValue = arr[mid]; // 中间值
if (searchValue < midValue){ // 左递归
return binarySearch(arr, left, mid - 1, searchValue);
}else if (searchValue > midValue){ // 右递归
return binarySearch(arr, mid + 1, right, searchValue);
}else {
return mid;
}
}
优化:当我们查找的数在该数组中有多个时,上面的方法只能返回一个值。优化返回相同的数的下标。利用集合存储查找到相同数的下标。
当找到一个数时,继续向左右寻找相同的数,并保持下标。
public ArrayList<Integer> binarySearch2(int[] arr, int left, int right, int searchValue){
if (left > right){ // 没有查找到
return new ArrayList<Integer>();
}
int mid = (left + right) / 2; // 中间值索引
int midValue = arr[mid]; // 中间值
if (searchValue < midValue){ // 左递归
return binarySearch2(arr, left, mid - 1, searchValue);
}else if (searchValue > midValue){ // 右递归
return binarySearch2(arr, mid + 1, right, searchValue);
}else {
ArrayList<Integer> integers = new ArrayList<>();
int temp = mid - 1;
while (temp >= 0 && arr[temp] == arr[mid]){ //向左查找有无和查找值相同的数
integers.add(temp); // 添加下标
temp--;
}
integers.add(mid);
temp = mid + 1;
while (temp < arr.length && arr[temp] == arr[mid]){ // 向右查找有无和查找值相同的数
integers.add(temp); // 添加下标
temp++;
}
return integers;
}
}
插值查找算法类似于二分查找,不同的是插值查找每次从自适应mid 处开始查找。
原理:将二分查找中的求mid 索引的公式 , low 表示左边索引 left, high 表示右边索引 right. key 就是要查找的值。
其余分别与二分查找无异。
代码实现:
public class InsertSearch {
public static void main(String[] args) {
int[] arr = {1,5,90,500,1000,1000};
InsertSearch insertSearch = new InsertSearch();
int i = insertSearch.binarySearch(arr, 0, arr.length - 1, 500);
System.out.println("i = " + i);
}
/**
* @Description: binarySearch 插值查找
* @param: [arr, left, right, searchValue]
* @return: int
* @auther: zqq
*/
public int binarySearch(int[] arr, int left, int right, int searchValue){
// 没有查找到, searchValue < arr[0] || searchValue > arr[arr.length - 1] 防止越界
if (left > right || searchValue < arr[0] || searchValue > arr[arr.length - 1]){
return -1;
}
int mid = left + (right - left) * (searchValue - arr[left]) / (arr[right] - arr[left]); // 中间值索引:插值查找
int midValue = arr[mid]; // 中间值
if (searchValue < midValue){ // 左递归
return binarySearch(arr, left, mid - 1, searchValue);
}else if (searchValue > midValue){ // 右递归
return binarySearch(arr, mid + 1, right, searchValue);
}else {
return mid;
}
}
}
注意:
斐波那契查找原理: 就是要查找到数组一个黄金分割点(mid):mid = left + F[k] - 1(mid为黄金分割点的下标)。若查找比黄金分割点的值小,则向比黄金分割点大的区域继续使用黄金分割点查找。若查找比黄金分割点的值小,则向比黄金分割点小的区域继续使用黄金分割点查找。这次依次查找,直到找到为止,原理有和二分查找相似,这是中点(mid)不一样。
那么关键就是如何查找到黄金分割点:
黄金分割点查找:
斐波那契数列的大小: 数组长度不同,那么对应所需要的斐波那契数列长度就不一样。若是写死,则很浪费空间。故斐波那契数列的大小可根据数组的大小进行选择。
实现动态斐波那契数列
public int[] fib( int right){
int[] fibonacii;
// 当数组长度小于等于5时,数组的长度大于等于数组长度对应的斐波那契数,
// 这时后面进行查找一个k使得fib[k]-1刚好大于等于数组长度就会越界,故写死给一个6。产生的最大斐波那契数是8,大于数组长度,就不会越界
if (right <= 5){
fibonacii = new int[6];
}else {//当数组长度大于5时,数组的长度小于数组长度对应的斐波那契数,不会产生越界
fibonacii = new int[right];
}
fibonacii[0] = 1;
fibonacii[1] = 1;
for (int i = 2; i < fibonacii.length; i++) {
fibonacii[i] = fibonacii[i -1] + fibonacii[i - 2];
}
return fibonacii;
}
斐波那契查找完整代码:
public class FibonacciSearch {
public static void main(String[] args) {
int[] arr = {1,5,90,500,1000};
FibonacciSearch fibonacciSearch = new FibonacciSearch();
int i = fibonacciSearch.fibonacciSearch(arr, 1000);
System.out.println("i = " + i);
}
/**
* @Description: fibonacciSearch 斐波那契查找
* @param: [arr, searchValue]
* @return: int
* @auther: zqq
* @date: 20/6/18 10:18
*/
public int fibonacciSearch(int[] arr, int searchValue){
int left = 0;
int right = arr.length - 1;
int[] fib = fib(arr.length);
int mid = 0;// 存储
int k = 0;
while (arr.length > fib[k] - 1){ // 找到一个k使得这个斐波那契数刚好大于等于right
k++;
}
if (arr.length < fib[k]){
arr = Arrays.copyOf(arr, fib[k]);// 因为right可能小于找到的斐波那契数,所以需要对数组扩容进行向后填充
for (int i = right+1; i < arr.length; i++) {
arr[i] = arr[right];
}
}
while (left <= right){
mid = left + fib[k - 1] - 1; // 分割点
if (searchValue < arr[mid]){ // 在黄金分割点左边
right = mid - 1;
k--;// 向fib[k-1]的范围查找
}else if (searchValue > arr[mid]){
left = mid + 1;
k -= 2;//向fib[k-2]的范围查找
}else {
if (mid <= right){
return mid;
}else {
return right;
}
}
}
return -1;// 没有找到
}
/**
* @Description: fib 构造斐波那契数列
* @param: [right, left]
* @return: int[]
* @auther: zqq
* @date: 20/6/18 9:50
*/
public int[] fib( int right){
int[] fibonacii;
// 当数组长度小于等于5时,数组的长度大于等于数组长度对应的斐波那契数,
// 这时后面进行查找一个k使得fib[k]-1刚好大于等于数组长度就会越界,故写死给一个6。产生的最大斐波那契数是8,大于数组长度,就不会越界
if (right <= 5){
fibonacii = new int[6];
}else {//当数组长度大于5时,数组的长度小于数组长度对应的斐波那契数,不会产生越界
fibonacii = new int[right];
}
fibonacii[0] = 1;
fibonacii[1] = 1;
for (int i = 2; i < fibonacii.length; i++) {
fibonacii[i] = fibonacii[i -1] + fibonacii[i - 2];
}
return fibonacii;
}
}
需要查找重复的可再次基础上修改。