1. 线性查找算法
/**
* 线性查找算法
* 找到一个满足条件的值就返回,若未查找到,返回-1
* @param arr
* @param n
* @return
*/
public static int seqSearch(int []arr,int n){
//线性查找是逐一对比,发现有相同值,就返回下标
for (int i = 0; i < arr.length; i++) {
if(arr[i]==n){
return i;
}
}
return -1;
}
2. 二分查找算法
/**
* 二分查找算法
* 找到一个满足条件的值就返回,若未查找到,返回-1
* @param arr 数组
* @param left 左边的索引
* @param right 右边的索引
* @param n 要查找的值
* @return 若查找到,返回下标;若未查找到,返回-1
*/
public static int binarySearch(int []arr,int left,int right,int n){
//递归出口,未找到
if(left>right){
return -1;
}
int mid=(left+right)/2;
//如果arr[mid]>n,在左边,向左递归
if(arr[mid]>n){
//right=mid-1;
return binarySearch(arr, left, mid-1, n);
}
//如果arr[mid]<n,在右边,向右递归
else if(arr[mid]<n){
//left=mid+1;
return binarySearch(arr, mid+1, right, n);
}
//如果arr[mid]=n,直接返回mid
else{
return mid;
}
}
/**
* 二分查找算法
* 找到所有满足条件的值返回,若未查找到,返回null
* @param arr
* @param left
* @param right
* @param n
* @return
*/
public static ArrayList<Integer> binarySearch2(int []arr,int left,int right,int n){
//递归出口,未找到
if(left>right){
return null;
}
int mid=(left+right)/2;
//如果arr[mid]>n,在左边,向左递归
if(arr[mid]>n){
//right=mid-1;
return binarySearch2(arr, left, mid-1, n);
}
//如果arr[mid]<n,在右边,向右递归
else if(arr[mid]<n){
//left=mid+1;
return binarySearch2(arr, mid+1, right, n);
}
//如果arr[mid]=n,向左向右扫描,找到区间
else{
ArrayList<Integer> resIndex=new ArrayList<>();
resIndex.add(mid);
//向左扫描
int tmpL=mid-1;
while(true){
if(tmpL<0||arr[tmpL]!=n){
break;
}
resIndex.add(tmpL);
tmpL--;
}
int tmpR=mid+1;
while(true){
if(tmpR>arr.length-1||arr[tmpR]!=n){
break;
}
resIndex.add(tmpR);
tmpR++;
}
return resIndex;
}
}
3. 插值查找算法
插值查找算法类似于二分查找,不同的是插值查找每次从自适应处开始查找。
折半查找中的mid索引的公式,low表示左边索引,high表示右边索引。
插入查找的推导:
假设数组的数是有序且均匀的,则findValue的位置可按这样的顺序来查找。
联立1,2可得
/**
* 插值查找:改进的二分查找(有序)
* 对mid值的取法进行改进,将要查找的元素的值的大小与数组元素的值的大小也考虑进去。
* @param arr
* @param low
* @param high
* @param n
* @return
*/
public static int insertSearch(int []arr,int low,int high,int n){
//递归出口,未找到(注意mid越界)
if(low>high||n<arr[low]||n>arr[high]){
return -1;
}
int mid=low+(n-arr[low])*(high-low)/(arr[high]-arr[low]);
//如果arr[mid]>n,在左边,向左递归
if(arr[mid]>n){
//right=mid-1;
return insertSearch(arr, low, mid-1, n);
}
//如果arr[mid]<n,在右边,向右递归
else if(arr[mid]<n){
//left=mid+1;
return insertSearch(arr, mid+1, high, n);
}
//如果arr[mid]=n,直接返回mid
else{
return mid;
}
}
注意:对于数据量较大,关键字分布较均匀的查找表来说,采用插值查找,速度较快,关键字分布不均匀的情况下,该方法不一定比折半查找要好。
4. 斐波那契查找算法
/**
* 获取一个斐波那契数列
* @return
*/
public static int[] Fibonacci(){
int []Fib=new int [max_Size];
Fib[0]=1;
Fib[1]=1;
for(int i=2;i<Fib.length;i++){
Fib[i]=Fib[i-1]+Fib[i-2];
}
return Fib;
}
/**
* 斐波拉契查找算法
* @param arr 数组
* @param key 需要查找的数
* @return 返回index,若无,返回-1
*/
public static int fibonaccSearch(int []arr,int key){
int low=0; //数组第一个元素的索引
int high=arr.length-1; //数组最后一个元素的索引
int k=0; //表示斐波那契分割数值的下标
int mid=0; //存放mid的值
int []fibo=Fibonacci(); //获取斐波拉契数列
//将原数组的长度变为(斐波拉契长度-1)的新数组
//注意此处的循环条件:保证新数组能完全包含原来的数组
while(fibo[k]-1<arr.length){
k++;
}
int []tmp=Arrays.copyOf(arr, fibo[k]-1);
for(int i = high + 1; i < tmp.length; i++) {
tmp[i] = arr[high]; //新数组的高位0用原数组的最高位填充
}
// 使用while来循环处理,找需要查找的数 key
// 只要这个条件满足,就可以找
while (low <= high) {
mid = low + fibo[k - 1] - 1;
//继续向数组的前面查找(左边)
if(key < tmp[mid]) {
high = mid - 1;
k--;
}
//继续向数组的后面查找(右边)
else if ( key > tmp[mid]) {
low = mid + 1;
k -= 2;
}
/*这是有问题的写法,因为这是在新数组中高于原数组的数全用原数组的最高位填充,可能返回的是原数组中不存在的索引
* eg[1,2,3,4,5,6]->[1,2,3,4,5,6,6]
* 若查找6,会返回6,而根据实际情况,应返回5
else {
return mid;
}*/
//找到,若mid不超过原数组,返回mid,若超过原数组,返回high
else{
if(mid<=high){
return mid;
}
return high;
}
}
return -1; //未找到,返回-1
}