算法系列二:插值查找、斐波那契查找

书接上回:算法系列一:顺序查找、二分查找、分块查找

四. 插值查找

4.1 简介

上一篇的二分查找中,每次查找都是从待查序列的中间开始,那么就很容易想到一个问题或者场景:假设我有一个数组0~999,我要查找的数为900,如果按照二分查找,那我每次取中间值要查找好一会,我们从上帝视角看明显可以从靠后的位置去查。于是针对这种情况,出现了插值查找

  • 基本思路
      插值查找是根据要查找的关键字key与查找表中最大最小记录的关键字比较后的 查找方法,其核心就在于插值的计算公式 (key-array[low])/(array[high]-array[low])*(high-low)。简而言之,基于二分查找算法,将查找点的选择改进为自适应选择。
    复杂度分析
  • 时间复杂性:如果元素均匀分布,则O(log(logn)),在最坏的情况下可能需要O(n)。
  • 空间复杂度:O(1)。
  • 优缺点分析
      对于长度比较长、关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。

区别对比
① 插值查找类似于二分法,但插值查找每次从自适应 mid 处开始查找;
② 二分查找种mid为(left+right) / 2, 插值查找则是通过公式计算得来;
③ 插值查找公式:int mid = low + (high - low) * (key - arr[low]) / (arr[high] - arr[low]) ;low表示左边索引left,high表示右边索引right. key 就是需要查找的值。

插值查找特点
① 查找序列有序;
数据量大、关键字分布均匀的有序序列,插值查找快;
③ 对于分布不均匀的有序序列来说,不一定比二分查找好。

4.2 代码实现

① 迭代法查找:

private static int insertSearch1(int arr[],int target){
	/*初始化左右搜索边界*/
	   int left=0,right=arr.length-1;
	   int mid;
	   while(left<=right){
	       mid=left+(target-arr[left])/(arr[right]-arr[left])*(right-left);
	       /*arr[mid]大于target,即要寻找的元素在左半边,所以需要设定右边界为mid-1,搜索左半边*/
	       if(target<arr[mid]){
	           right=mid-1;
	       /*arr[mid]小于target,即要寻找的元素在右半边,所以需要设定左边界为mid+1,搜索右半边*/
	          }else if(target>arr[mid]){
	           left=mid+1;
	       /*搜索到对应元素*/
	       }else if(target==arr[mid]){
	           return mid;
	       }
	   }
	   /*搜索不到返回-1*/
	   return -1;
	}

② 递归法实现:

private static int insertSearch2(int array[],int left,int right,int target){
	if(left<=right){
		int mid=left+(target-array[left])/(array[right]-array[left])*(right-left);
		/*搜索到对应元素*/
		if(array[mid]==target){
			return mid;
		}else if(array[mid]<target){
			/*array[mid]小于target,即要寻找的元素在右半边,所以需要设定左边界为mid+1,搜索右半边*/
			return insertSearch2(array,mid+1,right,target);
		}else{
			/*array[mid]大于target,即要寻找的元素在左半边,所以需要设定右边界为mid-1,搜索左半边*/
			return insertSearch2(array,left,mid-1,target);
		}
	}else{
		return -1;
	}
}

五. 斐波那契查找

5.1 简介

斐波那契查找又名黄金分割查找算法。

  1. 什么是黄金分割?
    黄金分割点是指把一条线段分割为两部分,使其中一部分与全长之比等于另一部分与这部分之比。取其前三位数字的近似值是0.618.由于按此比例设计的造型十分美丽,因此称为黄金分割,也称中外比。
  2. 什么是斐波那契数列?
    斐波那契数列{1, 1, 2, 3, 5, 8, 13, 21, 34, 55…}发现斐波那契数列的两个相邻数的比例,无限接近黄金分割值0.618

算法原理
斐波那契查找原理与二分查找算法和插值查找算法非常相似,仅仅改变了中间节点(mid)的位置,mid不再是中间或插值得到,而是位于黄金分割点附近,即mid=low+F(k-1)-1(F代表斐波那契数列)。

???怎么理解这里的mid

  • 由斐波那契数列F[k] = F[k-1] + F[k-2]的性质,可以得到(F[k]-1) = (F[k-1]-1) + (F[k-2]-1) + 1. 该式说明:只要顺序表的长度为F[k]-1,则可以将该表分成长度为F[k-1]-1 和F[k-2]-1的两段,如上图所示。从而中间位置为mid=low+F(k-1)-1.
  • 类似的,每一字段也可以用相同的方式分割
  • 但顺序表长度n不一定刚好等于F[k]-1,所以需要将原来的顺序表长度n增加至F[k]-1.这里的k值只要能使F[k]-1恰好大于或等于n即可,由以下代码得到,顺序表长度增加后,新增的位置(从n+1 到 F[k]-1位置),都赋为n位置的值即可
while(n>fib(k)-1){
  k++;
}
  • 复杂度分析
    最坏情况下,时间复杂度为O(logn),且其期望复杂度也为O(logn)。

5.2 算法实现

public class FibonacciSearch {
	
	public static int FLENGTH = 20;
	public static void main(String[] args) {
		int [] arr = {1,8,10,89,100,134};
		int target = 89;
		System.out.println("目标元素在数组中位置是:" + fibSearch(arr, target));		
	}

	public static int[] fib() {
		int[] f = new int[FLENGTH];
		f[0] = 1;
		f[1] = 1;
		for (int i = 2; i < FLENGTH; i++) {
			f[i] = f[i-1] + f[i-2];
		}
		return f;
	}
	
	public static int fibSearch(int[] arr, int target) {
		int low = 0;
		int high = arr.length - 1;
		int k = 0; 
		int mid = 0; 
		int f[] = fib();
		/*获取最相邻的斐波那契数组中元素的值,该值略大于数组的长度*/
		while(high > f[k] - 1) {
			k++;
		}
		/*因为 f[k]值可能大于arr的长度。如果大于时,需要构造一个新的数组temp[],将arr数组中的元素拷贝过去,不足的部分会使用0填充*/
		int[] temp=Arrays.copyOf(arr, f[k]);
		/*然后将temp后面填充的0,替换为最后一位数字
		 *如将temp数组由{1,8,10,89,100,134,0,0}变换为{1,8,10,89,100,134,134,134}*/
		for(int i = high + 1; i < temp.length; i++) {
			temp[i] = arr[high];
		}
		
		while (low <= high) { 
			mid = low + f[k - 1] - 1;
			if(target < temp[mid]) { 
				high = mid - 1;
				/*因为f[k]=f[k-1]+f[k-2],所以k--就相当于取temp数组的左边部分*/
				k--;
			} else if ( target > temp[mid]) { 
				low = mid + 1;
				/*同理,f[k]=f[k-1]+f[k-2],k -= 2就相当于取temp数组的右边部分*/
				k -= 2;
			} else {
				/*原arr数组中的值*/
				if(mid <= high){
					return mid;
				/*在temp中,扩展出来的高位的值*/
				}else{
					return high;
				}
			}
		}
		return -1;
	}
}

这里的算法实现可以理解可以看这位博主


活动地址:CSDN21天学习挑战赛

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值