查找算法 [前提有序数组]
思路: 不同的方式去获得 mid 值
二分查找:
- 根据 left 左边界、right 右边界,(left + right) / 2
求出中间下标 mid
以及 中间值 midValue- 在 left <= right 的条件下,查找值 findValue 与 中间值midValue 比较。
2.1 findValue > midValue : 查找值大于中间值,将左边界更新为 mid + 1
2.2 findValue < midValue :查找值小于中间值,将右边界更新为 mid - 1
2.3 返回 递归结果- 判断结束条件: left > right 结束
插值查找
- 结束判断条件: left > right || findValue < arr[0] || findValue > arr[arrl.length - 1]
1.1 三个条件缺一不可: 因为插值查找的 mid 计算需要使用到 findValue,如果这里不判断,当 findValue 非常大的时候,比如 90000, 那么计算出来的 mid 值就会非常的大,可能会出现 arr 越界mid 计算
: mid = left + (right - left) * (findValue - arr[left]) / (arr[right] - arr[left])
2.1 可以理解成 比例去记
2.2 计算出 中间值 midValue- 在 left <= right 的条件下,判断 findValue 与 midValue 的大小
3.1 findValue > midValue : 将左边界更新 mid + 1
3.2 findValue < midValue : 将右边界更新 mid - 1
3.3 返回 递归结果
斐波那契查找(黄金分割点法查找)
- 需要一个斐波那契数列 , maxSize = 20
- 定义需要使用到的变量:
2.1 left = 0 左边界
2.2 right = arr.length - 1 右边界
2.3 k 记录斐波那契数组的下标
2.4 mid 记录中间下标
2.5 f[] 斐波那契数组- 判断 right > f[k] - 1 { k++; } 补长 arr 数组 —> temp
- 将数组后面补长的内容使用最后一个数填充.
- 当 left <= right 时,进入循环判断
计算出 mid 值
: mid = left + f[k-1] -1- 比较 midValue 与 findValue 的大小
7.1 midValue > findValue
7.1.1 将右边界更新: right = mid - 1;
7.1.2 将斐波那契数组下标更新: k–;
7.2 midValue < findValue
7.2.1 将左边界更新 : left = mid + 1;
7.2.2 将斐波那契数组下标更新: k -= 2;
7.3 midValue == findValue
7.3.1 mid、right 返回小的一个
1. 二分查找
/**
* 二分查找前提--有序数组
*/
@Test
public void testBinarySearch(){
int [] arr = {1,5,8,10,45,96,96,96,100};
System.out.println(BinarySearch(arr, 0, arr.length - 1, 96));
// System.out.println(BinarySearch2(arr, 0, arr.length - 1, 101));
}
/**
* 二分查找
* @param arr 数组
* @param left 左边界
* @param right 右边界
* @param findValue 需要查找的值
* @return
*/
public int BinarySearch(int [] arr, int left , int right, int findValue){
// 判断结束条件
if(left > right){
return -1;
}
int mid = (left + right) / 2;
int midValue = arr[mid];
if(left <= right){
if(midValue < findValue){
return BinarySearch(arr, mid + 1, right, findValue);
}else if(midValue > findValue){
return BinarySearch(arr, left, mid - 1, findValue);
}else {
// 找到了
return mid;
}
}
return -1;
}
如果需要返回找到的所有的下标。
/**
* 可以返回找到的所有下标
* @param arr
* @param left
* @param right
* @param findValue
* @return
*/
public List<Integer> BinarySearch2(int [] arr, int left , int right, int findValue){
// 判断结束条件
if(left > right){
return Collections.singletonList(-1);
}
int mid = (left + right) / 2;
int midValue = arr[mid];
if(left <= right){
if(midValue < findValue){
return BinarySearch2(arr, mid + 1, right, findValue);
}else if(midValue > findValue){
return BinarySearch2(arr, left, mid - 1, findValue);
}else {
List<Integer> list = new ArrayList<>();
// 找到了
// 继续向前或者向后查找
int temp = mid;
while (temp > 0 && arr[temp] == findValue){
list.add(temp);
// 往前
temp--;
}
temp = mid+1;
while(temp < arr.length && arr[temp] == findValue){
list.add(temp);
temp++;
}
return list;
}
}
return Collections.singletonList(-1);
}
2. 插值查找
/**
* 插入查找
* @param arr 数组
* @param left 左边界
* @param right 右边界
* @param findValue 需要查找的值
* @return 数组下标
*/
public int insertValueSearch(int [] arr, int left, int right, int findValue){
// 结束判断条件 缺一不可
if(left > right || findValue < arr[0] || findValue > arr[arr.length - 1]){
return -1;
}
int mid = left + (right - left) * (findValue - arr[left]) / (arr[right] - arr[left]);
int midValue = arr[mid];
if(left <= right){
if(findValue < midValue){
return insertValueSearch(arr, left, mid - 1, findValue);
}else if(findValue > midValue){
return insertValueSearch(arr, mid + 1, right, findValue);
}else {
// 找到了
return mid;
}
}
return -1;
}
@Test
public void testInsertValueSearch(){
int [] arr = new int[100];
for (int i = 1 ; i <= 100; i++){
arr[i-1] = i;
}
System.out.println(insertValueSearch(arr, 0, arr.length - 1, 85));
}
3. 斐波那契查找(黄金分割点法查找)
// 定义斐波那契数列的数组大小
static int maxSize = 20;
/**
* 获取斐波那契数列
* @return
*/
public int[] getFib(){
int [] arr = new int[maxSize];
arr[0] = 1;
arr[1] = 1;
for (int i = 2; i < maxSize ; i++){
arr[i] = arr[i-1] + arr[i-2];
}
// System.out.println(Arrays.toString(arr));
return arr;
}
/**
* 斐波那契查找
* 黄金分割点法查找
* @param arr 数组
* @param findValue 需要查找的值
* @return
*/
public int FibSearch(int[] arr, int findValue){
// 结束
int left = 0;
int right = arr.length - 1;
int k = 0; // 表示斐波那契数组的下标
int mid = 0; // 存放中间值
int[] f = getFib(); // 获取斐波那契数列
// 获取到斐波那契分割数值的下标
while (right > f[k] - 1){
k++;
}
// 不足的会自动用 0 补充的
int[] temp = Arrays.copyOf(arr, f[k]);
for (int i = arr.length ; i < f[k] ; i++){
temp[i] = arr[arr.length - 1];
}
while(left <= right){
mid = left + f[k-1] - 1;
if(temp[mid] < findValue){
left = mid + 1;
k -= 2;
}else if(temp[mid] > findValue){
right = mid - 1;
k --;
}else {
// 找到了
if(mid <= right){
return mid;
}else {
return right;
}
}
}
return -1;
}
@Test
public void testFibSearch(){
int [] arr = {1,5,8,10,65,78,96,100};
System.out.println(FibSearch(arr, 100));
}