0
点赞
收藏
分享

微信扫一扫

【算法系列之万字总结常用的查找算法,持续补充更新中】



  • ​​🚢 顺序查找算法​​
  • ​​🚢 二分查找算法​​
  • ​​💥优化二分查找算法​​
  • ​​🚢 插值查找算法​​
  • ​​🚢 斐波那契查找算法​​
  • ​​💥优化斐波那契查找算法​​

🚢 顺序查找算法

🙄对于顺序查找算法比较容易理解,遍历一个数组即可,如果找到该值返回对应的下标即可.🙄

【算法系列之万字总结常用的查找算法,持续补充更新中】_搜索


🧧源代码🧧

/**
* 如果返回的是-1,则数组中无相关值
* @param arr 待搜索数组
* @param value 待搜索值
* @return -1或者 索引值
*/
private static int seqSearch(int[] arr, int value) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == value) { //如果找到对应的值
return i; //返回对应的索引
}
}
return -1; //没有找到返回-1
}

🎐测试代码🎐

public static void main(String[] args) {
int[] arr = {1, 34, -1, 5, 8, 2, 99};
int index = seqSearch(arr, 8);
if (index == -1) {
System.out.println("无相关值!");
} else {
System.out.println("改值对应的索引下标为:"+index);
}
}


🚢 二分查找算法

🙄对于某一个数组arr(这个数组是有序的,假设是升序),我们想要从数组中找到值value,返回它对应得索引
begin = 0;
end = arr.length
mid = (begin+end)/2
这三个变量begin、mid、end对应这数组的下表
1.我们将value与arr[mid]进行比较
2.如果value > arr[mid],那么我们只需要在mid的右边进行搜索即可;如果value < arr[mid],我们只需要在mid的左边进行搜索即可.
🙄
🎐注意点🎐
Ⅰ.如果数组不是有序的,则需要将其进行排序后再操作
Ⅱ.使用递归能够很好实现

【算法系列之万字总结常用的查找算法,持续补充更新中】_搜索_02


二分查找需要一定的递归知识

【☀️爆肝万字总结递归❤️玩转算法系列之我如何才能掌握递归解题的能力❤️十大经典问题助你突破极限☀️】

🧧源代码🧧

/**
*二分查找算法
* @param arr 待搜索的数组
* @param value 待查找的值
* @param begin 左边的索引
* @param end 右边的索引
* @return 如果找到该值,就返回其对应的下标,否者返回其-1
*/
private static int binarySearch(int[] arr, int value, int begin, int end) {
if (begin > end) {
return -1;
}
int mid = (begin + end) / 2; //取中间值
if (value > arr[mid]) {
return binarySearch(arr, value, mid + 1, end);
} else if (value < arr[mid]) {
return binarySearch(arr, value, begin, mid - 1);
} else {
return mid;
}
}

🎐测试代码🎐

public static void main(String[] args) {
int[] arr = {1,3, 4, 5, 8, 11, 23, 77};
int index = binarySearch(arr, 8, 0, arr.length - 1);
if (index == -1) {
System.out.println("未找到该值!");
} else {
System.out.println(index);
}
}



💥优化二分查找算法

❤️如果一个数组是[1,2,4,5,6,8,8,8,8,11,23],一旦查找其他的8,那么如果使用上面的查找算法,就只能返回一个索引值;因此,对该查找算法进行优化,让其能够返回所有的索引值❤️

/**
* 默认是升序数组
* 对上部分进行优化,能够查出重复数据的所有下表
* @param arr 待搜索数组
* @param value 待查询的值
* @param begin 左索引
* @param end 右索引
* @param list 存储搜索值下表的集合
*/
private static void binarySearch2(int[] arr, int value, int begin, int end, List<Integer> list) {
if (begin > end) { //递归结束条件
return;
}
int mid = (begin + end) / 2; //取中值
if (value > arr[mid]) { //说明需要去mid右边查找
binarySearch2(arr, value, mid + 1, end, list); //递归进入 mid+1 至 end这一段数据
} else if (value < arr[mid]) { //说明需要去mid左边查找
binarySearch2(arr, value, begin, mid - 1, list); //递归进入 begin 至 mid-1这一段数据
} else if (value == arr[mid]) { //说明找到了待查找的值;此时需要考虑的是在这个值的左边和右边可能有相同的数据
int tempFront = mid - 1; //例如arr [1,8,8,8,9],找到arr[2],它的前后都有8,所以需要进行判断
while (true) { //这个while循环是对arr[2]左边进行判断是否有8
if (tempFront < 0 || value != arr[tempFront]) { //这当上面的mid为0时,这个tempFront为-1,所以需要进行判断
break; //又或者左边第一个就不等于value,那么直接break即可
}
list.add(tempFront); //将符合value=8的索引值加入到list集合中
tempFront--;
}
list.add(mid); //将arr[2]加入到集合中,至于为什么现在加入是因为按照从大到小 list[2,3,4]
int tempAfter = mid + 1;
while (true) { //右边类似于左边
if (tempAfter > end || value != arr[tempAfter]) {
break;
}
list.add(tempAfter);
tempAfter++;
}
}
}

🎐测试代码🎐

public static void main(String[] args) {
int[] arr = {1,3, 4, 5, 5, 5, 5, 11, 23, 77};
List<Integer> list = new ArrayList<>();
binarySearch2(arr, 5, 0, arr.length - 1, list);
if (list.isEmpty()) {
System.out.println("未找到该值!");
} else {
System.out.println(list.toString());
}

}

🥫分析以上的二分查找算法

根据它的 int mid = (begin+end)/2 ,我们可以推算二分算法适用于什么场景;
假设一个数组为arr[1,2,3,4,5…,98,99,100] ,我们要找寻1这个值,那么需要几次找到呢?看下面的运行结果

【算法系列之万字总结常用的查找算法,持续补充更新中】_数据结构_03


可以看出经历了六次循环调用才找到;如果我们找value=50,那么一次就能成功!所以这个调用多少次与mid的取值也有很大关系;

分析mid=(begin+end)/2,看出begin、end无法做出改变,那么只有对1/2进行改动;mid=begin + (begin+end) / {(value-arr[begin])/(arr[end]-arr[begin])},根据上文中找寻1,我们可以算出mid = 1,所以一步就能找到;这就是下文提到的插值查找


🚢 插值查找算法

根据上文进行分析,这个插值查找算法是对二分查找算法的改进。
mid=(begin+end) / {(value-arr[begin]) / (arr[end]-arr[begin])}

下面这段源代码除了mid求的不一样之外,其他一模一样。☸

/**
* 自适应求出mid
* @param arr 待搜索数组
* @param value 待查询的值
* @param begin 左索引
* @param end 右索引
* @param list 存储搜索值下表的集合
*/
private static void interpolationSearch(int[] arr, int value, int begin, int end, List<Integer> list) {
System.out.println("test");
//这个value要进行判断是否小于arr[0],否者在下面求mid时会报错
if (begin > value || value < arr[0] || value > arr.length - 1) {
return;
}
int mid = begin + (begin + end) * (value - arr[begin]) / (arr[end] - arr[begin]);
if (value > arr[mid]) {
interpolationSearch(arr, value, mid + 1, end, list);
} else if (value < arr[mid]) {
interpolationSearch(arr, value, begin, mid - 1, list);
} else if (value == arr[mid]) {
int tempFront = mid - 1;
while (true) {
if (tempFront < 0 || value != arr[tempFront]) {
break;
}
list.add(tempFront);
tempFront--;
}
list.add(mid);
int tempAfter = mid + 1;
while (true) {
if (tempAfter > end || value != arr[tempAfter]) {
break;
}
list.add(tempAfter);
tempAfter++;
}
}
}

根据数组为arr[1,2,3,4,5…,98,99,100] ,查找其中的1,只需一次便能查到.

【算法系列之万字总结常用的查找算法,持续补充更新中】_数组_04


🚢 斐波那契查找算法

🎐这个斐波那契(黄金分割法)是对二分法的改进,也就是和插值查找类似;通过改变mid的取值方式来达到,每次都在黄金分割点附近。

【算法系列之万字总结常用的查找算法,持续补充更新中】_搜索_05

这个斐波那契查找需要借助斐波那契数列来实现

public class FibonacciSearch {

private static final int MAX_SIZE = 10;

public static void main(String[] args) {
int[] arr = {1, 3, 5, 8, 9, 12, 33, 44};
int index = fibonacciSearch(arr, 44);
if (index == -1) {
System.out.println("未找到该值!");
} else {
System.out.println(index);
}
}

/**
* 默认是升序序列
* 流程:
* 1.构建斐波那契数列
* 2.定k值,也就是根据f(k)-1 = f(k-1)- 1 + f(k-2)-1 +1;必须将待搜索的数组扩容至f(k) - 1
* 3.将扩容部分的数据赋值原数组的arr[end]
* 4.while循环判断
*
* @param arr 待搜索数组
* @param value 待查询的值
* @return 如果未搜索到相关的值,返回-1,如果搜索到了就返回对应的索引
*/
private static int fibonacciSearch(int[] arr, int value) {
int begin = 0;
int end = arr.length;
int k = 0;
int[] f = fib(); //构建一个斐波那契数列,斐波那契查找需要借助斐波那契数列来完成
while (end > f[k] - 1) { //每次都将k进行++,直到f[k]-1大于或等于数组长度(斐波那契数列是1,1,2,3,5,8...)那么f[k] - 1有可能稍微大于数组长度
++k; //至于为什么要将数组大小定到f[k] - 1 --》 f[k]=f[k-1]+f[k-2] --> f[k]-1 = f[k-1]-1 + f[k-2]-1 +1
} //刚好将数组分为三部分 即f[k-1]-1 、f[k-2]-1、1(1也就是mid)
int[] temp = Arrays.copyOf(arr, f[k] - 1); //由于f[k-1]可能大于数组长度,所以要进行扩容处理
for (int j = end + 1; j < f[k] - 1; j++) {
temp[j] = temp[end]; //将扩容部分的数据定义为原数组最后一个数据(也就是最大值)
}
while (begin < end) { //跳出条件,
int mid = begin + f[k - 1] - 1; //斐波那契数列的mid求解方式
if (value < temp[mid]) { //说明在mid的左边,左边是f[k-1]-1部分
k--; //k-- 代表 f[k-1]-1,左边部分刚好f[k-1]-1 = f[k-2]-1 + f[k-3]-1
end = mid - 1;
} else if (value > temp[mid]) {
k = k - 2; // k=k-2代表f[k-2]-1,这是因为在右边f[k-2]-1部分;f[k-2]-1 = f[k-3]-1 + f[k-4]-1
begin = mid + 1;
} else {
if (mid > arr.length) { //判断mid是否在扩容部分
return end;
} else {
return mid;
}
}
}
return -1;
}

/**
* 创建一个斐波那契数列,供黄金分割法查找时使用
* @return 斐波那契数列数组
*/
private static int[] fib() {
int[] f = new int[MAX_SIZE];
f[0] = 1;
f[1] = 1;
for (int i = 2; i < f.length; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f;
}



💥优化斐波那契查找算法

对于上面的斐波那契查找算法,我们可以看出对找寻重复的数组元素是不行的(例如[1,2,8,8,8,8,9],找寻其中的8,只能返回一个索引];所以对其进行改进,将索引加入到集合当中。

思路基本上和上文一样.

package com.zsh.algorithm.search;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
* @author:Ronin
* @since:2021/9/25
* @email:1817937322@qq.com
*/
public class FibonacciSearchImprove {

private static final Integer MAX_SIZE = 10;

public static void main(String[] args) {
int[] arr = {1, 3, 5, 6, 8, 10, 22, 22, 22};
List<Integer> list = new ArrayList<>();
fibonacciSearch(arr, list, 22);
if (list.isEmpty()) {
System.out.println("未找到该值!");
} else {
System.out.println(list.toString());
}
}

/**
* 对斐波那契查找进行改进,支持重复元素
*
* @param arr 待搜索集合
* @param list 下标集合
* @param value 待查询的值
*/
private static void fibonacciSearch(int[] arr, List<Integer> list, int value) {
int[] f = fib();
int k = 0;
int begin = 0;
int end = arr.length - 1;
int n = arr.length;
int mid = 0;
while (n > f[k] - 1) {
++k;
}
int[] temp = Arrays.copyOf(arr, f[k] - 1);
for (int j = end + 1; j < f[k] - 1; j++) {
temp[j] = temp[end];
}
while (begin < end) {
mid = begin + f[k - 1] - 1;
if (value < temp[mid]) {
k--;
end = mid - 1;
} else if (value > temp[mid]) {
k -= 2;
begin = mid + 1;
} else {
int tempFront = mid - 1;
while (tempFront >= 0) {
if (value == temp[tempFront]) {
list.add(tempFront);
tempFront--;
} else {
break;
}
}
list.add(mid);
int tempAfter = mid + 1;
while (tempAfter <= end) {
if (value == temp[tempAfter]) {
list.add(tempAfter);
tempAfter++;
} else {
break;
}
}
break;
}
}
}

//创建一个斐波那契数列
private static int[] fib() {
int[] f = new int[MAX_SIZE];
f[0] = 1;
f[1] = 1;
for (int i = 2; i < f.length; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f;
}
}


举报

相关推荐

0 条评论