1.二分查找
什么时候需要二分查找?
有序数列+不重复元素+只查找一个
这三个种情况一起才能去使用二分查找。
什么是二分查找?
不断去将一个数组分成两份,不断去比较这份数组的中位数和要查找数是否相同,直到相同为止就是二分查找。
举一个现实一点的例子,你借了一堆书,里面有一本书没登记上,你现在想把它找出来,你把书平均分成两份,你把其中一份拿去过安检,你发现报警器响了,那你就把这一份书拿出来再来分成两份,再拿一份去过安检,如果响了就去拆这一份不响就去拆另外一份,不断去重复这个操作,直到只剩一本,这一本就是我们需要的。
1.1二分查找的思路
那给你一串有序无重复数据,你要怎么去找这一串数据中你所需要的数呢?
这里有一串数组arr={1,2,3,4,5,6,7,8,...n}.在数组内部对应的下标为0-(n-1).你的目标为target.利用二分查找去找出这个数。
第一步,你需要知道这个数组的最大数,最小数和中位数。由于数组是有序的。所以最大的数为arr[n-1],最小的数为arr[0],所以初始的下标right=arr.size()-1,left=0
而这串数组的中位数下标为:middle=left+(right-lefte)/2,这个算法我也是参考了网上别人的想法,如果直接用middle=(right+lefte)/2会造成有符号数的溢出,但我个人人为如果将数据直接定义为unsigned格式不知道是不是就存在这个问题了。有的人在这里主要是会去纠结奇数和偶数中位数的区别,其实很简单:
比如数组有四个数,那其中位数的下标根据公式计算为2,对应的其实是数组的第三个数,如果第三个数不是目标,那你之后去对比的两堆数其实是第1,2个数一堆,然后第4个数一堆。
而如果数组有5个数,那中位数计算后的下标根据公式计算也为2,对应的也是数组的第三个数,如果第三个数不是目标,那你之后去对比的两堆数其实是第1,2个数一堆,然后第4,5个数一堆.
总数为奇和欧数只是会影响你对比的堆中个数,你不管怎么分堆,所需的数肯定不会被遗漏的,只是迭代次数不一样了。
接下来,你需要去对照找到中位数和target的大小关系,才知道这个target是属于哪一堆的。
初始的情况,我们需要去对比所有数据的中位数是否为目标target。如果不是就需要去判断中位数比target大还是小了。以1,2,3,4,5为例,查找2,下表为初始位置
数 | 1 | 2 | 3 | 4 | 5 |
下标 | 0 | 1 | 2 | 3 | 4 |
左右位置 | left | mid | right |
由于mid指向的数大于target,所以第二次应该去查找mid左边的部分,如下图所示:
数 | 1 | 2 |
下标 | 0 | 1 |
左右位置 | left/mid | right |
而此时mid指向的数小于target,所以第三次应该去查找mid右边的数,如下图所示:
数 | 2 |
下标 | 1 |
左右位置 | left/right/mid |
此时mid指向的数等于target,所以应该返回此时的下标mid。
1.2 二分查找的代码
明白了思路现在就可以开始编程了。以C++代码为例。力扣都是用的核心代码模式,但是实际不少大厂笔试的编程模式都是ACM模式,所以下面用两种模式来编写代码:
ACM模式:
#include<iostream>
#include<vector>
using namespace std;
int getarget(vector<int>& nums, int val)
{
int left = 0;
int right = nums.size()-1;//左右游标
while (left <= right)//闭合数组的二分查找
{
int middle = left + (right - left) / 2;//替换中位数
if (nums[middle] > val)
{
right = middle - 1;
}
else if (nums[middle] < val)
{
left = middle + 1;
}
else
{
return middle;
}
}
return -1;
}
int main()
{
int n;
vector<int>arr;
int temp ;
cin >> temp;
while (cin >> n)
{
arr.push_back(n);
if (cin.get() == '\n') break;
}//输入不知道个数的数组;
cout<<getarget(arr, temp)<<endl;
return 0;
}
输入格式为:第一行输入查找目标
第二行输入需要查找的数组用空格隔开
核心代码模式:
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size()-1;//左右游标
while (left <= right)//闭合数组的二分查找
{
int middle = left + (right - left) / 2;//替换中位数
cout << middle << endl;
if (nums[middle] > val)
{
right = middle - 1;
}
else if (nums[middle] < val)
{
left = middle + 1;
}
else
{
return middle;
}
}
return -1;
};