【算法】二分查找(二)查找边界二分

【算法】二分查找(二)查找边界二分

目录

题目介绍

二段性

1.二段搜索

1.1搜索段端点

1.1.1住段的左端点

1.1.2住段的右端点

2.死循环

2.1中点偏向

2.2多余搜索

3.模板

3.1求段左端点:​编辑

3.2求段右端点:​编辑

4.区别

提交代码


题目介绍

34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8 输出:[3,4]

示例 2:

输入:nums = [5,7,7,8,8,10], target = 6 输出:[-1,-1]

示例 3:

输入:nums = [], target = 0 输出:[-1,-1]

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109

二段性

区域 可划分成 两段性质部分





1.二段搜索

两指针 在各段部分内 中点算 半缩排区域 地 跃段住段 往界点搜索1.1搜索段端点

跃指针住指针相遇时 算排出 住段部分的端点1.1.1住段的左端点

1.1.2住段的右端点

2.死循环

中点 下次立到 在住段部分的 住指针时,住指针的移动 去原地踏步后,再下次的中点也还不变 继续立住指针 去原地踏步,进入死循环2.1中点偏向left + (right - left) / 2偏左中点left + (right - left + 1) / 2偏右中点

中点 不能 偏向 住段侧 立,否则 最后中点会立到住指针 而进入死循环2.2多余搜索

跃指针与住指针相遇 算排得到结果后 就可停止搜索了,如果此时继续去搜索,left == right的中点 会立到住指针 而进入 原地踏步死循环除非里面增加一个 left == right时 判断nums[left]等不等于target  已找到边界或找不到边界 而return退出,否则left <= right 条件退不出来的3.模板

while(left < right) {

      int mid = left + (right - left) / 2; —> 立 偏左中点

      if(mid落左跃段) left = mid + 1;

      else right = mid; —> mid落右住段

}



while(left < right) {

      int mid = left + (right - left + 1) / 2; —> 立 偏右中点

      if(mid落左住段) left = mid;

      else right = mid - 1; —> mid落右跃段

}

最上面 + 1,最下面 - 1)4.区别

朴素二分找值所在位置∈边界二分找值范围边界可以通过 找此一个值的边界位置 来找到此值

3.2求段端点:

3.1求段端点:


提交代码

public int[] searchRange(int[] nums, int target) { int[] ret = new int[2]; ret[0] = ret[1] = -1; if(nums.length == 0) return ret; // 查找段端点,住段的左端点: [<3] [这里|>=3] (左对应>=) int left = 0, right = nums.length - 1; while(left < right) { // 不能去多余搜索 left == right时的情况,否则会 中点立到住指针 进入死循环 int mid = left + (right - left) / 2; // 中点要偏向跃段部分 即偏左 if(nums[mid] < target) left = mid + 1; // mid落左跃段 else right = mid; // mid落右住段 } if(nums[left] != target) return ret; else ret[0] = left; // 查找段端点,住段的右端点: [<=3|这里] [>3] (右对应<=) // left = 0; // 接下来左指针 可以直接从找到的左端点处开始 对剩下的右半部分 进行二分 查找右端点 right = nums.length - 1; while(left < right) { int mid = left + (right - left + 1) / 2; // 中点不能偏向住段部分 即偏右 // 上面有+1 if(nums[mid] <= target) left = mid; // mid落左住段 else right = mid - 1; // mid落右跃段 // 下面有-1 } ret[1] = left; return ret; }

Read more

C++ 拷贝构造函数与赋值运算符:深拷贝与浅拷贝的核心辨析

C++ 拷贝构造函数与赋值运算符:深拷贝与浅拷贝的核心辨析

C++ 拷贝构造函数与赋值运算符:深拷贝与浅拷贝的核心辨析 💡 学习目标:掌握拷贝构造函数与赋值运算符的定义及调用场景,理解深拷贝与浅拷贝的本质区别,能够在实际开发中避免内存泄漏与野指针问题。 💡 学习重点:拷贝构造函数的触发条件、浅拷贝的缺陷、深拷贝的实现方法、赋值运算符的重载原则。 一、拷贝构造函数的概念与触发场景 ✅ 结论:拷贝构造函数是一种特殊的构造函数,用于通过一个已存在的对象创建一个新对象,其参数必须是本类对象的常量引用(const 类名&)。 1.1 拷贝构造函数的语法格式 class 类名 {public:// 普通构造函数 类名(参数列表);// 拷贝构造函数 类名(const 类名& other);}; ⚠️ 注意事项: 1. 拷贝构造函数的参数必须是常量引用,使用 const 防止实参被修改,使用引用避免无限递归调用拷贝构造函数。 2. 如果没有手动定义拷贝构造函数,编译器会自动生成一个默认拷贝构造函数,实现简单的成员变量值拷贝。 1.2 拷贝构造函数的触发条件

By Ne0inhk
【 C/C++ 算法】入门动态规划-----一维动态规划基础(以练代学式)

【 C/C++ 算法】入门动态规划-----一维动态规划基础(以练代学式)

每日激励:“不设限和自我肯定的心态:I can do all things。 — Stephen Curry” 绪论 : 本章是动态规划算法的基础入门篇,我将通过三道简单题 + 一道中等难度的一维动态规划题来带你对动态规划有个初认识,并基本了解动态规划的最基本常见的写法,只有将基本写法了解了,对后续的难的题目自然也不会毫无头绪,后续还将持续更新更多相关的动规算法,敬请期待~🙃 ———————— 早关注不迷路,话不多说安全带系好,发车啦(建议电脑观看)。 👻动态规划🌥️ 这里通过大量练习得出下面动态规划做题步骤 简单的说动态规划理解成:某种状态的公式 + 提前求出来值的容器 求出当前位置的值然后放到容器中后后续使用 因为最开始的值一般是会看见的所以就能有初始值,从而启动动态规划 从上中可以主要提炼出: * 状态 * 容器的重要性 * 公式,可以换种说法:状态转移方程 这样严格😈的说:动态规划 = 状态定义 + 状态转移方程 + 初始条件 + 状态存储(容器) 下述步骤是通过写完下述四道题后的总结,所以同样需要道友🗡️大量的练习沉淀最终就能对动态规划的题目

By Ne0inhk
【探寻C++之旅】C++11 深度解析:重塑现代 C++ 的关键特性

【探寻C++之旅】C++11 深度解析:重塑现代 C++ 的关键特性

请君浏览 * 前言 * 1. C++的发展历史 * 2. 列表初始化:统一对象初始化的优雅方案 * 2.1 从 C++98 到 C++11 的突破 * 2.2 std::initializer_list:容器初始化的 “神器” * 3. 右值引用和移动语义:彻底解决拷贝性能痛点 * 3.1 左值 vs 右值 * 3.2 左值引用 vs 右值引用 * 3.3右值引用的使用场景 * 3.3.1参数匹配 * 3.3.2 类型分类 * 3.3.3 移动构造和移动赋值

By Ne0inhk
C++ 继承:面向对象的代码复用核心机制

C++ 继承:面向对象的代码复用核心机制

C++ 继承:面向对象的代码复用核心机制 💡 学习目标:掌握继承的基本语法与核心特性,理解不同继承方式的访问权限控制,能够通过继承实现代码复用与扩展。 💡 学习重点:继承的语法格式、三种继承方式的区别、基类与派生类的关系、继承中的构造与析构顺序。 一、继承的概念与核心价值 ✅ 结论:继承是 C++ 面向对象三大特性之一,允许一个类派生类继承另一个类基类的属性和行为,实现代码复用,同时支持派生类在基类基础上扩展新功能。 继承的核心价值体现在两个方面: 1. 代码复用:避免重复编写相同的成员变量和成员函数,降低代码冗余度 2. 功能扩展:派生类可以在基类的基础上新增属性和方法,满足更复杂的业务需求 生活中的继承示例:学生和老师都属于“人”,都有姓名、年龄等属性和吃饭、睡觉等行为。可以先定义 Person 基类,再让 Student 和 Teacher 继承 Person,并各自扩展专属功能。 二、继承的基本语法与实现 2.1

By Ne0inhk