当前位置: 首页 > news >正文

c++ || 二分查找

文章目录

  • 二分查找
  • 无重复数字的升序数组的二分查找
  • 二维数组中查找目标整数
  • 寻找峰值
  • 旋转数组中的最小值
  • 搜索插入位置

二分查找

  • 二分查找需要的条件
    1.用于查找的内容逻辑上来讲是需要有序的
    2.查找的数量只能是一个,不是多个
  • 二分法中关注的问题
    1.while循环中left和right的关系,到底是left<=right还是left<right
    2.迭代过程中mid和right的关系,到底是right=mid-1,还是right= mid;

对于[left,right](左闭右闭区间)
  • 循环条件要使用while(left <= right),因为当(left == right)这种情况发生的时候,得到的结果是有意义的
  • if(nums[middle] > target) , right 要赋值为 middle - 1, 因为当前的 nums[middle] 一定不是 target ,需要把这个 middle 位置上面的数字丢弃,那么接下来需要查找范围就是[left, middle - 1]

对于[left,right)(左闭右开区间)
  • 循环条件使用while (left < right)
  • if (nums[middle] > target), right = middle,因为当前的 nums[middle] 是大于 target 的,不符合条件,不能取到 middle,并且区间的定义是 [left, right),刚好区间上的定义就取不到 right, 所以 right 赋值为 middle。

无重复数字的升序数组的二分查找

  • 问题:在一个元素升序的,无重复数字的整型数组nums和目标值target,目标值存在返回下标,不存在返回-1
  • 分析:数组为空 返回-1;用中点值作为一个标杆,将整个数组分为两个区间,目标值与中点值比较就能知道它会在哪个区间,这就是分治的思维
  • 步骤
    step 1:从数组首尾开始,每次取中点值。
    step 2:如果中间值等于目标即找到了,可返回下标,如果中点值大于目标,则中间值向右的所有数字都大于目标值,全部排除。如果中点值小于目标值,则说明中间数字向左的所有数字都小于目标值,全部排除。
    step 3:根据比较进入对应的区间,直到区间左右端相遇,意味着没有找到。
    int search(vector<int>& nums, int target) {
        // write code here
        if(nums.empty()) return -1; //为空 -1
        int left=0; //左闭
        int right = nums.size()-1; //右闭
        while(left <= right)   //<=
        {
             int mid = (left+right)/2;
            if(nums[mid]==target)
            {
                return mid;
            }
            if(target < nums[mid])
             right=mid-1; //mid-1
            else left=mid + 1;
        }
        return -1;
    }
      int search(vector<int>& nums, int target) {
        // write code here
        if(nums.empty()) return -1; //为空 -1
        int left=0;   //左闭
        int right = nums.size(); //右开
        while(left < right) //<
        {
             int mid = (left+right)/2;
            if(nums[mid]==target)
            {
                return mid;
            }
            if(target < nums[mid]) right=mid;//mid
            else left=mid + 1;
        }
        return -1;
    }

二维数组中查找目标整数

  • 问题: 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序,判断该二维数组中是否有该整数目标,存在返回TRUE,否则返回false;
  • 方法1: 逐行逐个查找
   bool Find(int target, vector<vector<int> > array) {
       for(auto i : array)//c++11语法
        {
            for(auto j : i)
            {
                if(j == target) return true;//找到目标值
            }
        }
        return false;//未找到
    
  • 方法2: 数组每行每列都是递增的,逐行使用二分搜索,查找target
    1
    bool binary_search(vector<int> & nums,int target)
    {
        int left = 0;
        int right = nums.size()-1;
        while(left<=right)
        {
            int mid = (left+right)/2;
            if(nums[mid]==target) return true;
            if(nums[mid]>target) right=mid-1;
            else left=mid+1;
        }
        return false;
    }
    bool Find(int target, vector<vector<int> > array) {
        if(array.empty()) return false;
        for(auto i:array)
        {
            if(binary_search(i,target)) return true;
        }
        return false;
    }

寻找峰值

  • 问题: 给定一个长度为n的数组nums,请你找到峰值并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个所在位置即可。
    1.峰值元素是指其值严格大于左右相邻值的元素。严格大于即不能有等于
    2.假设 nums[-1] = nums[n] = -\infty−∞
    3.对于所有有效的 i 都有 nums[i] != nums[i + 1]
    0
  • 分析: 每次找到一个基准元素,将数组分成两个区间,每次都想较高的一边走,就一定能找到一个峰值
  • 步骤: step 1:二分查找首先从数组首尾开始,每次取中间值,直到首尾相遇。
    step 2:如果中间值的元素大于它右边的元素,说明往右是向下,我们不一定会遇到波峰,但是那就往左收缩区间。
    step 3:如果中间值大于右边的元素,说明此时往右是向上,向上一定能有波峰,那我们往右收缩区间。
    step 4:最后区间收尾相遇的点一定就是波峰。
    int findPeakElement(vector<int>& nums) {
        // write code here
        int left = 0;
        int right = nums.size() - 1;
        //二分法
        while(left < right){
            int mid = (left + right) / 2;
            //右边是往下,不一定有坡峰
            if(nums[mid] > nums[mid + 1])
                right = mid;
            //右边是往上,一定能找到波峰
            else
                left = mid + 1;
        }
        //其中一个波峰
        return right;
    }

旋转数组中的最小值

  • **问题: ** 旋转数组的最小数字:把一个数组最开始的若干个元素搬到数组的末尾,称为数组的旋转
    {3,4,5,1,2} -> {1,2,3,4,5}
  • 思路:二分查找法 O(logn)
    注:旋转数组中包含两个递增排序的子数组,有阴影的是第二个子数组。
    (a)把P1指向数组的第一个数字,Pz指向数组的最后一个数字。
    由于P1和P2中间的数字5 [(first+last)/2] 大于P1指向的数字,中间的数字在第一个子数组中,下一步把P1指向中间的数字。
    (b)P1和P2中间的数字1,小于P2指向的数字,中间的数字在第二个子数组中。下一步把P2指向中间的数字。
    ©退出的条件:P1和P2指向两个相邻的数字(两个指针的距离是1),则P2指向的是数组中的最小数字。

特殊情况:当first、last、index指向的数字相同时,无法判断中间的数字是在第一个子数组还是在第二个子数组中,
也就没有办法移动指针缩小查找的范围,只能使用顺序查找


int MinSort(int* arr, int first, int last) //按顺序从前到后逐个遍历找出最小值
{
	int res = arr[first];
	for (int i = first + 1; i < last; ++i)
	{
		if (res > arr[i]) res = arr[i];
	}
	return res;
}
int Min(int* arr, int length)     //二分查找最小值
{
	if (arr == nullptr || length <= 0)return -1;
	int first = 0;
	int last = length - 1;
	int index = first;
	while (arr[first] >= arr[last])
	{
		if (last - first == 1)//循环退出的条件是 last和first之间距离为1
		{//此时first指向的是第一个数组的最后一个元素
			//last指向的是第二个数组的第一个元素
			index = last;
			break;
		}
		index = (first + last) / 2;
		if (arr[first] == arr[last] && arr[first] == arr[index])
		{ //下标为first、last、index指向的数字都相等 则只能顺序查找
			return MinSort(arr, first, last);
		}
		if (arr[index] >= arr[first])
		{
			first = index;
		}
		else if (arr[index] <= arr[last])
		{
			last = index;
		}
	}
	return arr[index];
}
int main()
{
	//int a[] = { 3,4,5,1,2 };
	int a[] = { 1,0,1,1,1 };
	int n = sizeof(a) / sizeof(a[0]);
	int min = Min(a, n);
	cout << "旋转数组中最小的数字是:" << min << endl;
	return 0;
}

搜索插入位置

  • 问题: 给定排序数组和目标值,在数组中找到目标值,返回其索引,目标值不存在与数组中,返回他将会被按顺序插入的位置
  • 分析: 四种情况
    1.目标值在数组所有元素之前 [0,-1]
    2.目标值等于数组中某一元素 return mid;
    3.目标值插入数组中的位置 [left,right] returnright+1
    4.目标值在数组所有元素之后 [left,right] return right+1
    int searchInsert(vector<int>& num, int target) {
        int n=num.size();
        int left=0;
        int right=n-1;
        while(left<=right)
        {
            int mid=(left+right)/2;
            if(num[mid]>target)
            {
                right=mid-1;
            }
            else if(num[mid]<target )
            {
                left=mid+1;
            }
            else{
                return mid;
            }
        }
        return right+1;
    }

相关文章:

  • AOP切面实现增删改防止重放攻击
  • oracle数据库 表中有数据,通过plsql 工具 连接 查询全表,却查不到数据
  • 第14章Linux实操篇-RPM与YUM
  • 小程序 input type=‘number‘ 不能输入小数点??
  • 高质量的子程序
  • 软件测试时Java面试题
  • 业务提前初始化执行
  • 区块链——Hyperledger Fabric2.2单点搭建网络
  • 从零开发一款图片编辑器Mitu-Dooring
  • 2022-08-30 第六小组 瞒春 学习笔记
  • 记录k8s-Calico网络插件报错问题
  • 北大肖臻老师《区块链技术与应用》系列课程学习笔记[25]以太坊-智能合约-5
  • 技术对接36
  • (附源码)springboot社区居家养老互助服务管理平台 毕业设计 062027
  • 【编程强训11】最近公共祖先+求最大连续bit数
  • CAP理论的例子讲解
  • CSS 三角实现
  • ES6--对象的扩展
  • exif信息对照
  • Java深入 - 深入理解Java集合
  • Js基础——数据类型之Null和Undefined
  • JS数组方法汇总
  • miaov-React 最佳入门
  • React的组件模式
  • SOFAMosn配置模型
  • -- 查询加强-- 使用如何where子句进行筛选,% _ like的使用
  • 基于阿里云移动推送的移动应用推送模式最佳实践
  • 普通函数和构造函数的区别
  • 强力优化Rancher k8s中国区的使用体验
  • 小程序button引导用户授权
  • 一、python与pycharm的安装
  • 进程与线程(三)——进程/线程间通信
  • ​ssh免密码登录设置及问题总结
  • ​低代码平台的核心价值与优势
  • ​油烟净化器电源安全,保障健康餐饮生活
  • #Ubuntu(修改root信息)
  • ( )的作用是将计算机中的信息传送给用户,计算机应用基础 吉大15春学期《计算机应用基础》在线作业二及答案...
  • (个人笔记质量不佳)SQL 左连接、右连接、内连接的区别
  • (南京观海微电子)——I3C协议介绍
  • (顺序)容器的好伴侣 --- 容器适配器
  • (译)2019年前端性能优化清单 — 下篇
  • (最优化理论与方法)第二章最优化所需基础知识-第三节:重要凸集举例
  • **登录+JWT+异常处理+拦截器+ThreadLocal-开发思想与代码实现**
  • .cn根服务器被攻击之后
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布
  • .NET Core 通过 Ef Core 操作 Mysql
  • .net on S60 ---- Net60 1.1发布 支持VS2008以及新的特性
  • .net开发时的诡异问题,button的onclick事件无效
  • .考试倒计时43天!来提分啦!
  • :not(:first-child)和:not(:last-child)的用法
  • @ModelAttribute 注解
  • @Transactional 竟也能解决分布式事务?
  • [ C++ ] STL priority_queue(优先级队列)使用及其底层模拟实现,容器适配器,deque(双端队列)原理了解
  • [ vulhub漏洞复现篇 ] JBOSS AS 5.x/6.x反序列化远程代码执行漏洞CVE-2017-12149