一、二分查找(折半查找)
思路
考虑数组中可能有等值元素
二、插值查找
介绍
插值查找算法: 插值查找算法类似于二分查找,不同的是插值查找每次从自适应 mid 处开始查找
插值索引:int mid = low + (high - low) * (key - arr[low]) / (arr[high] - arr[low]) ;
对比
1) 对于数据量较大,关键字分布比较均匀的查找表来说,采用插值查找, 速度较快.
2) 关键字分布不均匀的情况下,该方法不一定比折半查找要好
三、代码实现
package com.search;
import java.util.ArrayList;
import java.util.List;
/**
* @program: DataStructures
* @description: 二分查找
* @author: XuDeming
* @date: 2019-12-31 10:46:41
**/
// 二分查找的前提 该数组是有序的
public class BinarySearch {
static int num1 = 0;
static int num2 = 0;
public static void main(String[] args) {
// 二分查找
int[] arr = {1, 2, 3, 4, 5, 5, 5};
System.out.println("resIndex: " + binarySearch1(arr, 0, arr.length - 1, 5));
System.out.println("resIndex: " + binarySearch2(arr, 0, arr.length - 1, 5));
System.out.println("resIndex: " + binarySearch3(arr, 0, arr.length - 1, 5));
System.out.println("resIndex: " + binarySearch4(arr, 0, arr.length - 1, 5));
// 二分查找与插值查找
int [] arr2 = new int[100];
for(int i = 0; i < 100; i++) {
arr2[i] = i + 1;
}
System.out.println("resIndex: " + binarySearch2(arr2, 0, arr2.length - 1, 5));
System.out.println("二分查找" + num1 + "次");
System.out.println("resIndex: " + insertValueSearch(arr2, 0, arr2.length - 1, 5));
System.out.println("插值查找" + num2 + "次");
}
// 二分查找算法 - 非递归
/**
*
* @param arr 数组
* @param left 左边的索引
* @param rigth 右边的索引
* @param findVal 查找的值
* @return 找到返回下标 找不到返回-1
*/
public static int binarySearch1(int[] arr, int left, int rigth, int findVal) {
while (left <= rigth) {
int mid = (left + rigth) / 2;
if (arr[mid] > findVal) {
rigth = mid - 1;
} else if (arr[mid] < findVal) {
left = mid + 1;
} else {
return mid;
}
}
return -1;
}
// 二分查找算法 - 递归
/**
*
* @param arr 数组
* @param left 左边的索引
* @param rigth 右边的索引
* @param findVal 查找的值
* @return 找到返回下标 找不到返回-1
*/
public static int binarySearch2(int[] arr, int left, int rigth, int findVal) {
if (left > rigth)// 找不到
return -1;
num1++;
int mid = (left + rigth) / 2;
int midVal = arr[mid];
if (midVal > findVal) {
return binarySearch2(arr, left, mid - 1, findVal);
} else if (midVal < findVal) {
return binarySearch2(arr, mid + 1, rigth, findVal);
} else {
return mid;
}
}
// 二分查找算法 - 非递归 - 有重复元素
/**
*
* @param arr 数组
* @param left 左边的索引
* @param rigth 右边的索引
* @param findVal 查找的值
* @return 找到返回下标集合 找不到返回[ ]
*/
public static List<Integer> binarySearch3(int[] arr, int left, int rigth, int findVal) {
while (left <= rigth) {
int mid = (left + rigth) / 2;
int midVal = arr[mid];
if (midVal > findVal) {
rigth = mid - 1;
} else if (midVal < findVal) {
left = mid + 1;
} else {
return getIndexes(arr, findVal, mid);
}
}
return new ArrayList<>();
}
// 二分查找算法 - 递归 - 有重复元素
/**
*
* @param arr 数组
* @param left 左边的索引
* @param rigth 右边的索引
* @param findVal 查找的值
* @return 找到返回下标集合 找不到返回[ ]
*/
public static List<Integer> binarySearch4(int[] arr, int left, int rigth, int findVal) {
if (left > rigth)// 找不到
return new ArrayList<>();
int mid = (left + rigth) / 2;
int midVal = arr[mid];
if (midVal > findVal) {
return binarySearch4(arr, left, mid - 1, findVal);
} else if (midVal < findVal) {
return binarySearch4(arr, mid + 1, rigth, findVal);
} else {
return getIndexes(arr, findVal, mid);
}
}
/**
*
* @param arr 数组
* @param findVal 查找的值
* @param mid 其中一个值对应的下标
* @return 返回所有重复元素的集合
*/
private static List<Integer> getIndexes(int[] arr, int findVal, int mid) {
List<Integer> list = new ArrayList<>();
int temp = mid - 1;
while (true) {
if (temp < 0 || arr[temp] != findVal) break;
list.add(temp);
temp--;
}
list.add(mid);
int temp2 = mid + 1;
while (true) {
if (temp2 > arr.length -1 || arr[temp2] != findVal) break;
list.add(temp2);
temp2++;
}
return list;
}
// 插值查找算法 插值查找算法类似于二分查找,不同的是插值查找每次从自适应 mid 处开始查找
// 插值索引:int mid = low + (high - low) * (key - arr[low]) / (arr[high] - arr[low]) ;
// 1) 对于数据量较大,关键字分布比较均匀的查找表来说,采用插值查找, 速度较快.
// 2) 关键字分布不均匀的情况下,该方法不一定比折半查找要好
/**
*
* @param arr
* @param left
* @param right
* @param findVal
* @return
*/
public static int insertValueSearch(int[] arr, int left, int right, int findVal) {
// 找不到
if (left > right || findVal < arr[0] || findVal > arr[arr.length - 1])
return -1;
num2++;
// 求出自适应mid
int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]);
int midVal = arr[mid];
if (findVal > midVal) {
return insertValueSearch(arr, mid + 1, right, findVal);
} else if (findVal < midVal) {
return insertValueSearch(arr, mid - 1, right, findVal);
} else {
return mid;
}
}
}
四、结果打印