11、数据结构与算法 - 实战:查找算法
1. 顺序(线性)查找
思路:从头元素开始逐一开始与被查找元素比较 - 相等则返回,不想等则继续下一个元素,直至遍历完整个容器
public class SequentialSearch {
public static int search(int[] arrs, int value) {
for(int index = 0; index < arrs.length; index++) {
if(arrs[index] == value) {
return index;
}
}
return -1;
}
public static void main(String[] args) {
int[] arrs = {
1,-5,20,30,60};
int value = -20;
int index = search(arrs, value);
if(index != -1) {
System.out.println("查找到数值" + value + "在数组中的下标是" + index);
}else {
System.out.println("没有查找到");
}
}
}
2. 二分(折半)查找 - 有序的元素组
nlog 2
思路
- 先找中间元素下标 - midIndex = (left+right)/2
- 比较查找的元素searchValue与中间元素sortedArr[midIndex]
2.1 如果 searchValue > sortedArr[midIndex] - 向右半部分递归查找
2.2 如果 searchValue < sortedArr[midIndex] - 向左半部分递归查找
2.3 如果 searchValue = sortedArr[midIndex] - 返回midIndex
递归结束条件 - 满足任意一个即可
1、 left>right;
2、 searchValue=sortedArr[midIndex];
2.1 递归
public class BinarySearch {
//快速排序 - 递归
public static void quickSort(int[] array, int startIndex, int endIndex) {
if (startIndex >= endIndex)
return;
int left = startIndex;
int right = endIndex;
int temp = array[startIndex];
while (left < right) {
while (left < right && array[right] >= temp) right--;
array[left] = array[right];
array[right] = temp;
while (left < right && array[left] <= temp) left++;
array[right] = array[left];
array[left] = temp;
}
quickSort(array, startIndex, left - 1);
quickSort(array, left + 1, endIndex);
}
public static int[] sort(int[] arrs) {
int[] copyArr = arrs.clone();
quickSort(copyArr, 0, arrs.length - 1);
return copyArr;
}
//二分查找
public static int search(int[] arrs, int left, int right, int searchValue) {
//找不到直接返回-1
if (left > right) {
return -1;
} else {
int midIndex = (left + right) / 2;
if (searchValue > arrs[midIndex]) {
return search(arrs, midIndex + 1, right, searchValue);
} else if (searchValue < arrs[midIndex]) {
return search(arrs, left, midIndex - 1, searchValue);
} else {
return midIndex;
}
}
}
//查询查找元素的所有索引
public static Integer[] searchAllIndex(int[] arrs, int searchValue) {
int index = search(arrs, 0, arrs.length - 1, searchValue);
List<Integer> indexs = new ArrayList<>(arrs.length);
indexs.add(index);
if(index != -1) {
int currentIndex = index;
//往左寻找
while (true) {
currentIndex = currentIndex - 1;
if (currentIndex < 0)
break;
if (arrs[currentIndex] == searchValue) {
indexs.add(currentIndex);
} else {
break;
}
}
//往右寻找
currentIndex = index;
while (true) {
currentIndex = currentIndex + 1;
if (currentIndex == arrs.length)
break;
if (arrs[currentIndex] == searchValue) {
indexs.add(currentIndex);
} else {
break;
}
}
}
Integer[] indexss = new Integer[indexs.size()];
return indexs.toArray(indexss);
}
public static void main(String[] args) {
int[] a = {
4, 5, 10, 3, 1, 5, 7, 9};
int searchValue = 9;
int[] sortedArrs = sort(a);
int index = search(sortedArrs, 0, sortedArrs.length - 1, searchValue);
System.out.printf("未排序数组:%s\n", Arrays.toString(a));
System.out.printf("排序后数组:%s\n", Arrays.toString(sortedArrs));
if (index != -1) {
System.out.printf("查找的元素%d在排序数组中第%d位置\n", searchValue, index);
} else {
System.out.println("查找不到");
}
System.out.printf("查找的元素5在排序数组中所有位置是%s\n",Arrays.toString(searchAllIndex(sortedArrs,5)));
}
}
2.2 非递归 - 循环
//二分查找 - 非递归
public static int search(int[] arrs, int searchValue) {
int left = 0;
int right = arrs.length;
while(true) {
if (left > right) {
return -1;
}else {
Integer mid = (left + right)/2;
if(arrs[mid] == searchValue) {
return mid;
}
if(arrs[mid] > searchValue) {
right = mid - 1;
}else {
left = mid + 1;
}
}
}
}
3. 插值查找 - 有序的元素组
思路:跟二分查找差不多,只是中间索引公式不一样而已
中间索引有searchValue参与,自适应
使用场景
有序数组元素间差值分布比较一样 - 类似线性方程值
元素间分布不均匀不一定比二分查找快
二分查找中间索引公式: ( left + right )/2 → left + 1/2( right - left )
//其实就是上面公式的变种
插值查找中间索引公式: left + ( searchValue - arr[left] )/( arr[right]-arr[left] ) * ( right-left )
public class InsertSearch {
public static Integer[] search(int[] arrs, int left, int right, int searchValue) {
List<Integer> indexs = new ArrayList<Integer>(arrs.length);
//特点注意后面两个判断条件一定要加,-
// 因为midIndex有searchValue进行参与计算,一旦searchValue非常大时,一定会超出索引
// 当然你也可以在计算midIndex后进行判断是否超出数组范围。超过则返回-1 - 一样的效果
if(left > right || searchValue > arrs[arrs.length-1] || searchValue < arrs[0]) {
indexs.add(-1);
}else {
int midIndex = left + ( searchValue - arrs[left] )/( arrs[right]-arrs[left] ) * ( right-left );
if(searchValue > arrs[midIndex]) {
return search(arrs,midIndex+1, right, searchValue);
}else if(searchValue < arrs[midIndex]) {
return search(arrs,left, midIndex+1, searchValue);
}else {
indexs.add(midIndex);
//往左扫描
int curIndex = midIndex;
while(true) {
curIndex = curIndex - 1;
if(curIndex < 0) {
break;
}else {
if(arrs[curIndex] == searchValue) {
indexs.add(curIndex);
continue;
}
break;
}
}
//往右扫描
curIndex = midIndex;
while(true) {
curIndex = curIndex + 1;
if(curIndex == arrs.length) {
break;
}else {
if(arrs[curIndex] == searchValue) {
indexs.add(curIndex);
continue;
}
break;
}
}
}
}
Integer[] indexss = new Integer[indexs.size()];
return indexs.toArray(indexss);
}
public static void main(String[] args) {
int[] arrs = {
1,1,2,3,3,3,3,3,3,4,5,6,7};
String str = Arrays.toString(search(arrs,0, arrs.length-1, 3));
System.out.println(str);
}
}
4. 斐波那契(黄金分隔)查找 - 有序的元素组
思路:跟二分查找类似 - 重要:将分割的两部分之间的长度比是黄金分割比即可 - 而Fibonacci数组相临两元素又无限接近黄金分割比 - 故将Fibonacci与黄金分割结合使用
# 二分查找midIndex
midIndex = (left+right)/2
# 黄金分隔查找midIndex
# 斐波那契数组相邻元素之间的比无限接近 黄金分割比0.618
# 重要:将分割的两部分之间的长度比是黄金分割比即可
fibo(k) = fibo(k-1) + fibo(k-2)
# 二分法的思想就是找中心点进行比较 - 因为中心点占了一个位置故 左右总长度 fibo(k) -1
fibo(k) - 1 = ( fibo(k-1)-1 ) + ( fibo(k-2)-1 ) + 1
# 左边部分元素长度 = ( fibo(k-1)-1 )
# 右边部分元素长度 = ( fibo(k-2)-1 )
# 中心元素占一个位置 1
package top.linruchang.algorithm.search;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @Classname FibonacciSearch
* @Description
* @Date 2022/4/11 13:32
* @Created by lrc
*/
public class FibonacciSearch {
// 返回斐波那契数列第index个位置的数字
public static int fiboNum(int index) {
if (index == 1 || index == 2) {
return 1;
} else {
return fiboNum(index - 1) + fiboNum(index - 2);
}
}
/**
* @param maxValue 斐波那契数列最后一个元素 大于 等于该元素
* @return
*/
public static Integer[] fibonacci3(int maxValue) {
List<Integer> fiboList = new ArrayList<>();
int length = 1;
while (true) {
int fiboNum = fiboNum(length);
fiboList.add(fiboNum);
if (fiboNum >= maxValue)
break;
length++;
}
Integer[] fiboNums = new Integer[fiboList.size()];
return fiboList.toArray(fiboNums);
}
/**
*
* @param arrs
* @param searchValue
* @return 如果最后返回null,说明没找到寻找元素的索引
*/
public static Integer[] search(Integer[] arrs, int searchValue) {
//存储找到的索引
List<Integer> indexs = new ArrayList<Integer>(arrs.length);
int left = 0;
int right ;
int midIndex;
int k; //斐波那契的索引
// 每个元素代表元素长度 -- fibonacci3最后一个元素代表 copyArr数组必须符合的数组长度。 -
Integer[] fibonacci3 = fibonacci3(arrs.length);
// 查看fibonacci3全部元素
System.out.printf("fibonacci数组:%s\n",Arrays.toString(fibonacci3));
// fibonacci3[K] 代表当前部分的长度
k = fibonacci3.length-1;
right = fibonacci3[k];
//构成成fibonacci3[K]长的数组 - 不符合则直接用arrs最后一个元素进行填充
Integer[] copyArr = Arrays.copyOf(arrs, fibonacci3[k]);
int index = arrs.length;
while (true) {
if (copyArr.length == index)
break;
copyArr[index] = arrs[arrs.length-1];
index++;
}
System.out.println("拷贝的数组:" + Arrays.toString(copyArr));
//数组索引小于0直接退出循环,说明找不到了
while(k != 0) {
midIndex = left + fibonacci3[k-1];
//准备右部分开始比较
if(copyArr[midIndex] < searchValue) {
left = midIndex + 1;
k-=2;
//准备左部分开始比较
}else if(copyArr[midIndex] > searchValue) {
right = midIndex - 1;
k--;
}else {
if(midIndex >= arrs.length-1) {
midIndex = arrs.length-1;
}
indexs.add(midIndex);
int currentIndex = midIndex;
//往左寻找
while (true) {
currentIndex = currentIndex - 1;
if (currentIndex < 0)
break;
if (arrs[currentIndex] == searchValue) {
indexs.add(currentIndex);
} else {
break;
}
}
//往右寻找
currentIndex = midIndex;
while (true) {
currentIndex = currentIndex + 1;
if (currentIndex == arrs.length)
break;
if (arrs[currentIndex] == searchValue) {
indexs.add(currentIndex);
} else {
break;
}
}
break;
}
}
if(indexs.size() == 0) {
return null;
}
Integer[] indexss = new Integer[indexs.size()];
return indexs.toArray(indexss);
}
public static void main(String[] args) {
Integer[] a = {
4, 5, 10, 15, 24, 36,36};
System.out.printf("原数组:%s\n" , Arrays.toString(a));
System.out.printf("寻找到的元素索引:%s",Arrays.toString(search(a, 36)));
}
}