<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>二分查找 - 标签 - 豆浆小屋</title><link>https://soybeanmilk.netlify.app/tags/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/</link><description>二分查找 - 标签 - 豆浆小屋</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><copyright>This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.</copyright><lastBuildDate>Tue, 04 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://soybeanmilk.netlify.app/tags/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/" rel="self" type="application/rss+xml"/><item><title>二分查找模板</title><link>https://soybeanmilk.netlify.app/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE%E6%A8%A1%E6%9D%BF/</link><pubDate>Tue, 04 Aug 2026 00:00:00 +0000</pubDate><author><name>作者</name></author><guid>https://soybeanmilk.netlify.app/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE%E6%A8%A1%E6%9D%BF/</guid><description><![CDATA[<h1 id="二分查找模板与变种" class="headerLink">
    <a href="#%e4%ba%8c%e5%88%86%e6%9f%a5%e6%89%be%e6%a8%a1%e6%9d%bf%e4%b8%8e%e5%8f%98%e7%a7%8d" class="header-mark"></a>二分查找模板与变种</h1><h2 id="核心思想" class="headerLink">
    <a href="#%e6%a0%b8%e5%bf%83%e6%80%9d%e6%83%b3" class="header-mark"></a>1 核心思想</h2><p><strong>前提：数组有序</strong></p>
<p>每次排除一半，时间复杂度 O(log n)。</p>
<hr>
<h2 id="一基础模板找目标值" class="headerLink">
    <a href="#%e4%b8%80%e5%9f%ba%e7%a1%80%e6%a8%a1%e6%9d%bf%e6%89%be%e7%9b%ae%e6%a0%87%e5%80%bc" class="header-mark"></a>2 一、基础模板：找目标值</h2><h3 id="模板-1左闭右闭-left-right" class="headerLink">
    <a href="#%e6%a8%a1%e6%9d%bf-1%e5%b7%a6%e9%97%ad%e5%8f%b3%e9%97%ad-left-right" class="header-mark"></a>2.1 模板 1：左闭右闭 [left, right]</h3><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">binarySearch</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">nums</span><span class="p">,</span> <span class="kt">int</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">left</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">right</span> <span class="o">=</span> <span class="n">nums</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>  <span class="c1">// 右闭
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">while</span> <span class="p">(</span><span class="n">left</span> <span class="o">&lt;=</span> <span class="n">right</span><span class="p">)</span> <span class="p">{</span>                 <span class="c1">// &lt;= 因为是闭区间
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kt">int</span> <span class="n">mid</span> <span class="o">=</span> <span class="n">left</span> <span class="o">+</span> <span class="p">(</span><span class="n">right</span> <span class="o">-</span> <span class="n">left</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>  <span class="c1">// 防溢出，不用 (l+r)/2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="n">nums</span><span class="p">[</span><span class="n">mid</span><span class="p">]</span> <span class="o">==</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">mid</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">nums</span><span class="p">[</span><span class="n">mid</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">left</span> <span class="o">=</span> <span class="n">mid</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>    <span class="c1">// mid 已经看过了，+1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">right</span> <span class="o">=</span> <span class="n">mid</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>   <span class="c1">// mid 已经看过了，-1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>  <span class="c1">// 没找到
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="模板-2左闭右开-left-right" class="headerLink">
    <a href="#%e6%a8%a1%e6%9d%bf-2%e5%b7%a6%e9%97%ad%e5%8f%b3%e5%bc%80-left-right" class="header-mark"></a>2.2 模板 2：左闭右开 [left, right)</h3><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">binarySearch</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">nums</span><span class="p">,</span> <span class="kt">int</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">left</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">right</span> <span class="o">=</span> <span class="n">nums</span><span class="p">.</span><span class="n">size</span><span class="p">();</span>  <span class="c1">// 右开
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">while</span> <span class="p">(</span><span class="n">left</span> <span class="o">&lt;</span> <span class="n">right</span><span class="p">)</span> <span class="p">{</span>              <span class="c1">// &lt; 因为开区间不包含 right
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kt">int</span> <span class="n">mid</span> <span class="o">=</span> <span class="n">left</span> <span class="o">+</span> <span class="p">(</span><span class="n">right</span> <span class="o">-</span> <span class="n">left</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">nums</span><span class="p">[</span><span class="n">mid</span><span class="p">]</span> <span class="o">==</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">mid</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">nums</span><span class="p">[</span><span class="n">mid</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">left</span> <span class="o">=</span> <span class="n">mid</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">right</span> <span class="o">=</span> <span class="n">mid</span><span class="p">;</span>   <span class="c1">// 右开，right 本身不包含，所以不用 -1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><blockquote>
<p>推荐用<strong>左闭右闭</strong>，逻辑更直观，不容易错。</p></blockquote>
<hr>
<h2 id="二查找左边界第一个--target" class="headerLink">
    <a href="#%e4%ba%8c%e6%9f%a5%e6%89%be%e5%b7%a6%e8%be%b9%e7%95%8c%e7%ac%ac%e4%b8%80%e4%b8%aa--target" class="header-mark"></a>3 二、查找左边界（第一个 &gt;= target）</h2><h3 id="模板lower_bound-风格" class="headerLink">
    <a href="#%e6%a8%a1%e6%9d%bflower_bound-%e9%a3%8e%e6%a0%bc" class="header-mark"></a>3.1 模板：lower_bound 风格</h3><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">lowerBound</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">nums</span><span class="p">,</span> <span class="kt">int</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">left</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">right</span> <span class="o">=</span> <span class="n">nums</span><span class="p">.</span><span class="n">size</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="n">left</span> <span class="o">&lt;</span> <span class="n">right</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">mid</span> <span class="o">=</span> <span class="n">left</span> <span class="o">+</span> <span class="p">(</span><span class="n">right</span> <span class="o">-</span> <span class="n">left</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">nums</span><span class="p">[</span><span class="n">mid</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">right</span> <span class="o">=</span> <span class="n">mid</span><span class="p">;</span>       <span class="c1">// 找左边界，往左边缩
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">left</span> <span class="o">=</span> <span class="n">mid</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">left</span><span class="p">;</span>  <span class="c1">// left == right，就是第一个 &gt;= target 的位置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>判断是否存在：</strong> 返回值 &lt; nums.size() &amp;&amp; nums[left] == target</p>
<hr>
<h2 id="三查找右边界最后一个--target" class="headerLink">
    <a href="#%e4%b8%89%e6%9f%a5%e6%89%be%e5%8f%b3%e8%be%b9%e7%95%8c%e6%9c%80%e5%90%8e%e4%b8%80%e4%b8%aa--target" class="header-mark"></a>4 三、查找右边界（最后一个 &lt;= target）</h2><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">upperBound</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">nums</span><span class="p">,</span> <span class="kt">int</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">left</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">right</span> <span class="o">=</span> <span class="n">nums</span><span class="p">.</span><span class="n">size</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="n">left</span> <span class="o">&lt;</span> <span class="n">right</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">mid</span> <span class="o">=</span> <span class="n">left</span> <span class="o">+</span> <span class="p">(</span><span class="n">right</span> <span class="o">-</span> <span class="n">left</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">nums</span><span class="p">[</span><span class="n">mid</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">left</span> <span class="o">=</span> <span class="n">mid</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>    <span class="c1">// 找右边界，往右边扩
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">right</span> <span class="o">=</span> <span class="n">mid</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">left</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>  <span class="c1">// 最后一个 &lt;= target 的位置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<h2 id="四二分查找的四种情况速记" class="headerLink">
    <a href="#%e5%9b%9b%e4%ba%8c%e5%88%86%e6%9f%a5%e6%89%be%e7%9a%84%e5%9b%9b%e7%a7%8d%e6%83%85%e5%86%b5%e9%80%9f%e8%ae%b0" class="header-mark"></a>5 四、二分查找的四种情况速记</h2><table>
  <thead>
      <tr>
          <th>目标</th>
          <th>条件</th>
          <th>返回</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>是否存在 target</td>
          <td><code>==</code> 返回 mid</td>
          <td>下标 或 -1</td>
      </tr>
      <tr>
          <td>第一个 &gt;= target（左边界）</td>
          <td><code>&gt;=</code> 时 right=mid</td>
          <td>left</td>
      </tr>
      <tr>
          <td>第一个 &gt; target（upper_bound）</td>
          <td><code>&gt;</code> 时 right=mid</td>
          <td>left</td>
      </tr>
      <tr>
          <td>最后一个 &lt;= target</td>
          <td><code>&lt;=</code> 时 left=mid+1</td>
          <td>left - 1</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="五stl-自带函数" class="headerLink">
    <a href="#%e4%ba%94stl-%e8%87%aa%e5%b8%a6%e5%87%bd%e6%95%b0" class="header-mark"></a>6 五、STL 自带函数</h2><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;algorithm&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 第一个 &gt;= target 的迭代器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">auto</span> <span class="n">it1</span> <span class="o">=</span> <span class="n">lower_bound</span><span class="p">(</span><span class="n">nums</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">nums</span><span class="p">.</span><span class="n">end</span><span class="p">(),</span> <span class="n">target</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 第一个 &gt; target 的迭代器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">auto</span> <span class="n">it2</span> <span class="o">=</span> <span class="n">upper_bound</span><span class="p">(</span><span class="n">nums</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">nums</span><span class="p">.</span><span class="n">end</span><span class="p">(),</span> <span class="n">target</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 等于 target 的范围 [first, last)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">auto</span> <span class="n">range</span> <span class="o">=</span> <span class="n">equal_range</span><span class="p">(</span><span class="n">nums</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">nums</span><span class="p">.</span><span class="n">end</span><span class="p">(),</span> <span class="n">target</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// target 出现的次数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">cnt</span> <span class="o">=</span> <span class="n">upper_bound</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">v</span><span class="p">.</span><span class="n">end</span><span class="p">(),</span> <span class="n">x</span><span class="p">)</span> <span class="o">-</span> <span class="n">lower_bound</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">v</span><span class="p">.</span><span class="n">end</span><span class="p">(),</span> <span class="n">x</span><span class="p">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<h2 id="六常见应用场景" class="headerLink">
    <a href="#%e5%85%ad%e5%b8%b8%e8%a7%81%e5%ba%94%e7%94%a8%e5%9c%ba%e6%99%af" class="header-mark"></a>7 六、常见应用场景</h2><h3 id="1-搜索插入位置leetcode-35" class="headerLink">
    <a href="#1-%e6%90%9c%e7%b4%a2%e6%8f%92%e5%85%a5%e4%bd%8d%e7%bd%aeleetcode-35" class="header-mark"></a>7.1 1. 搜索插入位置（LeetCode 35）</h3><p>找第一个 &gt;= target 的位置，就是插入位置。</p>
<h3 id="2-在排序数组中查找元素的第一个和最后一个位置leetcode-34" class="headerLink">
    <a href="#2-%e5%9c%a8%e6%8e%92%e5%ba%8f%e6%95%b0%e7%bb%84%e4%b8%ad%e6%9f%a5%e6%89%be%e5%85%83%e7%b4%a0%e7%9a%84%e7%ac%ac%e4%b8%80%e4%b8%aa%e5%92%8c%e6%9c%80%e5%90%8e%e4%b8%80%e4%b8%aa%e4%bd%8d%e7%bd%aeleetcode-34" class="header-mark"></a>7.2 2. 在排序数组中查找元素的第一个和最后一个位置（LeetCode 34）</h3><p>分别找左边界和右边界。</p>
<h3 id="3-二分答案最大化最小值--最小化最大值" class="headerLink">
    <a href="#3-%e4%ba%8c%e5%88%86%e7%ad%94%e6%a1%88%e6%9c%80%e5%a4%a7%e5%8c%96%e6%9c%80%e5%b0%8f%e5%80%bc--%e6%9c%80%e5%b0%8f%e5%8c%96%e6%9c%80%e5%a4%a7%e5%80%bc" class="header-mark"></a>7.3 3. 二分答案（最大化最小值 / 最小化最大值）</h3><p>答案有单调性时，对答案二分：</p>
<ul>
<li>「使&hellip;最大值最小」→ 二分答案，check 是否可行</li>
<li>「使&hellip;最小值最大」→ 二分答案，check 是否可行</li>
</ul>
<h3 id="4-旋转排序数组" class="headerLink">
    <a href="#4-%e6%97%8b%e8%bd%ac%e6%8e%92%e5%ba%8f%e6%95%b0%e7%bb%84" class="header-mark"></a>7.4 4. 旋转排序数组</h3><p>部分有序，判断哪一半是有序的，再决定搜哪边。</p>
<hr>
<h2 id="七写二分的步骤" class="headerLink">
    <a href="#%e4%b8%83%e5%86%99%e4%ba%8c%e5%88%86%e7%9a%84%e6%ad%a5%e9%aa%a4" class="header-mark"></a>8 七、写二分的步骤</h2><ol>
<li><strong>确定区间形式</strong>：左闭右闭还是左闭右开（推荐左闭右闭）</li>
<li><strong>确定循环条件</strong>：<code>&lt;=</code> 还是 <code>&lt;</code></li>
<li><strong>确定 mid 更新方式</strong>：left = mid+1 / right = mid-1 还是别的</li>
<li><strong>考虑边界情况</strong>：target 比所有都小 / 都大 / 不存在</li>
</ol>
<hr>
<h2 id="八易错点" class="headerLink">
    <a href="#%e5%85%ab%e6%98%93%e9%94%99%e7%82%b9" class="header-mark"></a>9 八、易错点</h2><ol>
<li><strong>mid 溢出</strong>：用 <code>left + (right - left) / 2</code>，不用 <code>(left + right) / 2</code></li>
<li><strong>死循环</strong>：left = mid 时容易死循环，mid 要向上取整 <code>left + (right - left + 1) / 2</code></li>
<li><strong>边界判断</strong>：循环结束后记得判断是否越界</li>
<li><strong>区间一致性</strong>：全程保持区间定义一致，别一会儿闭一会儿开</li>
</ol>
]]></description></item><item><title>Q704 二分查找</title><link>https://soybeanmilk.netlify.app/q704-%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><author><name>作者</name></author><guid>https://soybeanmilk.netlify.app/q704-%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/</guid><description><![CDATA[<blockquote>
<p><strong>题目</strong>：给定一个升序整数数组 <code>nums</code> 和目标值 <code>target</code>。如果目标值存在，返回它的下标；否则返回 <code>-1</code>。题目保证数组中的元素不重复。</p></blockquote>
<h2 id="简要回答" class="headerLink">
    <a href="#%e7%ae%80%e8%a6%81%e5%9b%9e%e7%ad%94" class="header-mark"></a>1 简要回答</h2><p>二分查找适用于<strong>有序数组</strong>。每次比较中间元素与目标值，根据大小关系排除一半搜索范围，直到找到目标值或区间为空。</p>
<p>真正容易出错的地方不是算法思想，而是区间边界。写代码前必须先确定搜索区间是 <code>[left, right]</code> 还是 <code>[left, right)</code>，并让循环条件与边界更新始终遵守这个定义，这就是<strong>循环不变量</strong>。</p>
<ul>
<li>时间复杂度：$O(\log n)$</li>
<li>空间复杂度：$O(1)$</li>
</ul>
<h2 id="核心思想" class="headerLink">
    <a href="#%e6%a0%b8%e5%bf%83%e6%80%9d%e6%83%b3" class="header-mark"></a>2 核心思想</h2><p>设数组为：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">下标：  0   1   2   3   4   5
</span></span><span class="line"><span class="cl">数组： -1   0   3   5   9  12
</span></span><span class="line"><span class="cl">目标： target = 9
</span></span></code></pre></td></tr></table>
</div>
</div><p>查找过程：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">第 1 次：[0, 5]，mid = 2，nums[2] = 3 &lt; 9
</span></span><span class="line"><span class="cl">         排除下标 0～2，下一轮搜索 [3, 5]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">第 2 次：[3, 5]，mid = 4，nums[4] = 9
</span></span><span class="line"><span class="cl">         找到目标，返回下标 4
</span></span></code></pre></td></tr></table>
</div>
</div><p>每次至少排除当前区间的一半，所以最多查找约 $\log_2 n$ 次。</p>
<h2 id="什么是循环不变量" class="headerLink">
    <a href="#%e4%bb%80%e4%b9%88%e6%98%af%e5%be%aa%e7%8e%af%e4%b8%8d%e5%8f%98%e9%87%8f" class="header-mark"></a>3 什么是循环不变量</h2><blockquote>
<p>在循环开始、执行过程中和结束时，搜索目标所在区间的定义始终不变。</p></blockquote>
<p>例如选择左闭右闭区间 <code>[left, right]</code> 后：</p>
<ul>
<li><code>left</code> 指向的元素在搜索范围内。</li>
<li><code>right</code> 指向的元素也在搜索范围内。</li>
<li>每次更新后，新的区间仍然必须是左闭右闭。</li>
</ul>
<p>因此不能一会儿按照闭区间写 <code>right = mid - 1</code>，一会儿又按照开区间写 <code>right = mid</code>。</p>
<h2 id="size-与-size---1-的区别" class="headerLink">
    <a href="#size-%e4%b8%8e-size---1-%e7%9a%84%e5%8c%ba%e5%88%ab" class="header-mark"></a>4 size 与 size - 1 的区别</h2><p>假设数组中有 6 个元素：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">元素个数 size = 6
</span></span><span class="line"><span class="cl">有效下标：0、1、2、3、4、5
</span></span><span class="line"><span class="cl">最后一个有效下标：size - 1 = 5
</span></span></code></pre></td></tr></table>
</div>
</div><p>因此，<code>size</code> 表示的是<strong>元素个数</strong>，而 <code>size - 1</code> 才是<strong>最后一个元素的下标</strong>。</p>
<h3 id="为什么左闭右闭使用-size---1" class="headerLink">
    <a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e5%b7%a6%e9%97%ad%e5%8f%b3%e9%97%ad%e4%bd%bf%e7%94%a8-size---1" class="header-mark"></a>4.1 为什么左闭右闭使用 size - 1</h3><p>左闭右闭区间 <code>[left, right]</code> 包含 <code>right</code>：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">数组有效下标：[0, 5]
</span></span><span class="line"><span class="cl">搜索区间：    [0, 5]
</span></span></code></pre></td></tr></table>
</div>
</div><p>所以 <code>right</code> 必须指向最后一个有效元素：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">right</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">nums</span><span class="p">.</span><span class="n">size</span><span class="p">())</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>如果这里写成 <code>right = nums.size()</code>，就会把不存在的 <code>nums[6]</code> 也放进搜索范围，可能造成越界。</p>
<h3 id="为什么左闭右开使用-size" class="headerLink">
    <a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e5%b7%a6%e9%97%ad%e5%8f%b3%e5%bc%80%e4%bd%bf%e7%94%a8-size" class="header-mark"></a>4.2 为什么左闭右开使用 size</h3><p>左闭右开区间 <code>[left, right)</code> 不包含 <code>right</code>：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">数组有效下标：[0, 5]
</span></span><span class="line"><span class="cl">搜索区间：    [0, 6)
</span></span></code></pre></td></tr></table>
</div>
</div><p>虽然 <code>right = 6</code>，但右端点不参与搜索，真正包含的仍然是下标 <code>0～5</code>：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">right</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">nums</span><span class="p">.</span><span class="n">size</span><span class="p">());</span>
</span></span></code></pre></td></tr></table>
</div>
</div><blockquote>
<p><strong>记忆方法</strong>：</p>
<ul>
<li>右边界<strong>包含在区间里</strong>：它必须是有效下标，所以使用 <code>size - 1</code>。</li>
<li>右边界<strong>不包含在区间里</strong>：它可以指向末尾元素的后一个位置，所以使用 <code>size</code>。</li>
</ul></blockquote>
<h2 id="写法一左闭右闭区间" class="headerLink">
    <a href="#%e5%86%99%e6%b3%95%e4%b8%80%e5%b7%a6%e9%97%ad%e5%8f%b3%e9%97%ad%e5%8c%ba%e9%97%b4" class="header-mark"></a>5 写法一：左闭右闭区间</h2><p>因为左右端点都有效，所以：</p>
<ul>
<li>初始化：<code>right = nums.size() - 1</code></li>
<li>循环条件：<code>left &lt;= right</code></li>
<li>去左半边：<code>right = mid - 1</code></li>
<li>去右半边：<code>left = mid + 1</code></li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Solution</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">search</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">nums</span><span class="p">,</span> <span class="kt">int</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">left</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">right</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">nums</span><span class="p">.</span><span class="n">size</span><span class="p">())</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 搜索区间始终是 [left, right]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">while</span> <span class="p">(</span><span class="n">left</span> <span class="o">&lt;=</span> <span class="n">right</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">int</span> <span class="n">mid</span> <span class="o">=</span> <span class="n">left</span> <span class="o">+</span> <span class="p">(</span><span class="n">right</span> <span class="o">-</span> <span class="n">left</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">nums</span><span class="p">[</span><span class="n">mid</span><span class="p">]</span> <span class="o">==</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="n">mid</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">nums</span><span class="p">[</span><span class="n">mid</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">left</span> <span class="o">=</span> <span class="n">mid</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>   <span class="c1">// 下一轮：[mid + 1, right]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">right</span> <span class="o">=</span> <span class="n">mid</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>  <span class="c1">// 下一轮：[left, mid - 1]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><blockquote>
<p>初学时可以先固定使用左闭右闭写法。循环条件和边界更新比较对称：<code>&lt;=</code>、<code>mid + 1</code>、<code>mid - 1</code>。</p></blockquote>
<h2 id="写法二左闭右开区间" class="headerLink">
    <a href="#%e5%86%99%e6%b3%95%e4%ba%8c%e5%b7%a6%e9%97%ad%e5%8f%b3%e5%bc%80%e5%8c%ba%e9%97%b4" class="header-mark"></a>6 写法二：左闭右开区间</h2><p>因为右端点不在搜索范围内，所以：</p>
<ul>
<li>初始化：<code>right = nums.size()</code></li>
<li>循环条件：<code>left &lt; right</code></li>
<li>去左半边：<code>right = mid</code></li>
<li>去右半边：<code>left = mid + 1</code></li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Solution</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">search</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">nums</span><span class="p">,</span> <span class="kt">int</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">left</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">right</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">nums</span><span class="p">.</span><span class="n">size</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 搜索区间始终是 [left, right)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">while</span> <span class="p">(</span><span class="n">left</span> <span class="o">&lt;</span> <span class="n">right</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kt">int</span> <span class="n">mid</span> <span class="o">=</span> <span class="n">left</span> <span class="o">+</span> <span class="p">(</span><span class="n">right</span> <span class="o">-</span> <span class="n">left</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">nums</span><span class="p">[</span><span class="n">mid</span><span class="p">]</span> <span class="o">==</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="n">mid</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">nums</span><span class="p">[</span><span class="n">mid</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">left</span> <span class="o">=</span> <span class="n">mid</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>  <span class="c1">// 下一轮：[mid + 1, right)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">right</span> <span class="o">=</span> <span class="n">mid</span><span class="p">;</span>     <span class="c1">// 下一轮：[left, mid)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="两种写法对照" class="headerLink">
    <a href="#%e4%b8%a4%e7%a7%8d%e5%86%99%e6%b3%95%e5%af%b9%e7%85%a7" class="header-mark"></a>7 两种写法对照</h2><table>
  <thead>
      <tr>
          <th>对比项</th>
          <th>左闭右闭</th>
          <th>左闭右开</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>搜索区间</td>
          <td><code>[left, right]</code></td>
          <td><code>[left, right)</code></td>
      </tr>
      <tr>
          <td><code>right</code> 初值</td>
          <td><code>size - 1</code></td>
          <td><code>size</code></td>
      </tr>
      <tr>
          <td>循环条件</td>
          <td><code>left &lt;= right</code></td>
          <td><code>left &lt; right</code></td>
      </tr>
      <tr>
          <td>向左收缩</td>
          <td><code>right = mid - 1</code></td>
          <td><code>right = mid</code></td>
      </tr>
      <tr>
          <td>向右收缩</td>
          <td><code>left = mid + 1</code></td>
          <td><code>left = mid + 1</code></td>
      </tr>
      <tr>
          <td>区间为空</td>
          <td><code>left &gt; right</code></td>
          <td><code>left == right</code></td>
      </tr>
  </tbody>
</table>
<blockquote>
<p><strong>不要混用</strong>：<code>[left, right]</code> 配套的是 <code>left &lt;= right</code> 和 <code>right = mid - 1</code>；<code>[left, right)</code> 配套的是 <code>left &lt; right</code> 和 <code>right = mid</code>。混用会漏查元素、越界或造成死循环。</p></blockquote>
<h2 id="为什么这样计算-mid" class="headerLink">
    <a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e8%bf%99%e6%a0%b7%e8%ae%a1%e7%ae%97-mid" class="header-mark"></a>8 为什么这样计算 mid</h2><p>推荐写法：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">mid</span> <span class="o">=</span> <span class="n">left</span> <span class="o">+</span> <span class="p">(</span><span class="n">right</span> <span class="o">-</span> <span class="n">left</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>不推荐：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">mid</span> <span class="o">=</span> <span class="p">(</span><span class="n">left</span> <span class="o">+</span> <span class="n">right</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>数学结果相同，但当 <code>left</code> 和 <code>right</code> 很大时，<code>left + right</code> 可能发生整数溢出。先计算差值更加安全。</p>
<h2 id="二分查找的真正前提" class="headerLink">
    <a href="#%e4%ba%8c%e5%88%86%e6%9f%a5%e6%89%be%e7%9a%84%e7%9c%9f%e6%ad%a3%e5%89%8d%e6%8f%90" class="header-mark"></a>9 二分查找的真正前提</h2><p>基础二分查找需要搜索空间具有<strong>单调性</strong>。在本题中表现为数组升序，因此比较 <code>nums[mid]</code> 与 <code>target</code> 后能够确定目标只可能位于哪一半。</p>
<blockquote>
<p>数组中有重复元素还能二分吗？可以。重复元素不会让普通二分查找失效，但返回的可能是任意一个匹配位置。如果题目要求第一个或最后一个匹配位置，就需要使用边界二分（lower_bound / upper_bound）。</p></blockquote>
<h3 id="常见应用" class="headerLink">
    <a href="#%e5%b8%b8%e8%a7%81%e5%ba%94%e7%94%a8" class="header-mark"></a>9.1 常见应用</h3><ul>
<li>在有序数组中查找某个值。</li>
<li>查找第一个大于等于目标值的位置，即 <code>lower_bound</code>。</li>
<li>查找最后一个小于等于目标值的位置。</li>
<li>对具有单调性的答案范围进行&quot;二分答案&quot;。</li>
</ul>
<p>相关练习：LeetCode 35 搜索插入位置、LeetCode 34 查找元素首末位置、LeetCode 69 x 的平方根、LeetCode 367 有效的完全平方数。</p>
<h2 id="空数组的细节" class="headerLink">
    <a href="#%e7%a9%ba%e6%95%b0%e7%bb%84%e7%9a%84%e7%bb%86%e8%8a%82" class="header-mark"></a>10 空数组的细节</h2><p><code>vector::size()</code> 返回无符号整数。如果直接计算 <code>nums.size() - 1</code>，空数组时会发生无符号下溢。因此左闭右闭版本先转换为有符号整数：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">right</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">nums</span><span class="p">.</span><span class="n">size</span><span class="p">())</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="易错点" class="headerLink">
    <a href="#%e6%98%93%e9%94%99%e7%82%b9" class="header-mark"></a>11 易错点</h2><ul>
<li>没先定义搜索区间，就开始拼凑边界代码。</li>
<li>左闭右闭写成 <code>while (left &lt; right)</code>，漏掉只剩一个元素的情况。</li>
<li>左闭右开写成 <code>right = mid - 1</code>，额外漏掉一个元素。</li>
<li>写成 <code>left = mid</code> 或 <code>right = mid</code> 时没有证明区间一定缩小，可能死循环。</li>
<li>使用 <code>(left + right) / 2</code>，忽略整数溢出风险。</li>
<li>看到重复元素就认为不能二分；真正的问题是返回哪一个匹配位置。</li>
</ul>
]]></description></item></channel></rss>