查找算法-二分查找
Posted 夜忙猪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了查找算法-二分查找相关的知识,希望对你有一定的参考价值。
算法思想
二分查找也称折半查找(Binar Search),是一种在 有序
数组中查找某一特定元素的搜索算法。
搜索过程:
从数组的中间开始搜索,如果中间的元素正好是要查找的元素,则搜索过程结束。
如果该值大于中间元素,则在数组大于中间的那一半查找。
如果该值小于中间元素,则在数组小于中间的那一半查找。
而且再次查找的过程回到第一步,在该部分的中间元素开始。直到找到该元素或者数组为空。
其时间的复杂度为O(logn),空间复杂度o(1)
二分法很容易理解,但是细节是魔鬼,要写出一个正确的二分法不是一件容易的事情。
算法实现
首先我们了解一下其基本的实现,nums为有序数组,len为其长度,target为要寻找的目标
// 寻找一个数,如果存在返回其索引,否则返回-1。
int binarySearch(int* nums, int len, int target)
{
int left = 0;
int right = len - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
}
}
return -1;
}
当然我们也要学会拒绝造轮子嘛 O(∩_∩)O
包含这个头文件
void bsearch(const void *key, const void *base, size_t nitems, size_t size, int (compare)(const void , const void));
key -- 要查找的元素
base -- 指向要排序的数组的第一个元素的指针。
nitems -- 由 base 指向的数组中元素的个数。
size-- 数组中每个元素的大小,以字节为单位。
compare -- 用来比较两个元素的函数,即函数指针(回调函数)
#include <stdlib.h>
#define ARRAY_SIZE 5
int values[ARRAY_SIZE] = { 32, 32, 32, 32, 32 };
// compare函数
int cmpfunc(const void* a, const void* b)
{
return (*(int*)a - *(int*)b);
}
int main()
{
int* item;
int res;
int key = 32;
item = (int*)bsearch(&key, values, ARRAY_SIZE, sizeof(int), cmpfunc);
printf("lib index =%d\n", (int)(item - values) < 0 ? -1 : (int)(item - values)); //如果存在输出其索引,否则输出-1
return (0);
}
有时候我们可能想要最先出现的那个值,那么就需要我们把左值找出来
寻找左侧边界
// 寻找左侧边界
int leftBoundSearch(int* nums, int len, int target)
{
if (len == 0) {
return -1;
}
int left = 0;
int right = len;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
right = mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid;
}
}
if (left == len) {
return -1;
} else {
return nums[left] == target ? left : -1;
}
}
有时候我们可能想要最后出现的那个值,那么就需要我们把右值找出来
寻找右侧边界
// 寻找右侧边界
int rightBoundSearch(int* nums, int len, int target)
{
if (len == 0) {
return -1;
}
int left = 0;
int right = len;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
left = mid + 1;
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid;
}
}
if (left == 0) {
return -1;
} else {
return nums[left - 1] == target ? (left - 1) : -1;
}
}
那么我们放到一起来体验一下把
/* 使用 二分法 在数组中查找值 32 */
#include <stdio.h>
#include <stdlib.h>
#define ARRAY_SIZE 5
int values[ARRAY_SIZE] = { 32, 32, 32, 32, 32 };
// compare函数
int cmpfunc(const void* a, const void* b)
{
return (*(int*)a - *(int*)b);
}
// 寻找一个数,如果存在返回其索引,否则返回-1。
int binarySearch(int* nums, int len, int target)
{
int left = 0;
int right = len - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
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 leftBoundSearch(int* nums, int len, int target)
{
if (len == 0) {
return -1;
}
int left = 0;
int right = len;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
right = mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid;
}
}
if (left == len) {
return -1;
} else {
return nums[left] == target ? left : -1;
}
}
// 寻找右侧边界
int rightBoundSearch(int* nums, int len, int target)
{
if (len == 0) {
return -1;
}
int left = 0;
int right = len;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
left = mid + 1;
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid;
}
}
if (left == 0) {
return -1;
} else {
return nums[left - 1] == target ? (left - 1) : -1;
}
}
// main
int main()
{
int* item;
int res;
int key = 112;
item = (int*)bsearch(&key, values, ARRAY_SIZE, sizeof(int), cmpfunc);
printf("lib index =%d\n", (int)(item - values) < 0 ? -1 : (int)(item - values));
res = binarySearch(values, ARRAY_SIZE, key);
printf("std index =%d\n", res);
res = leftBoundSearch(values, ARRAY_SIZE, key);
printf("lft index =%d\n", res);
res = rightBoundSearch(values, ARRAY_SIZE, key);
printf("rgt index =%d\n", res);
return (0);
}
lib index =2
std index =2
lft index =0
rgt index =4
光说不练假把式
我们在leetcode上来找两个例题试试手。
34. 在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8 输出: [3,4] 示例 2:
输入: nums = [5,7,7,8,8,10], target = 6 输出: [-1,-1]
这个很明显的就是要用二分法嘛,虽然官方给了两个方法
1:线性扫描方法
2:二分查找
但是方法一线性扫描时间复杂度肯定不满足嘛,相信用上面的例子很容易改出答案来。
875. 爱吃香蕉的珂珂
我们再看一个不是特别明显的例子。
珂珂喜欢吃香蕉。这里有 N 堆香蕉,第 i 堆中有 piles[i] 根香蕉。警卫已经离开了,将在 H 小时后回来。
珂珂可以决定她吃香蕉的速度 K (单位:根/小时)。每个小时,她将会选择一堆香蕉,从中吃掉 K 根。如果这堆香蕉少于 K 根,她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉。
珂珂喜欢慢慢吃,但仍然想在警卫回来前吃掉所有的香蕉。
返回她可以在 H 小时内吃掉所有香蕉的最小速度 K(K 为整数)。
示例 1:
输入: piles = [3,6,7,11], H = 8 输出: 4 示例 2:
输入: piles = [30,11,23,4,20], H = 5 输出: 30 示例 3:
输入: piles = [30,11,23,4,20], H = 6 输出: 23
题解
int cmp(const void* a, const void* b) {
return *(int*)a - *(int*)b;
}
int minEatingSpeed(int* piles, int pilesSize, int H){
qsort(piles, pilesSize, sizeof(int), cmp);//先排成有序数组
/ * 二分法查找左值 */
int left = 1; ;
int right = piles[pilesSize-1];
int mid;
int time=0;
while (left < right) {
mid = (left + right) / 2;
time = 0;
for (int i=0; i<pilesSize; i++) {
time += (piles[i] + (mid - 1)) / mid;
}
if (time > H)
left = mid + 1;
else
right = mid;
}
return left;
}
以上是关于查找算法-二分查找的主要内容,如果未能解决你的问题,请参考以下文章