7、查找算法
7.1 介绍
常用的查找算法有四种:
- 线性查找(顺序查找)
- 二分查找(折半查找)
- 插值查找
- 斐波那契查找
7.2 线性查找
给定一个数列,和一个带查找的元素,线性查找方法即将整个数列进行遍历,分别将遍历到的元素与待查找的目标值进行比较,如果相等,则表示在数列中找到了对应的元素,直接返回该值对应的下标,如果遍历结束没有找到目标值,则返回-1。
以数列{1,23,43,25,66,47,97,242,13,56,70,33}为例,查找目标值为13,代码实现如下:
package com.kevin.search;
/**
* @author : kevin ding
* @date : 2022/3/9 22:09
* @description : 线性查找 遍历整个数组,分别与待查找元素进行对比,如果找到就返回对应的下标,没有找到就返回-1
*
*/
public class SeqSearchDemo {
public static void main(String[] args) {
int[] array = {1,23,43,25,66,47,97,242,13,56,70,33};
int targetValue = 13;
int res = seqSearch(array, targetValue);
if(res == -1){
System.out.println("没有在arr中找到" + targetValue);
}else {
System.out.println("找到了" + targetValue + "下标为:" + res);
}
}
public static int seqSearch(int[] arr, int targetValue){
// 遍历整个数组,与目标值进行比较
for (int i = 0; i < arr.length; i++) {
if(arr[i] == targetValue){
return i;
}
}
return -1;
}
}
7.3 二分查找
7.3.1 算法思想:
分查找算法的前提是要求给定的数列是有序的,具体思路如下:
-
对于给定的有序数组,需要确定数组的中间位置的下标mid = (left + right) / 2;
-
随后将目标值与中间位置mid处的值进行比较
- 如果目标值比中间位置的值要大,则要查找的目标值在mid的右边,递归的向右查找
- 如果目标值比中间位置的值要小,则要查找的目标值在mid的左边,递归的向左查找
- 如果两者相等,则表示目标值所在的位置即为mid位置
-
递归查找结束的条件:
- 找到的时候结束
- 整个数组找完了也没有找到,也需要结束(left > right)
7.3.2 代码实现
假设给定数组:{11, 12, 33, 37, 42, 75, 88, 174, 189, 325, 456},待查找的值为189,找到其对应的位置
代码实现:
package com.kevin.search;
/**
* @author : kevin ding
* @date : 2022/3/9 22:28
* @description : 二分查找算法 二分查找算法的前提是要求给定的数列是有序的
*/
public class BinarySearchDemo {
public static void main(String[] args) {
int[] array = {11, 12, 33, 37, 42, 75, 88, 174, 189, 325, 456};
int targetValue = 189;
int index = binarySearch(array, 0, array.length - 1, targetValue);
if(index == -1){
System.out.println("array中没有找到给定的数");
}else {
System.out.println("找到了," + targetValue + "在array中的索引位置为:" + index);
}
}
/**
*
* @param arr 给定的有序数组
* @param left 数组的左边界下标
* @param right 数组的有边界
* @param targetVal 待查找的值
* @return 返回结果,是否找到
*/
public static int binarySearch(int[] arr, int left, int right, int targetVal){
// 递归完毕 没有找到,结束
if(left > right){
return -1;
}
//找中间值
int mid = (left + right) / 2;
// 中间值和待查找的值进行比较
if(targetVal > arr[mid]){
// 待查找的值大于中间位置的值,则向右递归查找
return binarySearch(arr, mid+1,right, targetVal);
}else if(targetVal < arr[mid]){
// 待查找的值小于中间位置的值,向左递归查找
return binarySearch(arr, left, mid-1, targetVal);
}else{
return mid;
}
}
}
7.3.3 代码优化
上述二分查找的代码是找到指定的数就直接返回对应的索引,假设待查找的值在给定的数组中出现多次,如何将其对应的索引全部找出来。
譬如给定数组:{11, 12, 33, 37, 42, 75, 75, 75, 75, 88, 174, 189, 325, 456}
package com.kevin.search;
import javax.sound.midi.MidiChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author : kevin ding
* @date : 2022/3/9 22:58
* @description : 二分查找算法 二分查找算法的前提是要求给定的数列是有序的
*/
public class BinarySearchDemo {
public static void main(String[] args) {
int[] array = {11, 12, 33, 37, 42, 75, 75, 75, 75, 88, 174, 189, 325, 456};
int targetValue = 75;
List<Integer> integers = binarySearch2(array, 0, array.length - 1, targetValue);
System.out.println(integers);
}
/**
*
* @param arr 给定的有序数组
* @param left 数组的左边界下标
* @param right 数组的有边界
* @param targetVal 待查找的值
* @return 返回结果,是否找到
*/
public static List<Integer> binarySearch2(int[] arr, int left, int right, int targetVal){
// 首先定义一个ArrayList 用于存放结果
List<Integer> resList = new ArrayList<>();
if(left > right){
return resList;
}
int mid = (left + right) / 2;
if(targetVal > arr[mid]) {
return binarySearch2(arr, mid+1, right, targetVal);
}else if(targetVal < arr[mid]){
return binarySearch2(arr, left, mid-1, targetVal);
}else {
// 找到了与目标值相等元素所在的位置,找到该位置后不能直接返回
// 扫描mid左边的所有满足值等于targetVal的元素的下标,将其全部加入到集合resList中
// 扫描mid右边的所有满足值等于targetVal的元素的下标,将其全部加入到集合resList中
// 扫描左边
int temp = mid - 1;
while (true){
if(temp < 0 || arr[temp] != targetVal){
break;
}
// 因为是往左边查找,将每次找到的下标插在第一位
resList.add(0, temp);
// 下标左移一位
temp -= 1;
}
// 先把mid处的值追加到resList中
resList.add(mid);
// 扫描右边
temp = mid + 1;
while (true){
if(temp > right || arr[temp] != targetVal){
break;
}
// 因为是往右移动扫描,所有下标是在一直变大的,直接将查找到的下标追加到resList中即可
resList.add(temp);
// 向右移动
temp += 1;
}
// 最后返回resList的结果
return resList;
}
}
}