二分查找

二分查找

思路:

  • 找到最后一个小于等于IP的元素
  • 找到第一个大于等于IP的元素

前提条件:

  • 数据有序
  • 随机访问

实现:

  • 递归实现
  • 非递归(循环实现)

注意事项:

  1. 循环退出条件
  2. mid 的取值(重点防止越界)
  3. left 和 right的更新
#include<stdio.h>

#define SIZE(a) (sizeof(a) / sizeof(a[0]))

int bsearchByrecursion(int arr[], int n, int key);
int bsearchByloop(int arr[], int n, int key);
int searchFirstkey(int arr[], int n, int key);
int searchLastkey(int arr[], int n, int key);
int bsearchEqualorgreated(int arr[], int n, int key);
int bsearchEqualorless(int arr[], int n, int key);

int main(void)
{
	int arr[] = { 0,2,2,2,2,2,20,30,31,32,34,40,100 };
	int key = -50;
	int index = bsearchEqualorless(arr, SIZE(arr), 22);
	printf("index = %d\n", index);

	return 0;
}

// 递归实现二分查找
int bsearch(int arr[], int left, int right, int key)
{
	// 边界条件:区间为 0 
	if (left > right)
	{
		return -1;
	}
	// 递归公式
	/*
	   存在溢出风险。
	   原因:eft可能不断增大,如果到极限状态,
	   也就是left达到了right-1的地步的时候刚好数组的长度又很大,
	   那么就可能导致left + right的溢出出现负数
	   int mid = (left + right) / 2;
	*/
	// int mid = left + (right - left) / 2;			// 正确写法
	int mid = left + (right - left >> 1);
	if (key < arr[mid])
	{
		return bsearch(arr, left, mid - 1, key);
	}
	if (key > arr[mid])
	{
		return bsearch(arr, mid + 1, right, key);
	}
	return mid;			// key == arr[mid];
}

int bsearchByrecursion(int arr[], int n, int key)
{
	// 委托
	return bsearch(arr, 0, n - 1, key);
}

// 循环实现二分查找
int bsearchByloop(int arr[], int n, int key)
{
	int left = 0;
	int right = n - 1;

	// left = right说明还有一个元素,该元素还要和key进行比较
	while (left <= right)
	{
		int mid = left + (right - left >> 1);
		// 不能写成left = mid, right = mid;有可能出现死循环。
		if (key > arr[mid])
		{
			left = mid + 1;
		}
		else if (key < arr[mid])
		{
			right = mid - 1;
		}
		else
		{
			return mid;
		}
	}

	return -1;
}

// 寻找第一个与 key 相等的元素
int searchFirstkey(int arr[], int n, int key)
{
	int left = 0, right = n - 1, mid;
	while (left <= right)
	{
		mid = left + (right - left >> 1);
		if (key > arr[mid])
		{
			left = mid + 1;
		}
		else if (key < arr[mid])
		{
			right = mid - 1;
		}
		else
		{
			if (mid == 0 || arr[mid - 1] < key)		// 短路原则,不可能越界
			{
				return mid;
			}
			// 如果不是第一个与 key 相等的
			right = mid - 1;
		}
	}

	return -1;
}

// 查找最后一个与 key 相等的元素
int searchLastkey(int arr[], int n, int key)
{
	int left = 0, right = n - 1, mid;
	while (left <= right)
	{
		mid = left + (right - left >> 1);
		if (key > arr[mid])
		{
			left = mid + 1;
		}
		else if (key < arr[mid])
		{
			right = mid - 1;
		}
		else
		{
			if ((mid == n - 1) || (arr[mid + 1] > key))
			{
				return mid;
			}
			// 不是最后一个值等于 key 的元素
			left = mid + 1;
		}
	}

	return -1;		// 数组中不存在该元素
}

// 查找第一个大于等于 key 的元素
int bsearchEqualorgreated(int arr[], int n, int key)
{
	int left = 0, right = n - 1, mid;
	while (left <= right)
	{
		mid = left + (right - left >> 1);
		if (key > arr[mid])
		{
			left = mid + 1;
		}
		else
		{
			// 找到一个大于等于 key 的元素
			if (mid == left || arr[mid - 1] < key)
			{
				return mid;
			}
			// 如果不是第一个大于等于 key 的,则继续在左边找
			right = mid - 1;
		}
	}
        return -1;
}

// 查找最后一个小于等于 key 的元素
int bsearchEqualorless(int arr[], int n, int key)
{
	int left = 0, right = n - 1, mid;
	while (left <= right)
	{
		mid = left + (right - left >> 1);
		if (key < arr[mid])
		{
			right = mid - 1;
		}
		else
		{
			if (mid == right || arr[mid + 1] > key)
			{
				return mid;
			}
			left = mid + 1;
		}
	}

        return-1
}


posted @ 2023-02-13 21:07  MyXjl  阅读(16)  评论(0编辑  收藏  举报