数据结构与算法 二分查找
一、简述
记--二分查找的C语言简单实现。
例子打包:外链:https://wwi.lanzouq.com/b0ca7a38b 密码:ckk5
二、二分查找
释义 | 假设有一个有序表A,元素个数为n,要查找元素为K 1)将A表分成左右两个子表A1,A2,让中间元素A[n/2]跟K比较, 2)如果A[n/2] == K说明已经找到K, 3)如果A[n/2] > K说明K在左子表A1,继续将表A1分为两个子表进行查找(即将A1当做A表继续按步骤1开始) 4)如果A[n/2] < K说明K在右子表A2,继续将表A2分为两个子表进行查找(即将A2当做A表继续按步骤1开始) 二分到最后只剩下一个元素,如果A[n/2] != K 说明找不到元素。 |
时间复杂度 | O(log2 n) |
空间复杂度 | O(1) |
优点 | 查找速度较快,不需要全部遍历元素 |
缺点 | 待查表需要有序 |
三、二分查找实现
3.1 实现框架
/******************************************
* 函数: BinarySearch
* 功能: 二分查找
* 参数: int nums[]:待查找数据数组
* int nums_size:数据数组元素个数
* int target:查找目标
* 输入:
* 输出:
* 返回: int 成功返回:;其它返回:
* 说明: @Liang 2022.03.05 周六
******************************************/
int BinarySearch(int nums[], int nums_size, int target) {
int left = 0;
int right = ...;
int mid = 0;
while (...) {
mid = (left + right) / 2; //防止(left+right)溢出写法:mid = left + (right - left) / 2;
if (nums[mid] == target) {
//TODO 找到目标
} else if (nums[mid] < target) {
left = ...; //目标在右半部分,进行收缩左边界
} else if (nums[mid] > target) {
right = ...; //目标在右半部分,进行收缩右边界
}
}
return ...;
}
3.2 测试代码
#include <stdio.h>
/******************************************
* 函数: BinarySearch
* 功能: 二分查找
* 参数: int nums[]:待查找数据数组
* int nums_size:数据数组元素个数
* int target:查找目标
* 输入:
* 输出:
* 返回: int 成功返回:;其它返回:
* 说明: @Liang 2022.03.05 周六
******************************************/
int BinarySearch(int nums[], int nums_size, int target) {
int left = 0;
int right = nums_size - 1;
int mid = 0;
while (left <= right) {
mid = left + (right - left) / 2; //防止(left+right)溢出写法
if (nums[mid] == target) {
return mid;//找到目标
} else if (nums[mid] < target) {
left = mid + 1; //目标在右半部分,进行收缩左边界
} else if (nums[mid] > target) {
right = mid - 1; //目标在右半部分,进行收缩右边界
}
}
return -1;
}
/* 获取数据元素个数 */
#define GET_ARR_NUM(arr) (sizeof(arr)/sizeof(arr[0]))
int main(int argc, char *argv[])
{
int ret;
int target;
int nums[] = {2, 15, 26, 37, 41, 68, 93};
target = 93;
ret = BinarySearch(nums, GET_ARR_NUM(nums), target);
if (0 <= ret) {
printf("target:%d, nums[%d]=%d\r\n", target, ret, nums[ret]);
} else {
printf("not found target:%d, ret:%d\r\n", target,ret);
}
target = 100;
ret = BinarySearch(nums, GET_ARR_NUM(nums), target);
if (0 <= ret) {
printf("target:%d, nums[%d]=%d\r\n", target,ret, nums[ret]);
}
else {
printf("not found target:%d, ret:%d\r\n", target, ret);
}
getchar();
return 0;
}
运行结果
四、找左侧边界、右侧边界
待查找表中存在相同的元素,找到最左边的元素,比如: {1, 2, 3, 3, 3, 3, 3, 5, 7}中查找第一个3 或最后一个3。
测试代码
#include <stdio.h>
/******************************************
* 函数: BinarySearch
* 功能: 二分查找
* 参数: int nums[]:待查找数据数组
* int nums_size:数据数组元素个数
* int target:查找目标
* 输入:
* 输出:
* 返回: int 成功返回:;其它返回:
* 说明: @Liang 2022.03.05 周六
******************************************/
int BinarySearch(int nums[], int nums_size, int target) {
int left = 0;
int right = nums_size - 1;
int mid = 0;
while (left <= right) {
mid = left + (right - left) / 2; //防止(left+right)溢出写法
if (nums[mid] == target) {
return mid;//找到目标
} else if (nums[mid] < target) {
left = mid + 1; //目标在右半部分,进行收缩左边界
} else if (nums[mid] > target) {
right = mid - 1; //目标在右半部分,进行收缩右边界
}
}
return -1;
}
//找左边界
int BinarySearchLeft(int nums[], int nums_size, int target) {
int left = 0;
int right = nums_size; //因为[left, right)右开区间right取不到,所以不减一,否则漏了right
int mid = 0;
while (left < right) { //[left, right) 存在最后一个元素未查找的情况,故在return之前需要检查
mid = left + (right - left) / 2; //防止(left+right)溢出写法
if (nums[mid] == target) {
right = mid;//找到目标,但可能不是最左的,进行收缩右边界,逐步向左逼近
} else if (nums[mid] < target) {
left = mid + 1; //目标在右半部分,进行收缩左边界
} else if (nums[mid] > target) {
right = mid; //目标在右半部分,进行收缩右边界, 因为[left, right)右开区间right取不到,所以不减一,否则漏了right
}
}
if (left == nums_size) {//因为[left, right)
return -1;//遍历结束都找不到target
}
return (nums[left] == target) ? left : -1;
}
//找右边界
int BinarySearchRight(int nums[], int nums_size, int target) {
int left = 0;
int right = nums_size;//因为[left, right)右开区间right取不到,所以不减一,否则漏了right
int mid = 0;
while (left < right) { //存在最后一个元素未查找的情况,故在return之前需要检查
mid = left + (right - left) / 2; //防止(left+right)溢出写法
if (nums[mid] == target) {
left = mid + 1;//找到目标,但可能不是最右的,进行收缩左边界,逐步向右逼近, 由于mid是取整的,所以+1才能保证收缩。
} else if (nums[mid] < target) {
left = mid + 1; //目标在右半部分,进行收缩左边界
} else if (nums[mid] > target) {
right = mid; //目标在右半部分,进行收缩右边界
}
}
if (0 < left) {
--left;//因为在nums[mid] == target时为了收缩左边界进行了left = mid + 1;
}
return (nums[left] == target) ? left : -1;
}
/* 获取数据元素个数 */
#define GET_ARR_NUM(arr) (sizeof(arr)/sizeof(arr[0]))
int main(int argc, char *argv[])
{
int ret;
int target;
int nums[] = { 1, 2, 3, 3, 3, 3, 3, 5, 7 };
target = 3;
ret = BinarySearch(nums, GET_ARR_NUM(nums), target);
if (0 <= ret) {
printf("BinarySearch target:%d, nums[%d]=%d\r\n", target, ret, nums[ret]);
} else {
printf("BinarySearch not found target:%d, ret:%d\r\n", target, ret);
}
ret = BinarySearchLeft(nums, GET_ARR_NUM(nums), target);
if (0 <= ret) {
printf("BinarySearchLeft target:%d, nums[%d]=%d\r\n", target, ret, nums[ret]);
} else {
printf("BinarySearchLeft not found target:%d, ret:%d\r\n", target, ret);
}
ret = BinarySearchRight(nums, GET_ARR_NUM(nums), target);
if (0 <= ret) {
printf("BinarySearchRight target:%d, nums[%d]=%d\r\n", target, ret, nums[ret]);
} else {
printf("BinarySearchRight not found target:%d, ret:%d\r\n", target, ret);
}
getchar();
return 0;
}
运行结果