java基础---数组的查找算法(2)
一、查找的基本概念
查找分为有序查找和无序查找,这里均以数组为对象,有序查找指的是数组元素有序排列,无序查找指的是数组元素有序或无序排列
- 平均查找长度(Average Search Length,ASL):和指定查找元素key进行比较的表中数据的个数的期望值
对于含有n个数据元素的查找表,查找成功的平均查找长度为:
ASL = Pi*Ci
的和。Pi:查找表中第i个数据元素的概率。
Ci:找到第i个数据元素时已经比较过的次数。
- 查找分类:线性查找(顺序查找)、二分查找(折半查找)、插值查找、斐波那契查找、分块查找、哈希查找、二叉树查找
- 查找性能从慢到快:
- 顺序查找,时间复杂度O(N)
分块查找,时间复杂度O(logN+N/m);
二分查找,时间复杂度O(logN)
Fibonacci查找,时间复杂度O(logN)
插值查找,时间复杂度O(log(logN))
哈希查找,时间复杂度O(1)
二、 线性查找
顺序查找适合于存储结构为顺序存储或链接存储的线性表,即查找对象存储空间连续
时间复杂度为O(n)
查找成功时的平均查找长度为:(假设每个数据元素的概率相等)
ASL =(1+2+3+…+n)/n = (n+1)/2 ;
当查找不成功时,需要n+1次比较,时间复杂度为O(n);
package Search; import java.util.ArrayList;
import java.util.List; public class LinearSearch { public static void main(String[] args) {
int[] arr={1,9,19,0,-2,9,43};
System.out.println(firstlinearSearch(arr,-2));
System.out.println(lastlinearSearch(arr,-1));
System.out.println(repeatlinearSearch(arr,9));
}
//返回顺序查找第一次找到的下标位置
private static int firstlinearSearch(int[] arr, int key) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == key) {
return i;
}
}
return -1;}
//返回顺序查找最后一次找到的下标位置
private static int lastlinearSearch(int[] arr, int key) {
int index=-1;
for (int i = 0; i < arr.length; i++) {
if (arr[i] == key) {
index=i;
}
}
return index;
}
//返回所有重复的元素下标
private static List<Integer> repeatlinearSearch(int[] arr, int key) {
List<Integer> list= new ArrayList<>();
for (int i = 0; i < arr.length; i++) {
if (arr[i] == key) {
list.add(i);
}
}
return list;
}
}
三、 二分查找
折半查找,属于有序查找算法。这里的有序是指查找的数组元素是有序排列的
用给定值k先与中间结点的关键字比较,中间结点把线形表分成两个子表,若相等则查找成功;若不相等,再根据k与该中间结点关键字的比较结果确定下一步查找哪个子表,
递归思想
递归条件:查找值>中间值, 向右递归;查找值<中间值, 向左递归
终止条件:查找值=中间值, 返回中间值坐标; 查找区间溢出(left>right), 返回-1;
- 如果有溢出,说明left+right>Integer.MAX_VALUE
对待有重复元素的数组:当 查找值=中间值, 在中间值坐标向左向右遍历(temp>left && arr[temp]==arr[mid];temp<right&&arr[temp]=arr[mid]),直到找到与中间值不相等的元素,顺序添加到表中
- 非递归思想,直接用循环while(left<right)
- 查找值>中间值, left=mid; 查找值<中间值, right=mid;
- 终止条件:查找值=中间值, 返回中间值坐标; 查找区间溢出(left>right), 返回-1;
最坏情况下,关键词比较次数为log2(n+1),且期望时间复杂度为O(log2n);
折半查找的前提条件是需要有序表顺序存储,对于静态查找表,一次排序后不再变化,折半查找能得到不错的效率。
但对于需要频繁执行插入或删除操作的数据集来说,维护有序的排序会带来不小的工作量,那就不建议使用
package Search;
import java.util.ArrayList;
import java.util.List;
public class BinarySearch {
public static void main(String[] args) {
int[] arr={1,2,3,4,5,6,7,8,8,8,10,23,45,67,89,100,101};
System.out.println(binarysearch(arr,0,arr.length-1,8));
System.out.println(repeatbinarysearch(arr,0,arr.length-1,8));
System.out.println(bobinarysearch(arr,0,arr.length-1,8));
} //返回所有与查找元素相同的所有元素下标
private static List<Integer> repeatbinarysearch(int[] arr, int left, int right, int key) {
if (left>right) return new ArrayList<>();
List<Integer> list= new ArrayList<>();
int mid=(left+right)/2;
if (arr[mid]<key){
return repeatbinarysearch(arr,mid+1,right,key);
}else if (key<arr[mid]){
return repeatbinarysearch(arr,left,mid-1,key);
}else {
int temp=mid-1;
while (true) {
if (temp < left || arr[temp] != arr[mid]) {
break;
}
else temp--;
}
for (int i= temp+1;i<mid;i++){
list.add(i);
}
temp=mid;
while (true) {
if (temp >right || arr[temp] != arr[mid]) {
break;
}
else {list.add(temp);
temp++;}
}
return list;
}
} //不一定返回的是顺序第一次找到的下标
private static int binarysearch(int[] arr, int left, int right, int key) {
if (left>right) return -1;
int mid= (left+right)/2;
if (arr[mid]<key){
return binarysearch(arr,mid+1,right,key);
}else if (key < arr[mid]){
return binarysearch(arr,left,mid-1,key);
}else return mid;
}
//非递归
private static int bobinarysearch(int[] arr, int left, int right, int key) {
if (arr==null||arr.length==0) return -1;
if (left>right) return -1;
int mid;
while (left<right){
mid=left+(right-left)/2;
if (arr[mid]==key)return mid;
else if(arr[mid]<key) left=mid+1;
else right=mid-1;
}
return -1;
}
}
四、 插值查找
基于二分查找算法,将查找点的选择改进为自适应选择,可以提高查找效率,属于有序查找
查找成功或者失败的时间复杂度均为O(log2(log2n))
对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。
反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。
与二分查找唯一的不同点是中值的选择利用了自适应算法 mid=left+(right-left) *(key-arr[left])/(arr[right]-arr[left])
package Search; import java.util.ArrayList;
import java.util.List; public class InsertValueSearch {
public static void main(String[] args) {
int[] arr=new int[100];
for (int i=0;i<arr.length;i++){
arr[i]=i+1;
}
arr[8]=8;
System.out.println(insertsearch(arr,0,arr.length-1,8));
System.out.println(repeatinsertsearch(arr,0,arr.length-1,8)); } private static List<Integer> repeatinsertsearch(int[] arr, int left, int right, int key) {
if (left > right) return new ArrayList<>();
List<Integer> list = new ArrayList<>();
//唯一的不同点是中值的选择利用了自适应算法
//low + (high-low) * (value-a[low]) / (a[high]-a[low]);
int mid = left + (right - left) * (key - arr[left]) / (arr[right] - arr[left]);
if (arr[mid] < key) {
return repeatinsertsearch(arr, mid + 1, right, key);
} else if (key < arr[mid]) {
return repeatinsertsearch(arr, left, mid - 1, key);
} else {
int temp = mid - 1;
while (true) {
if (temp < left || arr[temp] != arr[mid]) {
break;
} else temp--;
}
for (int i = temp + 1; i < mid; i++) {
list.add(i);
}
temp = mid;
while (true) {
if (temp > right || arr[temp] != arr[mid]) {
break;
} else {
list.add(temp);
temp++;
}
}
return list;
}
}
private static int insertsearch(int[] arr, int left, int right, int key) {
if (left>right) return -1;
int mid=left+(right-left)*(key-arr[left])/(arr[right]-arr[left]) ;
if (arr[mid]<key){
return insertsearch(arr,mid+1,right,key);
}else if (key < arr[mid]){
return insertsearch(arr,left,mid-1,key);
}else return mid;
}
}
五、 斐波那契查找
黄金分割比例 :要求开始表中记录的个数为某个斐波那契数小1,n=F(k)-1;
最坏情况下,时间复杂度为O(log2n),且其期望复杂度也为O(log2n)。
生成的数组长度是f[k]-1而不是f[k]
f[k]-1=f[k-1]-1+f[k-2]-1+1,只要顺序表的长度为f[k]-1,就可以将其分为左右两端,左边端为f[k-1]-1,右边为f[k-2]-1,中间值mid= left+ f[k-1]-1
步骤:
生成一个斐波那次数组,这个数组的长度为f[k]-1
生成的数组很可能比要比较的数组元素大,因此要进行元素赋值和填充
package Search; import java.util.Arrays;
import java.util.List; public class FibonacciSearch {
public static void main(String[] args) {
int[] arr={1,2,3,4,5,6,7,8,8,8,10,23,45,67,89,100,101};
System.out.println(fibonacci(arr,100));
// System.out.println(repeatfibonacci(arr,8));
}
//构造斐波那次数组
private static int[] fib(int size) {
int[] f=new int[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;
}
private static int fibonacci(int[] arr, int key) {
int low = 0;
int high = arr.length-1;
int f[]= fib(arr.length);
//此时已经找到比high大的那个斐波那次点
int k=0;
while (high-low>f[k]-1) k++;
//复制一份arr,但长度可能有扩充
int[] temp = Arrays.copyOf(arr, f[k]);
//把扩充后的元素都赋值为当前数组的最大元素
for (int i=high;i<temp.length;i++) temp[i]=arr[high];
int mid=0;
while (low<=high){
mid= low + f[k-1]-1;
//此时f[k-1]指代的是左半部分
if (temp[mid]>key){
high=mid-1;
k--;
} //f[k-2]指代右半部分
else if (temp[mid] < key){
low=mid+1;
k-=2;
}
else return Math.min(mid, high);
}
return -1;
}
}
六、分块查找
分块查找是结合二分查找和顺序查找的一种改进方法。在分块查找里有索引表和分块的概念。索引表就是帮助分块查找的一个分块依据,索引表是二分查找,而后进行顺序查找
分块查找只需要索引表有序,类似于哈希表,但又不如散列表好用。假设每个分块的长度为s,则分块查找的时间复杂度可以近似为:
,即
- 分块查找算法的查找算法并不复杂,复杂的时索引表的建立与维护,当元素加、减、修改时,如何保证各个分块之间依旧相对有序且各个分块的大小均匀,是分块查找算法最大的挑战
java基础---数组的查找算法(2)的更多相关文章
- java基础---数组的排序算法(3)
一.排序的基本概念 排序:将一个数据元素集合或序列重新排列成按一个数据元素某个数据项值有序的序列 稳定排序:排序前和排序后相同元素的位置关系与初始序列位置一致(针对重复元素来说,相对位置不变) 不稳定 ...
- java基础-数组的折半查找原理
java基础-数组的折半查找原理 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 如果让你写一个数组的查找功能,需求如下:在一个数组中,找一个元素,是否存在于数组中, 如果存在就返回 ...
- Java中常用的查找算法——顺序查找和二分查找
Java中常用的查找算法——顺序查找和二分查找 神话丿小王子的博客 一.顺序查找: a) 原理:顺序查找就是按顺序从头到尾依次往下查找,找到数据,则提前结束查找,找不到便一直查找下去,直到数据最后一位 ...
- Java学习之二分查找算法
好久没写算法了.只记得递归方法..结果测试下爆栈了. 思路就是取范围的中间点,判断是不是要找的值,是就输出,不是就与范围的两个临界值比较大小,不断更新临界值直到找到为止,给定的集合一定是有序的. 自己 ...
- Java基础——数组应用之StringBuilder类和StringBuffer类
接上文:Java基础——数组应用之字符串String类 一.StringBuffer类 StringBuffer类和String一样,也用来代表字符串,只是由于StringBuffer的内部实现方式和 ...
- Java基础-数组常见排序方式
Java基础-数组常见排序方式 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 数据的排序一般都是生序排序,即元素从小到大排列.常见的有两种排序方式:选择排序和冒泡排序.选择排序的特 ...
- 《Java基础——数组的定义与使用》
Java基础--数组的定义与使用 一. 一维数组: 格式一: 数组类型 数组变量[]=new 数据类型[长度]; //需要后续赋值,且后续赋值时只能为单个元素赋值. 或 数组类型 数组变量 ...
- Java实现的二分查找算法
二分查找又称折半查找,它是一种效率较高的查找方法. 折半查找的算法思想是将数列按有序化(递增或递减)排列,查找过程中采用跳跃式方式查找,即先以有序数列的中点位置为比较对象,如果要找的元素值小 于该中点 ...
- Java基础(50):二分法查找的非递归实现和递归实现(完整代码可运行,参考VisualGO理解更佳)
一.概念 二分查找算法也称折半查找,是一种在有序数组中查找某一特定元素的搜索算法. 二.算法思想 搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束:如果某一特定元素大于或者 ...
随机推荐
- Redis-内存优化(一)
一.正确使用redis 数据类型 我们先了解下 String 类型的内存空间消耗问题,以及选择节省内存开销的数据类型的解决方案.例如一个图片存储系统,要求这个系统能快速地记录图片 ID 和图片在存储系 ...
- 5G通讯与芯片
5G通讯与芯片 美国商务部可能接近达成一项新的规则,允许美国公司与华为重启谈判,在共同制定下一代通信技术5G标准方面进行合作. 华为美国首席安全官安迪·珀迪(Andy Purdy)向第一财经记者独家回 ...
- 主成分分析法(PCA)原理和步骤
主成分分析法(PCA)原理和步骤 主成分分析(Principal Component Analysis,PCA)是一种多变量统计方法,它是最常用的降维方法之一,通过正交变换将一组可能存在相关性的变量数 ...
- AMD Ryzen 5000‘Cezanne’APU
AMD Ryzen 5000'Cezanne'APU Spotted,Zen 3&7nm Vega芯片将在2021年前保留AM4支持 AMD Ryzen 5000 'Cezanne' APU ...
- h265player开发
h265player开发 https://github.com/goldvideo/h265player 简介 随着视频编码技术的发展,相比H.264, H.265同等画质体积仅为一半.带宽占用省一半 ...
- httprunner 2.5.7 下.env 文件环境变量的使用及debugtalk的使用,对test的参数化及执行
一.httprunner 2.5.7 下.env 文件的使用 1..env 文件配置如下: 2.debugtalk.py 编写如下: 在debugtalk.py中增加开始和结束执行语句: 3.需要做 ...
- thymeleaf模板引擎基础知识
一.表达式 分为四类: 1.变量表达式 ${} :获取容器上下文变量的值. 举例: 获取application域中的username: ${application.username} 获取sessio ...
- mybatis学习——映射器(mappers)
在定义 SQL 映射语句之前,我们需要告诉 MyBatis 到哪里去找到这些语句. 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射 ...
- C语言const关键字的作用
1 #define _CRT_SECURE_NO_WARNINGS 2 #include <stdio.h> 3 #include <string.h> 4 #include ...
- Android客户端网络预连接优化机制探究
一.背景 一般情况下,我们都是用一些封装好的网络框架去请求网络,对底层实现不甚关注,而大部分情况下也不需要特别关注处理.得益于因特网的协议,网络分层,我们可以只在应用层去处理业务就行.但是了解底层的一 ...