IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 二分查找算法【配合经典力扣题目讲解】(C++实现) -> 正文阅读

[数据结构与算法]二分查找算法【配合经典力扣题目讲解】(C++实现)

1 二分查找的基本原理

二分查找的使用情况:对于已经有序的数据来说,二分查找可以极大的提高查找效率,我们知道查找的本质就是排除,为什么二分查找可以右极高的查找效率呢?本质就是一次查找就可以排除一半无效的数据,这就是它高效的原因之一;
从时间复杂度度的角度来说:二分查找的查找效率达到 O(log n),这比遍历一遍数据的查找方式的时间复杂度O(n)好很多;特别是体现在数据量极大的情况下;


二分查找的原理也就是:要查找某一个数x,前提查找的数据是有序的,那么就可以找到该有序数据的中间位置middle,用x和middle比较,如果x比middle大,那么说明,x在middle的右边,那么就排除middle左边的所有数据(包括middle本身);如果x比middle小,那么说明,x在middle的左边,那么就可以排除middle右边的所有数据(包括middle本身);如果x == middle等于那么就说明找到了x;


2 二分查找算法的两种写法

大家对二分擦或者算法总有一种一看就会一写就废的感觉;
主要是对查找的边界控制得不够清晰;
对于边界的控制有左闭右闭的写法,也有左闭右开的写法;
两种写法都是有些微的差别,但是原理是一样的;


2.1 左闭右闭的二分查找写法

void binary_search(vector<int> nums,int x)
{
// 定义左闭右闭的区间里,[left, right]
	int left = 0;
    int right = nums.size() - 1; 
    // 当left==right,区间[left, right]依然有效,所以用 <=
     while (left <= right) {
     	int middle = left + ((right - left) >>1);
            if (nums[middle] > x) {
            // x 在左区间,所以[left, middle - 1]
                right = middle - 1; 
            } else if (nums[middle] < target) {
            // target 在右区间,所以[middle + 1, right]
                left = middle + 1; 
            } else { // nums[middle] == target
                 // 数组中找到目标值
            }
        }
        //退出循环表示:left>right此时
        // 未找到目标值
    }
}

我们重点关注循环的边界条件:while(left<=right),为什么要用<=,而不用<,其实就是因为我们用的是左闭右闭区间的写法,此时让left==right 时候,还在区间范围内,所以要继续查找;


2.2 左闭右开的区间的写法

void binary_search(vector<int> nums,int x)
{
// 定义左闭右闭的区间里,[left, right)
	int left = 0;
    int right = nums.size(); //注意这里的right是比数组最后一个值的下标还多1的下标
    // 当left==right,区间[left, right)无效,所以用 <
     while (left < right) {
     	int middle = left + ((right - left) >>1);
            if (nums[middle] > x) {
            // x 在左区间,所以[left, middle)
            //注意这里和闭区间的写法不一样
                right = middle; 
            } else if (nums[middle] < target) {
            // target 在右区间,所以[middle + 1, right)
                left = middle + 1; 
            } else { // nums[middle] == target
                 // 数组中找到目标值
            }
        }
        //退出循环表示:left>right此时
        // 未找到目标值
    }
}

而我们的左闭右开区间的写法:相比于左闭右闭区间的写法,在while的判断条件有所不同,还有当 x < 中间值时候,right = middle,而不是right = middle-1;因为开区间,说明middle本身就不是在数据方位内的值了,所以不用-1;


3 四道经典力扣的二分算法的题目

3.1 有效的完全平方数

在这里插入图片描述


思路:只要我们在[1,num]的范围内,试数,找到一个数x ,只要满足x*x = num,那么就可以了;
此时,由于【1,num】的数都是升序,即有序,我们可以使用二分查找算法,这样查找效率更高;


代码:

class Solution {
public:
//只要在[1,num]的区间不断试数就可以
    bool isPerfectSquare(int num) {
        int left = 1;
        int right  =num;

        while(left <=right)
        {
            long long middle = left +((right - left)>>1);

            long long val = middle*middle;
            if(val == num)
                return true;
            else if(val < num)
                left = middle+1;
            else
                right = middle -1;
            
        }
        return false;
    }
};

3.2 x 的平方根

在这里插入图片描述


思路:
依旧是,在[0,x]中找到一个数,k,只要满足kk <= x,
那么就可以说明当k
k = x,k就是x的算术平方根;
当k*k<x,那么说明k可能是x的算术平方根(题目的要求小数点的数会被舍掉,所以说,k * k< x是有可能成为x的算术平方根的值);
当k > x那么直接pass掉;
由于这个又是在[0,x]升序查找,所以说:我们还是用二分查找算法解决;


代码

class Solution {
public:
    int mySqrt(int x) {

        //在[0,x]中不断试数,算出结果
        //找到一个数k,满足k*k <= x即可
        int left = 0;
        int right = x; 
        int ret = -1; //保存结果的值
        while(left <=right)
        {
            int middle = left + ((right-left)>>1);
            if((long long)middle*middle<=x)
            {
                ret = middle;//由于<的情况也有可能找到ret,所以就保存该middle值
                left = middle+1;
            }
            else
            {
                right = middle -1;
            }
         }
         return ret;
    }
};

3.3 搜索插入位置

在这里插入图片描述


有序的数据,毫无疑问,直接二分算法:

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
		//注意我这里是左闭右开的区间
        int left = 0;
        int right = nums.size();

        while(left < right)
        {
            int middle = left +((right - left)>>1);

            if(target<nums[middle])
            {
                right = middle;
            }
            else if ( target > nums[middle])
            {
                left = middle +1;
            }
            else
            {
                return middle;
            }
        }
        //1.头插,比[1,2,3],插入0,
        //2.尾插,比如[1,2,3]插入4,
        //3.插中间,比如[1,2,4]插入3
        return right;
    }
};

3.4 在排序数组中查找元素的第一个和最后一个位置

在这里插入图片描述


二分算法不多说:
主要是我们要如何找到第一个数位置,和最后一个数位置:
首先我们得清楚,当我们找第一个数位置时候,需要把一种情况:即二分查找时候,target == nums[middle]时候得条件,也需要让 right = middle -1;的操作,这样才可以把最后一个数的位置给避开;
反之亦然,也就是说,擦或者最后一个数时候,也是这样操作;


代码:

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        //先找到第一个值的最左下标
        int left = LeftIndex(nums,target);
          //在找最后一个值的最右下标
        int right = RightIndex(nums,target);
        if(right < left )
            return vector<int>{-1,-1};
        //right >=left,
        //大于情况就是正常的有多个相同的目标值,
        //等于的情况就是数组只有一个数
        return vector<int>{left,right};


    }
    int LeftIndex (vector<int>&nums ,int target)
    {
        int left = 0;
        int right = nums.size() - 1;

        while(left <= right)
        {
            int middle = left + ((right - left) >> 1);

            if(target <= nums[middle])
                right = middle - 1;
            else
                left = middle + 1;
        }
        //退出循环后,下标区间为【right,left】,而left就是要找的值
        //或者退出循环后,left不是要找的值如【1,7,8,9,10】找 8,
        // 退出循环后下标位置在left = 2;  right = 1
        return left;
    }
    int RightIndex (vector<int>&nums,int target)
    {
          int left = 0;
        int right = nums.size() - 1;
         while(left <= right)
        {
            int middle = left +( (right - left) >> 1);

            if(target >=nums[middle]) //大于等于的条件就继续移动左下标,直到退出循环
                left = middle + 1;
            else
                right = middle - 1;
        }
        //退出循环后,下标区间为【right,left】,而right就是要找的值
        return right;
    }
};


  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-03-10 22:50:47  更:2022-03-10 22:52:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 13:50:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码