代码随想录 LeetCode数组篇 二分查找

news2024/11/15 7:58:49

文章目录

  • (简单)35. 搜索插入位置
  • (*中等)34. 在排序数组中查找元素的第一个和最后一个位置
  • (简单,常见面试题)69. x的平方根
  • (简单) 367. 有效的完全平方数


# (简单)704. 二分查找

在这里插入图片描述

题目链接

代码随想录 - 二分查找思路

二分查找,思路很简单,但是在while循环left和right的比较是写<=还是<,还有right=mid还是right=mid-1容易混淆

需要想清楚对区间的定义,是[left,right],还是[left,right)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

(版本一,左闭右闭版本)

class Solution {
    public int search(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int mid;
        while (left <= right) {
            mid = left + ((right-left)/2);//防止溢出
            if (nums[mid] == target) {
                return mid;
            }
            if (target > nums[mid]) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return -1;
    }
}

(版本二,左闭右开)

class Solution {
    public int search(int[] nums, int target) {

        //避免当target小于nums[0] 或者大于 nums[nums.length-1]时 多次循环
        if (target < nums[0] || target > nums[nums.length - 1]) {
            return -1;
        }

        int left = 0;
        int right = nums.length;
        int mid;
        while (left < right) {
            mid = left + ((right - left) >> 1);
            if (nums[mid] == target) {
                return mid;
            }
            if (target > nums[mid]) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        return -1;
    }
}

(简单)35. 搜索插入位置

题目链接

在这里插入图片描述

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int mid;
        while (left <= right) {
            mid = left + ((right - left) >> 1);
            if (nums[mid] == target) {
                return mid;
            }
            if (target > nums[mid]) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return left;//right+1
    }
}

(*中等)34. 在排序数组中查找元素的第一个和最后一个位置

在这里插入图片描述
题目链接

我的思路:按照传统二分查找的方式,找到等于target的数组的下标,然后向两边扩展,如果没找到直接返回[-1,-1]

在这里插入图片描述

class Solution {

    public int[] searchRange(int[] nums, int target) {

        //如果数组长度是0,则不用判断
        if (nums == null || nums.length == 0) {
            return new int[]{-1, -1};
        }

        //如果target的值小于第一个元素或者大于最后一个元素,也不用判断
        if ((target < nums[0]) || (target > nums[nums.length - 1])) {
            return new int[]{-1, -1};
        }

        int res = search(nums, target);
        if (res == -1) {
            return new int[]{-1, -1};
        }
        int left = res;
        int right = res;
        while ((left >= 0) && (nums[left] == nums[res])) {
            left--;
        }
        while ((right < nums.length) && (nums[right] == nums[res])) {
            right++;
        }
        return new int[]{left + 1, right - 1};
    }

    public int search(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int mid;
        while (left <= right) {
            mid = left + ((right - left) >> 1);
            if (target == nums[mid]) {
                return mid;
            }
            if (target > nums[mid]) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return -1;
    }
}

看了官方的解题思路,是找出数组中【第一个等于target的位置(leftIdx)】和【第一个大于target的位置减一(rightIdx)】

二分查找中

  • 寻找leftIdx即为在数组中寻找第一个大于等于 target的下标
  • 寻找rightIdx即为在数组中寻找第一个大于 target的下标,然后减一

二者的判断条件不同,为了代码的复用,定义一个函数,其中包含布尔类型的lower参数,该参数为true时,则查找第一个大于等于target的下标,该参数为false时,则查找第一个大于target的下标

最后,因为target可能不在数组中,因此需要重新校验两个下标leftIdx和rightIdx,看是否符合条件,如果符合,就返回[leftIdx,rightIdx],不符合就返回[-1,-1]
在这里插入图片描述

class Solution {

    public int[] searchRange(int[] nums, int target) {

        //如果数组长度是0,则不用判断
        if (nums == null || nums.length == 0) {
            return new int[]{-1, -1};
        }

        //如果target的值小于第一个元素或者大于最后一个元素,也不用判断
        if ((target < nums[0]) || (target > nums[nums.length - 1])) {
            return new int[]{-1, -1};
        }

        int leftIdx = search(nums, target, true);
        int rightIdx = search(nums, target, false) - 1;

        if (leftIdx >= 0 && rightIdx < nums.length && leftIdx <= rightIdx && nums[leftIdx] == target && nums[rightIdx] == target) {
            return new int[]{leftIdx, rightIdx};
        }
        return new int[]{-1, -1};

    }

    public int search(int[] nums, int target, boolean lower) {
        int left = 0;
        int right = nums.length - 1;
        int mid;
        int ans = nums.length;
        while (left <= right) {
            mid = left + ((right - left) >> 1);

            //说明当前的mid所在的值比target大,需要缩小范围
            //找leftIdx,如果target小于nums[mid],缩小范围
            //如果target==nums[mid],也要缩小范围,但是会记录mid
            //找rightIdx,只要target<nums[mid],缩小范围
            if (target < nums[mid] || (lower && target == nums[mid])) {
                right = mid - 1;
                ans = mid;
            } else {
                //找rightIdx,只要target < nums[mid] 不成立
                //也就是target=nums[mid]或者target>nums[mid] left都会变化
                left = mid + 1;
            }
        }
        return ans;
    }
}

(简单,常见面试题)69. x的平方根

在这里插入图片描述
我的思路,将x的值赋给tmp,将tmp不断除以2

记录下面两个数,在这两个数之间使用二分查找:

  1. 使得tmp^2小于x的最大的tmp值
  2. 使得tmp^2大于x的最小的tmp值
class Solution {
    public int mySqrt(int x) {
        if (x == 0 || x == 1) {
            return x;
        }

        int pre = x;
        int tmp = x / 2;
        while (tmp > x / tmp) { //原来是 tmp * tmp > x
            pre = tmp;
            tmp /= 2;
        }

        if (tmp == x / tmp) { //原来是 tmp * tmp == x 
            return tmp;
        }

        //pre和tmp之间找到答案,缩小范围
        return search(x, tmp, pre);
    }

    public int search(int x, int tmp, int pre) {
        int left = tmp;
        int right = pre;
        int mid;
        int ans = tmp;
        while (left <= right) {
            mid = left + ((right - left) >> 1);
            if (mid == x / mid) { //原来是 mid * mid == x
                return mid;
            }
            if (mid > x / mid) { // 原来是mid * mid > x
                right = mid - 1;
            } else {
                ans = mid;
                left = mid + 1;
            }
        }
        return ans;
    }
}

官网解答,笔记:

在不使用 x \sqrt{x} x 函数的情况下,得到x的平方根的整数部分。一般的思路有以下几种:

  • 通过其它的数学函数代替平方根函数得到精确结果,取整数部分作为答案
  • 通过数学方法得到近似结果,直接作为答案

方法一 袖珍计算器算法

【袖珍计算器算法】是一种用指数函数exp和对数函数ln代替平方根函数的方法。通过有限的、可以使用的数学函数,得到想要的结果。

在这里插入图片描述
当x=2147395600时, e 1 2 ln ⁡ x e^{\frac{1}{2}\ln x} e21lnx​的计算结果与正确值46340相差 1 0 − 11 10^{-11} 1011,这样在对结果取整数部分时,会得到46339这个错误结果。

因此,在得到结果的整数部分ans后,应该找出ans与ans+1中哪一个是真正的答案。

class Solution {
    public int mySqrt(int x) {
        if (x == 0 || x == 1) {
            return x;
        }
        int ans = (int) Math.exp(0.5 * Math.log(x));
        return (long) (ans + 1) * (ans + 1) > x ? ans : ans + 1;
    }
}

方法二 二分查找

由于x平方根的整数部分ans是满足 k 2 ≤ x k^2 \leq x k2x的最大k值,因此可以对k进行二分查找,从而得到答案

二分查找的下界是1,上界可以粗略的=地设定为x。在二分查找中的每一步中,我们只需要比较中间元素mid的平方与x的大小关系,并通过比较的结果调整上下界的范围。由于我们所有的运算都是整数运算,所以,不会存在误差,因此在得到最终的答案ans后,也就不需要再去尝试ans+1了。

class Solution {
    public int mySqrt(int x) {
        if (x == 0 || x == 1) {
            return x;
        }
        int left = 1;
        int right = x;
        int mid;
        int ans = -1;
        while (left <= right) {
            mid = left + ((right - left) >> 1);
            if ((long) mid * mid == x) {
                return mid;
            }
            if ((long) mid * mid < x) {
                ans = mid;
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return ans;
    }
}

(简单) 367. 有效的完全平方数

在这里插入图片描述
二分查找

class Solution {
    public boolean isPerfectSquare(int num) {
        if (num == 1) {
            return true;
        }
        int left = 1;
        int right = num;
        int mid;
        while (left <= right) {
            mid = left + ((right - left) >> 1);
            if ((long) mid * mid == num) {
                return true;
            }
            if ((long) mid * mid < num) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return false;
    }
}

当然还可以使用内置的库函数

根据完全平方数的性质,只需要判断num的平方根x是否为整数即可。

class Solution {
    public boolean isPerfectSquare(int num) {
        int x = (int) Math.sqrt(num);
        return x * x == num;
    }
}

还有暴力法

如果num为完全平方数,那么一定存在正整数x满足x * x = num。于是,可以从1开始遍历所有的正整数,一直遍历到46340即可,因为 2 31 − 1 ≈ 46340 \sqrt{2^{31}-1} \approx 46340 2311 46340

class Solution {
    public boolean isPerfectSquare(int num) {
        if (num == 1) {
            return true;
        }
        for (int i = 2; i <= 46340; i++) {
            if (i * i > num) {
                break;
            }
            if (i * i == num) {
                return true;
            }
        }
        return false;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/480015.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

三款自研AI应用引领未来,重塑行业新风尚

在这个科技日新月异的时代&#xff0c;AI技术已经渗透到我们生活的方方面面。今天&#xff0c;我们将向您推荐三款领域独具特色的AI应用&#xff0c;它们分别是AI律师、AI小红书文案提示词、以及AI Midjourney提示词。这些应用都具有独特的内涵&#xff0c;让我们一起走进这些智…

linux--进程程序替换

目录 一、什么是进程程序替换 二、原理&#xff1a; 三、为什么要进行程序替换 四、六种替换函数 命名理解 (1)函数execl (2)函数execv (3)execlp (4)execvp (5)execle (6)execve 一、什么是进程程序替换 所谓进程程序替换&#xff0c;顾名思义&#xff0c;就是使用一个…

‘cnpm‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件

文章目录 1.0 问题描述2.0 检查环境&#xff1a;2.0.1查看node 环境2.0.2 cnpm 要安装好 3.0 查看原因4.0 问题解决【配置环境变量】4.0.1 方式一4.0.2 方式二 5.0 测试成功 1.0 问题描述 ‘cnpm’ 不是内部或外部命令&#xff0c;也不是可运行的程序 或批处理文件。 2.0 检…

手动开发--简单的 Spring 基于注解配置的程序

目录 手动开发--简单的 Spring 基于注解配置的程序 需求说明 思路分析程序结构 2) 程序框架图 ● 应用实例 创建ComponentScan.java注解 创建WyxSpringConfig 创建WyxSpringApplicationContext 作用 注意 获取全类名的步骤 Class.forName和Class.loadClass的区别 手…

c++ 11标准模板(STL) std::vector (二)

定义于头文件 <vector> template< class T, class Allocator std::allocator<T> > class vector;(1)namespace pmr { template <class T> using vector std::vector<T, std::pmr::polymorphic_allocator<T>>; }(2)(C17…

马上五一了,带大家玩一下五子棋——C语言

五一祝福 因为这篇博文实在五一这天完成的&#xff0c;所以呢&#xff0c;在介绍五子棋之前&#xff0c;先祝各位支持小白的大佬都五一快乐&#xff01; 花了点时间下了个“五一快乐”的五子棋&#xff0c;哈哈哈哈哈哈&#xff0c;还不太熟练&#xff0c;所以写的有点丑&…

常用 Composition API【VUE3】

二、常用 Composition API 7. 计算属性与监视 7.1 computed函数 与Vue2.x中computed配置功能一致写法 <template><h1>一个人的信息</h1>姓&#xff1a;<input type"text" v-model"person.firstName"><br><br>名&a…

【ROS 开发神器 Visual Studio Code 的安装和设置】

【ROS 开发神器 Visual Studio Code 的安装和设置】 1. Visual Studio Code的安装1.1 点击deb文件下载1.2 安装VScode1.3 启动软件1.4 添加收藏夹 2. 导入工作空间2.1 熟悉Vscode基本界面2.2 添加工作空间 3. 安装简体中文语言4. 安装ROS插件5. 安装CMake插件6. 安装括号颜色插…

SpringBoot 中的加密模块

Spring Boot 是一款流行的 Java 开发框架&#xff0c;它提供了多种加密模块&#xff0c;用于保护数据的安全性。本文将介绍 Spring Boot 中的加密模块&#xff0c;包括对称加密、非对称加密和哈希加密等&#xff0c;同时还会提供相应的代码示例。 一、对称加密 对称加密是一种…

改进YOLOv8 | 即插即用篇 | 全维动态卷积 |《 OMNI-DIMENSIONAL DYNAMIC CONVOLUTION》

单个静态卷积核是现代卷积神经网络(CNNs)的常见训练范式。然而,最近的动态卷积研究表明,学习加权为其输入依赖注意力的n个卷积核的线性组合可以显著提高轻量级CNNs的准确性,同时保持高效的推理。然而,我们观察到现有的作品通过卷积核空间的一个维度(关于卷积核数量)赋予…

关于密码学的进一步答疑:SSL和TLS的区别、CA和CT的关系

《密码学&#xff1a;一文读懂常用加密技术原理及其逻辑与应用方法》一文一经发布后&#xff0c;后台收到了许多私信&#xff0c;承蒙喜爱&#xff0c;这篇文章将主要对后台收到的高频问题予以统一回应。 问题一: 在讨论加密解密的过程中&#xff0c;常常在同一语境下同时出现S…

设计模式之原型模式(深拷贝浅拷贝)

目录 1、什么是原型模式 2、前置知识&#xff08;深拷贝&浅拷贝&#xff09; 2.1 浅拷贝 2.2 深拷贝 3、代码实现 3.1 通过Object中的clone方法实现浅拷贝 3.2 通过对象流来实现深拷贝 4、原型模式总结 4.1 优缺点 4.2 使用场景 4.3 对比直接new对象有何不同 1、…

如何使用递归函数实现Excel列号转换列标

在Excel中&#xff0c;列标与列号转换是VBA开发过程中经常用到的功能&#xff0c;下面这篇博客为大家解释了多种方法。 【Excel列标与列号转换】 那么这篇博文的核心是“递归过程”&#xff0c;实现这个功能并不是必须使用递归过程&#xff0c;但是这也不失为一种实现方法&am…

【Android入门到项目实战-- 8.2】—— 使用HTTP协议访问网络

目录 一、使用HttpURLConnection 1、使用Android的HttpURLConnection步骤 1&#xff09;获取HttpURLConnection实例 2)设置HTTP请求使用的方法 3)定制HTTP请求&#xff0c;如连接超时、读取超时的毫秒数 4)调用getInputStream()方法获取返回的输入流 5)关闭HTTP连接 2、…

NXP - LPC1769与LPC1768的区别

文章目录 NXP - LPC1769与LPC1768的区别概述笔记General description验证结论END NXP - LPC1769与LPC1768的区别 概述 openpnp设备用到了冰沙主板. 冰沙主板的主控MCU用到了LPC1769, 想着研究一下. 订了OM13085UL, 遥遥无期… 买了LPC MCU的书, 里面提到了书的作者的网店, 居…

python+vue精品课程建设制作django服务网站系统

功能介绍通篇文章的撰写基础是实际的应用需要&#xff0c;然后在架构系统之前全面复习大学所修习的相关知识以及网络提供的技术应用教程&#xff0c;以视频建设制作服务的实际应用需要出发&#xff0c;架构系统来改善现视频建设制作服务工作流程繁琐等问题。不仅如此以操作者的…

kotlin在鸿蒙开发中的实践

先说一说kotlin 我们知道&#xff1a; kotlin目前是安卓首选的编程语言。 安卓逐渐抛弃java&#xff0c;拥抱kotlin这是大的趋势。 kotlin的最大优点就是与java的互操作性。 kotlin编译的产物和java一样是bytecode(不抬杠&#xff0c;本文只说面向jvm的kotlin)。 kotlin是一…

Cadence基础操作:Schematic编辑

本文转载自B站up主:_WithB&#xff0c;原文链接如下&#xff1a;https://www.bilibili.com/read/cv20414466 鼠标 左键单击 –> 选中或确定操作 按住左键 –> 选中区域内所有组件 左键双击&#xff0c;可以选择以特定操作模式和窗口类型进入对应组件的下一层一般我是ed…

Winform从入门到精通(36)—ColorDialog(史上最全)更新中

前言 当我们需要设置某个控件的颜色时,并且需要弹出一个可以选择颜色的对话框时,这时候就需要使用ColorDialog 一、属性 1、AllowFullOpen 该属性用于启用或者禁用“自定义颜色按钮”,该属性为true时,可以自定义颜色 2、AnyColor 实际测试该属性没什么作用 3、Colo…

请求与相应

从容器到Servlet 前面我们介绍了JSP的内置对象和Servlet的相关知识&#xff0c; 以及如何部署和开发一个Servlet。但是&#xff0c; 并没有详细介绍如何将Servlet与JSP结合起来使用。Web容器是JSP唯一可以识别的HTTP服务器&#xff0c; 所以必须了解Web容器如何生成请求和响应…