【数据结构】之折半查找(二分搜索)

本文介绍折半查找(二分搜索)算法,它是在有序数组中查找特定元素的搜索算法。阐述其原理,通过猜物品价格、找未消磁书等例子说明应用,还给出 Java 和 C 语言实现代码,对比不同实现方式及时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

博主声明:

转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主 威威喵 原创,请多支持与指教。

本文首发于此   博主威威喵  |  博客主页https://blog.youkuaiyun.com/smile_running

    我们学习数据结构与算法时,肯定会学到过折半查找,它也称作二分搜索,是一种在有序数组中查找某一特定元素的搜索算法。这里有一个前提:有序数组,必须牢记。

    折半查找的原理是:在一个数组的中间位置开始查找,如果要查找的目标值恰好等于数组中间的数值,则查找成功,这也是最理想的一种情况。当然也有可能小于它,这时要查找的目标值必定会在前半截数组中,因为数组是有序的。反之,若大于它,则必定在后半截的数组中。以此一直循环或递归操作,直到找到目标值的下标,或者这个数组查无此值为止。

    二分查找算法在生活中的例子也比比皆是,比如我之前在一个综艺节目上看到的一个猜物品价格的小游戏。规定这个物品的价格在 800 ~ 1200 元之间,然后猜的离答案最接近的一个获胜,每一位参与者有 5 次机会。虽然是靠运气,也许你盲目的猜 5 次,可能会接近价格。但是用二分查找法来猜,那肯定是比盲目瞎猜来的好。

    比如,我们第一次可以先猜 1000 元,然后看价格高了还是低了。低了话就猜 1100 元,高了话就猜 900 元,以此类推下去,虽然只有 5 次机会,但总能无限地接近真实的价格。最后再讲一个笑话,之前在网上看段子的时候无意间看到的。

    小故事:【威威猫】从书店出来时,手里抱着一堆书,可能有一本没有消磁,警报响了。他一本一本拿出来靠近警报检查底是哪一本。这个时候,在一旁的保安大哥一把把书抢过来,把所有的书分成两堆,果然只有一堆警报会响,然后把响的那一堆再分成两堆。以此类推下去,于是很快的找出了那一本未消磁的书。

    从这个例子我们可以得出一个结论,保安大哥是一位程序员,哈哈。保安大哥用二分查找算法,很快的找出了目标,而【威威猫】一本一本的尝试,循环遍历找出目标,可是花费时间比较长,这就是算法的时间复杂度的比较了。接下来呢,我们来看看代码究竟该如何写。

    首先来看看折半查找的动态图,更加形象生动,容易理解。

    我们来看 Java 代码如何写吧,代码也非常简单。如下:

package com.xww;

/**
 * 二分法查找(前提:有序数组或集合)
 * 
 * @author xww 博主:威威喵 博客:https://blog.youkuaiyun.com/smile_Running
 */
public class BinarySearch {

	// 有序数组
	static int arr[] = new int[] { 2, 5, 7, 11, 19, 35, 46, 50, 64, 73, 81, 96 };

	public static void main(String[] args) {

		int len = arr.length;

		// 从数组中找目标为 51
		int result1 = binarySearch(arr, 51);
		if (result1 > 0) {
			System.out.println("所在位置:" + result1);
		} else {
			System.out.println("没有找到");
		}

		// 从数组中找目标为 50
		int result2 = binarySearchRecursion(arr, 0, len, 50);
		if (result2 > 0) {
			System.out.println("所在位置:" + result2);
		} else {
			System.out.println("没有找到");
		}
	}

	/**
	 * 
	 * @param arr
	 *            查找的数组目标
	 * @param key
	 *            查找的值
	 */
	static int binarySearch(int arr[], int key) {
		int low = 0;
		int high = arr.length;

		// 若low一直小于high,说明查找还在持续中
		while (low <= high) {
			// 取得low与high的中间位置
			int mid = (low + high) / 2;
			// 如果要查找的目标key正好等于数组arr[mid]的值,则查找成功
			if (key == arr[mid])
				return mid;
			// 若key比较大,往arr[mid]右边查找
			else if (key > arr[mid])
				low = mid + 1;
			// 若key比较小,往arr[mid]左边边查找
			else
				high = mid - 1;
		}
		return -1;
	}

	/**
	 * 递归方式
	 * 
	 * @param arr
	 *            查找的数组目标
	 * @param low
	 *            二分起始位置
	 * @param high
	 *            二分结束位置
	 * @param key
	 *            查找的值
	 */
	static int binarySearchRecursion(int arr[], int low, int high, int key) {
		// 如果low>high时,说明查找结束了,并未查找成功
		if (low > high)
			return -1;

		int mid = (low + high) / 2;
		if (key == arr[mid]) {
			return mid;
		} else if (key > arr[mid]) {
			low = mid + 1;
			return binarySearchRecursion(arr, low, high, key);
		} else {
			high = mid - 1;
			return binarySearchRecursion(arr, low, high, key);
		}
	}
}

其输出结果如下:

    如上部分代码,我定义了一个有序的数组,分别用两种方式实现了二分查找法。一种是循环遍历,另一种是递归调用方式。两种方式的目的都是一样的,查找目标 key 的值,返回在数组所在的位置 index。若返回 -1 ,则表示该数组中不存在此 key 值。代码都有了注释, 想必也很简单,无需过多解释了

    这几天开始捡起之前大一学的 C 语言来,虽然 C 和 Java 的语法差不多类似,比较有一点区别在那,刚好复习了这个二分搜索法,索性一起写了这个 C 语言版的例子。代码如下:

#include <stdio.h>
#define LEN 10

/** 冒泡排序 **/
int* sort(int *arr)
{
	int i,j;
	for(i=0; i<LEN; i++)
		for(j=0; j<LEN-i-1; j++)
			if(arr[j] > arr[j+1])
			{
				arr[j] ^= arr[j+1];
				arr[j+1] ^= arr[j];
				arr[j] ^= arr[j+1];
			}
	return arr;
}

/** 二分法查找 **/
int binarySearch(int arr[], int key)
{
	int low = 0;
	int high = LEN;

	while(low <= high)
	{
		/** 折半 **/
		int mid = (low + high) / 2;

		if(arr[mid] == key)
			return mid;
		else if(arr[mid] < key) /** key 在数组的右半截 **/
			low = mid + 1;
		else /** key 在数组的左半截 **/
			high = mid - 1;
	}
	return -1;
}

void printArray(int* arr)
{
	int i;
	for(i=0; i<LEN; i++)
		printf("%d\t", arr[i]);
	printf("\n");
}

void main()
{
	int arr[LEN] = {5,3,-7,16,5,6,-11,14,65,96};
	int index;

	/** 进行排序 **/
	int *new_arr = sort(arr);
	printArray(new_arr);

	/** -1 表示没有找到 **/
	index = binarySearch(new_arr, 65);
	printf("查找结果,index = %d\n", index);
}

其输出结果如下:

    关键性算法都是差不多类似的,只不过 C 语言的一些语法一时忘记了,脑子里还存有一点点印象,看一看基础语法还是能回忆起来的。想当初刚学 C 语言还是一脸懵逼的,很简单的算法都不会写,哈哈,现在要开始一点一点的捡起来了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值