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

2、二分和贪心

一、二分

这里有个小技巧,你会发现,只要是求最大最小最多等等的贪心过程,我们就有3种方法:①二分②贪心算法③动态规划
我们先讲二分和贪心,动态规划比较麻烦,留到后期。

1、了解

在这里插入图片描述

2、模版

class Solution {
public:
    int search(vector<int>& nums, int target) {
        // 二分查找的前提是数组必须是有序的
        int left = 0;                  // 左边界,初始为数组起始位置
        int right = nums.size() - 1;   // 右边界,初始为数组末尾位置

        // 当左边界小于等于右边界时,继续查找
        while (left <= right) {
            int mid = left + (right - left) / 2; // 计算中间位置,避免溢出

            if (nums[mid] == target) {
                // 如果中间值等于目标值,直接返回中间位置
                return mid;
            } else if (nums[mid] < target) {
                // 如果中间值小于目标值,说明目标值在右半部分
                left = mid + 1; // 移动左边界
            } else {
                // 如果中间值大于目标值,说明目标值在左半部分
                right = mid - 1; // 移动右边界
            }
        }

        // 如果未找到目标值,返回 -1
        return -1;
    }
};

以下是提供的C++二分查找代码的分析和说明:


代码分析

这段代码实现了标准的二分查找算法,结构清晰且符合最佳实践。关键点如下:

  1. 有序数组处理

    • 明确说明输入数组必须有序(符合二分查找的前提条件)。
  2. 边界初始化

    int left = 0;
    int right = nums.size() - 1;
    
    • 初始化左右指针为数组的起始和末尾位置,逻辑正确。
  3. 循环条件

    while (left <= right)
    
    • 使用 <= 确保区间内仅剩一个元素时仍能进入循环,避免漏查。
  4. 中间值计算

    int mid = left + (right - left) / 2;
    
    • 避免 (left + right) 的潜在溢出问题,更安全的写法。
  5. 条件分支

    • 找到目标值时直接返回索引。
    • 未找到时调整左右边界,逐步缩小搜索范围,逻辑正确。

测试用例验证

示例 1:目标存在
  • 输入:nums = [-1, 0, 3, 5, 9, 12], target = 9
  • 执行流程:
    1. left=0, right=5mid=2(值为3)→ 3 < 9 → left=3
    2. left=3, right=5mid=4(值为9)→ 命中,返回4。
  • 输出:4(正确)
示例 2:目标不存在
  • 输入:nums = [-1, 0, 3, 5, 9, 12], target = 2
  • 执行流程:
    1. mid依次计算为2、0、1 → 最终 left > right,退出循环。
  • 输出:-1(正确)

边界情况测试

  1. 空数组

    vector<int> nums = {};
    int target = 5;
    
    • 直接返回 -1,正确处理。
  2. 单元素数组

    vector<int> nums = {5};
    int target = 5;  // 返回0
    int target = 2;  // 返回-1
    
    • 结果符合预期。
  3. 目标在两端

    vector<int> nums = [2, 4, 6];
    int target = 2;  // 返回0
    int target = 6;  // 返回2
    
    • 均正确命中。

总结

代码完全正确,涵盖了二分查找的核心逻辑,且处理了所有边界情况。适用于有序数组的快速查找,时间复杂度为 O(log n),空间复杂度为 O(1)

3、例题

  • ACwing分巧克力
    • 这道题的思路:
      • 假设需要的大小是 1-N中的某一个数,这里的N就是 n的数据范围最大值。我从这个数里找中间值,作为所有巧克力的边长,看满足这个边长的情况下,我能拿到多少块巧克力。
      • 如果拿到的巧克力的数量小于 小朋友人数,说明我们需要扩大巧克力数量,就要缩减边长的长度,移动 r = mid-1 。反之,大于就移动 l =mid+1 。
      • 最终结果就是 l -1 。之所以 l要减一,是因为我上面的式子是 l=mid+1 ,但实际上答案是 mid 。所以我们l要减一 。

二、贪心

1、了解

  • 举例了解:如果你需要从一个数组中,找到2个数之和是最大的 ,那么我们就找最大的数,就行了。
    • 也就是说,我们需要什么最大,就找什么的最大值。
  • 贪心是一种思想,而不是一个模版之类的题。其实在算法中,模版只是其次,你要的是做题思想,这个需要做题提升。

2、例题

  • ACwing1522排成最小的数字
  • 首先要知道什么是前导0和后导0
    • 前导零

      • 定义:出现在字符串或数字最左侧的无效零 。
      • 示例
        • 数字 00123 的前导零是开头的两个 0,有效部分是 123。
    • 后导0

      • 定义:出现在字符串或数字最右侧的无效零。
      • 示例
        • 数字 0012300 的后导零是尾部的两个 0,有效部分是 00123。
    • 题解过程:

      • 我们要先排序,再处理前导0 。为什么呢?
        • 因为:当我给你 0-010-0234的3个数字时。假设排序后是00100234,那么我们只删除前2个0,最终为100234。如果先删,就可能出现,你不知道删哪一个,或者你删成每个数字的第一个0.就会导致错误。
      • 因为这个排序具有反对称性、传递性、完整性。用 return a + b < b + a; 的方法排序。

http://www.kler.cn/a/600980.html

相关文章:

  • S32K3 RAM ECC 的问题
  • 《似锦》:曹兴昱—残暴和孝顺并不冲突家庭成长环境分析以命抵命逻辑悖论
  • 代码随想录Day23
  • Scrapy——Redis空闲超时关闭扩展
  • Spring 源码硬核解析系列专题(三十二):Spring Cloud LoadBalancer 的负载均衡源码解析
  • 数据库的操作,以及sql之DML
  • Linux输入系统应用编程
  • 字符串常量,数组和指针的不同形式
  • uv:Rust 驱动的 Python 包管理新时代
  • 飞书只有阅读权限的文档下载,飞书文档下载没有权限的文件
  • Qt 线程类
  • 详解c++20的协程,自定义可等待对象,生成器详解
  • <tauri><rust><GUI>基于rust和tauri,实现多窗口与窗口间通信
  • ISIS-2 邻居建立关系
  • Python 编程中函数嵌套的相关解析
  • React 中React.memo的作用,如何利用它进行组件性能优化?
  • 单片机中C++的局部static变量的初始化仍然遵循控制流
  • Python爬虫异常处理:自动跳过无效URL
  • 2021年蓝桥杯第十二届CC++大学B组真题及代码
  • Redisson 实现分布式锁简单解析