二分查找
1.二分查找
二分法查找,也称为折半法,是一种在有序数组中查找特定元素的搜索算法。他的时间复杂度为O(logN)
2.在一组没有重复值的升序数组中查找所求数
//代码及其运行截图
int Binarysearch(int* str,int strlen, int target){
int left = 0;
int right = strlen - 1;
while (left <= right){//如果只是left<right,则会少一次比较
int mid = left+((right-left)>>1);//(left+right)/2容易溢出
if (*(str + mid) < target){//要找的数在中点的右边
left = mid+1;
}
else if (*(str + mid) > target){//要找的数在中点的左边
right = mid-1;
}
else
{
return mid;
break;
}
}
return -1;
}
int main(){
int str[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 23, 45 };
int size = sizeof(str) / sizeof(str[0]);
int x = Binarysearch(str, size, 11);
cout << x << endl;
}
这段代码,在没有重复值的升序数组中,运行结果会准确输出,如下运行结果:
!
但是,如果升序数组arr[]={1,2,3,4,4,4,5,5,6,7,8}则输出的结果不会准确,此时我们需要的是出现所找的数的第一个下标或者最后一个下标。
3.在一组有重复值的升序数组中查找所求数
首先我们可以想到,通过二分法拿到一个值,然后再通过这个值向左或向右进行遍历查询,但是此时的时间复杂度为O(logN),因此,我们可以继续通过二分查找法,查找最左或者最右目标值。
查找最左目标值:
//在一组有重复值的升序数组里
int search(int* str,int strlen, int target){
int left = 0;
int right = strlen - 1;
while (left <= right){
int mid = left+((right-left)>>1);
if (*(str + mid) >= target) //此时,假设当mid下标所指的值等于我们所查找的值,先不进行返回,此时最高位指向mid且左移一位
right = mid - 1;
else
left = mid + 1;
if (*(str + right)<target) //此时进行上一个条件中right下标所存的数据与我们target值的比较,如果此时此时的值是小于target,那么mid下标所存的数据则为所求数的最左值,进行输出,否则继续循环
return mid;
else
continue;
}
return -1;
}
此时我们所求的最左值为3
查找最右目标值:
//在一组有重复值的升序数组里
//查找最右目标值与查找最左目标值的思路相似
int search(int* str,int strlen, int target){
int left = 0;
int right = strlen - 1;
while (left <= right){
int mid = left+((right-left)>>1);
if (*(str + mid) <= target)
left = mid + 1;
else
right=mid-1;
if (*(str + right)>target)
return mid;
else
continue;
}
return -1;
}
最终代码及结果:
#include<iostream>
using namespace std;
//二分查找,有重复值的二分查找
//在一组有重复值的升序数组里
int right_search(int* str, int strlen, int target){
int left = 0;
int right = strlen - 1;
while (left <= right){
int mid = left + ((right - left) >> 1);
if (*(str + mid) <= target)
left = mid + 1;
else
right = mid - 1;
if (*(str + right) == target )
return right;
else
continue;
}
if (left > right)
return -1;
}
//在一组有重复值的升序数组里
int left_search(int* str, int strlen, int target){
int left = 0;
int right = strlen - 1;
while (left <= right){
int mid = left + ((right - left) >> 1);
if (*(str + mid) >= target) //此时,假设当mid下标所指的值等于我们所查找的值,先不进行返回,此时最高位指向mid且左移一位
right = mid - 1;
else
left = mid + 1;
if (*(str + left) == target) //此时进行上一个条件中right下标所存的数据与我们target值的比较,如果此时此时的值是小于target,那么mid下标所存的数据则为所求数的最左值,进行输出,否则继续循环
return left;
else
continue;
}
if (left > right)
return -1;
}
int main(){
int str[] = { 1, 2, 3, 4, 4, 4, 4, 4, 5, 5, 6, 7, 8, 9, 10 };
int size = sizeof(str) / sizeof(str[0]);
cout << "找打5的位置:" << endl;
int right_x5 = right_search(str, size, 5);
int left_x5 = left_search(str, size, 5);
if (left_x5 == right_x5)
cout << "可知所求数在此数组里只有一个,且下标为" << right_x5 << endl;
else{
cout << "他的最左下标值为:" << left_x5 << endl;
cout << "他的最右下标值为:" << right_x5 << endl;
}
cout << "找打2的位置:" << endl;
int right_x2 = right_search(str, size, 2);
int left_x2 = left_search(str, size, 2);
if (left_x2 == right_x2)
cout << "可知所求数在此数组里只有一个,且下标为" << right_x2 << endl;
else{
cout << "他的最左下标值为:" << left_x2 << endl;
cout << "他的最右下标值为:" << right_x2 << endl;
}
}
运行截图:
以上都是我个人所思所得,如果有错误,欢迎在评论区指正。