二分查找算法,插值查找,斐波那契查找

二分查找

原理:给定一个有序的数组,每次从中间开始查找,判断查找的val与中间值的比较,大于继续向后查找,小于向前查找。
步骤:

  1. 得到一个有序的数组。
  2. 计算中间索引,mid = (left + right)/ 2
  3. 与待查找的值进行比较,相等返回mid。
  4. 不相等,向左或向右重复2,3步。

JAVA代码实现:

/**
	 * 二分查找
	 * 
	 * @param arr 排序好的数组
	 * @param left 数组左下标
	 * @param right 数组右下标
	 * @param findVal 待查找的值
	 * @return 查找值的下标
	 */
	public static int binarySearch(int[] arr, int left, int right, int findVal) {
		// 左下标大于右下标,找不到该值
		if (left > right) {
			return -1;
		}
		// 计算mid
		int mid = (left + right) / 2;
		int searchVal = arr[mid];
		if (findVal > searchVal) {
			// 大于查找的值,向右递归查找
			return binarySearch(arr, mid + 1, right, findVal);
		} else if (findVal < searchVal) {
			// 小于查找的值,向左递归查找
			return binarySearch(arr, left, mid - 1, findVal);
		}
		return mid;
	}

如果数组有重复数据,可以返回一个集合,存储所有的下标。

/**
	 * 查找符合条件的值的下标
	 * 参数同二分查找
	 * 
	 * @param arr
	 * @param left
	 * @param right
	 * @param findVal
	 * @return
	 */
	public static List<Integer> binarySearchAll(int[] arr, int left, int right, int findVal) {
		if (left > right) {
			return null;
		}
		int mid = (left + right) / 2;
		int searchVal = arr[mid];
		if (findVal > searchVal) {
			return binarySearchAll(arr, mid + 1, right, findVal);
		} else if (findVal < searchVal) {
			return binarySearchAll(arr, left, mid - 1, findVal);
		}
		// 创建一个list集合,添加查找到的mid下标
		List<Integer> result = new ArrayList<Integer>();
		result.add(mid);
		// 将mid-1得到查找值的左边下标
		int temp = mid - 1;
		while (true) {
			// 一直循环,直到左边没有查找的值
			if (temp < 0 || arr[temp] != findVal) {
				break;
			}
			result.add(temp);
			temp--;
		}
		temp = mid + 1;
		while (true) {
			// 一直循环,直到右边没有查找的值
			if (temp > arr.length - 1 || arr[temp] != findVal) {
				break;
			}
			result.add(temp);
			temp++;
		}
		return result;
	}

插值查找

跟二分查找大部分相同,只是计算mid位置的时候不一样,该查找算法适合数组中元素相差较大时使用。
mid计算:mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]),计算查找的值在数组中的比例。

public static int insertValueSearch(int[] arr, int left, int right, int findVal) {
        if (left > right || findVal < arr[0] || findVal > arr[arr.length - 1]) {
            return -1;
        }
        // 除了计算mid不一样 其它跟二分一样
        int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]);
        int midVal = arr[mid];
        if (findVal > midVal) {
            return insertValueSearch(arr, mid + 1, right, findVal);
        } else if (findVal < midVal) {
            return insertValueSearch(arr, left, mid - 1, findVal);
        } else {
            return mid;
        }
    }

斐波那契查找

斐波那契数列:开始为1,1后面每位数字都是前两个数字相加。
例如:1 1 2 3 5 8 13…
这个数列,越往下就越趋近于0.618,也就是黄金分割,所以该查找就是构建一个斐波那契数列来找到数组中的黄金分割点,进行查找。

有一个待查找的数组,假如长度只有7,则循环遍历斐波那契的数组,发现数组中8大于7,而数组长度只有7,构建一个长度为8的新数组,把原数组copy过去,多出来的部分使用原数组的最大值进行填充。(从小到大排序的),记录当前斐波那契数组下标为k。
现在就有一个长度8的数组,然后找到k - 1的位置下的斐波那契数,分割点就是mid = fib[k - 1] + 查找数组的最小下标(low) - 1,数组就分割成了0 1 2 3 【4】 5 6 7,然后发现这个数组位置的数据不是自己要的,判断是大于还是小于,小于就往左找,查找数组的最大下标(high) = mid - 1,将k自减,继续上面步骤。大于往右找,k自减2,mid+1,重复上面步骤。
JAVA代码实现:

// 获取斐波那契数列
	public static int[] fib(int maxSize) {
		// 定义一个斐波那契数列
		int[] f = new int[maxSize];
		f[0] = 1;
		f[1] = 1;
		// 从2开始,后面数组中的值等于前两项相加
		for (int i = 2; i < maxSize; i++) {
			f[i] = f[i - 1] + f[i - 2];
		}
		return f;
	}
	/**
	 * 
	 * @param arr 待查找的数组
	 * @param key 查找的值
	 * @return 找到的下标值
	 */
	public static int fibSearch(int[] arr, int key) {
		// 数组查找的最小下标
		int low = 0;
		// 最大下标
		int high = arr.length - 1;
		// 斐波那契数组的下标
		int k = 0;
		// 分割点
		int mid = 0;
		// 获取斐波那契数组
		int[] fib = fib(20);
		// 找到不小于high的斐波那契数
		while (high > fib[k] - 1) {
			k++;
		}
		// fib[k]值可能大于arr的长度,因此需要构造新的数组,并指向arr
		int[] temp = Arrays.copyOf(arr, fib[k]);
		for (int i = high + 1; i < temp.length; i++) {
			// 用数组最大值填充多余部分
			temp[i] = arr[high];
		}
		// 当最小下标小于等于最大下标时,一直循环
		while (low <= high) {
			// 计算分割点
			mid = low + fib[k - 1] - 1;
			if (key < temp[mid]) {
				// key的值小于mid值 向左查找,k自减
				high = mid - 1;
				k--;
			} else if (key > temp[mid]) {
				// 向右查找,k自减2
				low = mid + 1;
				k -= 2;
			} else {
				// key等于mid 当mid小于等于high的时候,返回mid 
				if (mid <= high) {
					return mid;
				} else {
					// 大于high的时候证明找到了扩充的数据,返回high
					return high;
				}
			}
		}
		return -1;
	}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dichotomy_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值