八,Java实现简单的查找算法(顺序, 折半, 差值查找)
文章目录
八, 查找算法
在Java中, 我们常用的查找有四种: 顺序查找, 二分(折半)查找, 插值查找, 斐波那契查找(另作一篇文章)
8.1 顺序查找
- 适用于存储结构为顺序或链式存储的线性表
- 顺序查找的基本思想:
又称为无序查找, 从数据结构的一端开始, 顺序扫描, 依次将扫描到的结点关键字与给定值k比较, 若相等则查找成功. 若扫描结束仍没找到关键字等于k的结点,表示查找失败.
- 性能分析:
时间复杂度为o(n), 查找不成功时需要比较n+1次.
顺序表对表无要求,插入数据可在o(1)内完成, 缺点是时间复杂度较大, 数据规模较大时,效率较低.
- 算法较简单, 随便举个栗子:
public class sequanceSearch {
public static void main(String[] args) {
///顺序查找
int target = 12346;
int arr[] = {1,8,10,89,1000,1234};
for(int i=0; i<arr.length; i++){
if(arr[i] == target){
System.out.println("找到了目标值"+target+", 下标为: "+i);
return;
}
}
System.out.println("未找到");
}
}
8.2 二分查找(折半查找)[要求有序]
- 待查找元素必须是有序的, 如果是无序则要进行排序操作.
- 二分查找的基本思想:
①, 中间结点把线性表分为了两个子表, 首先我们用给定值k先与中间结点的关键字比较, 若相等则查找成功;
②, 若不相等, 再根据k与该中间结点关键字的比较结果确定下一步去查找哪一个子表, 这样递归进行, 直到查找到或查找结果发现无匹配.
- 性能分析
最坏情况下, 关键字比较次数为 log2(n+1), 期待时间复杂度为 o(log2n);
优点: 比较次数少, 查找速度快, 平均性能好,占用系统内存较少.
缺点: 要求待查表为有序表, 插入删除困难.
所以折半查找适用于不经常变动但查找较为频繁的有序列表.
- 实现思路:
8.2.1 二分查找基本实现
1. 递归法实现二分查找
public class BinarySearch{
//二分查找递归实现
public int binarySearch(int[] arr, int findVal, int low, int high){
// 使用(low+high)/2会有整数溢出的可能
int mid = low + (high - low)/2;
int midVal = arr[mid];
//写成多个if主要是为了更清楚的显示这个方法的逻辑
//递归出口
if(low > high) return -1;
//左递归
if(midVal > findVal)
return binarySearch(arr, findVal, 0, mid-1);
//右递归
if(midVal < findVal)
return binarySearch(arr, findVal, mid+1, arr.length-1);
if(midVal == findVal)
return mid;
return -1; //注意我们使用 else if写整个方法会更加简洁, 并且这一句可省略;
}
}
2. 非递归方式实现二分查找
package DataStrcture.SearchDemo;
public class NonRecurBinarySearch {
//二分查找的非递归实现
public static int binarySearch(int target, int[] arr) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (arr[mid] == target) return mid;
if (target < arr[mid]) {
right = mid - 1;
}
if (target > arr[mid]) {
left = mid + 1;
}
}
return -1;
}
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7};
int tar = binarySearch(1,arr);
System.out.println("找到了, 索引为"+tar);
}
}
8.2.2 二分查找优化实现(查找多个相同的数)
实现思路
/**
目标: 对折半查找进行优化, 使其能够查找多个相同的数据,并返回数据个数以及索引
思路:
* 1. 我们需要使用一个list存放查找到的目标数据的索引
* 2. 当我们找到目标值arr[mid]时,先在mid左侧遍历, 找寻相同数据
* 3. 然后mid右侧遍历,同样是为了找寻相同数据
* 4. 注意,我们跳出遍历循环的条件是, 遍历左侧或右侧到了头(<0), 尾部(>arr.length-1), 或者 找到的数据不再等于目标值
* (因为折半查找的数据是有序的, 目标值当然是连续的了,)
*/
- 具体实现如下:
import java.util.ArrayList;
import java.util.List;
public class UpdatedBinarySearch {
//测试方法
public static void main(String[] args) {
int arr[] = { 1, 8, 10, 89,1000,1000,1000, 1234};
UpdatedBinarySearch ub = new UpdatedBinarySearch();
ArrayList<Integer> integers = ub.uBinarySearch(arr, 0, arr.length - 1, 1000);
System.out.println(integers);
}
///主方法
public ArrayList<Integer> uBinarySearch(int[] arr, int low, int high, int findVal) {
if (low > high) return new ArrayList<>();//递归出口
int mid = low + (high - low) / 2;
int midVal = arr[mid];
ArrayList<Integer> list = new ArrayList<Integer>();
//左边递归
if (midVal > findVal) {
return uBinarySearch(arr, 0, mid - 1, findVal);
}
//右边递归
else if (midVal < findVal) {
return uBinarySearch(arr, mid + 1, high, findVal);
}
// 找到了(arr[mid] == findVal)
else{
//左边查找相同数据
int temp = mid - 1;
while (true) {
if (temp < 0 || arr[temp] != midVal) break;
list.add(temp);
temp -= 1;
}
list.add(mid);
//右边查找相同数据
int temp_1 = mid + 1;
while (true) {
if (temp_1 > arr.length - 1 || arr[temp_1] != midVal) break;
list.add(temp_1);
temp_1 += 1;
}
}
return list;
}
}
8.3 插值查找[要求有序]
- 基于二分查找算法, 将查找点的选择改进为自适应选择, 可以提高效率. 相应的, 插值查找也属于有序查找
- 性能分析:
查找成功或失败的时间复杂度均为: o(log2(log2n))
插值查找适用于表长较大, 而关键字分布又比较均匀的查找表, 这种情况下的平均性能比折半好的多, 如果分布不均匀, 则插值不太合适.
插值查找的核心: mid值是自适应的
mid值的计算方法:
8.3.1 插值查找的递归实现
注: 插值查找除了mid是自适应的,需要通过固定的计算公式得出意外, 其他的实现跟二分查找基本一致, 但是插值查找在待查序列分布比较均匀(比如序列从小到大或从大到小排列)的查找速度相当快!
- 代码实现:
package DataStrcture.SearchDemo;
import java.util.Arrays;
public class InsertSearch {
public static void main(String[] args) {
int[] arr= new int[100];
for(int i=0; i < 100; i++){
arr[i] = i+1;
}
int findVal = 99;
int index = insertSearch(arr,findVal, 0, arr.length-1);
System.out.println("index = "+index);
}
//插值查找的主方法
public static int insertSearch(int[] arr, int findVal, int low, int high){
System.out.println("hi");
//递归出口
if(low > high || findVal < arr[0] || findVal > arr[arr.length-1]) return -1;
/// 核心: mid///
int mid = low + (high - low)*(findVal - arr[low])/(arr[high] - arr[low]);
int midVal = arr[mid];
if(midVal > findVal){
return insertSearch(arr, findVal, low, mid-1);
}else if(midVal < findVal){
return insertSearch(arr, findVal, mid+1, high);
}else{
return mid;
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)