<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>勇者抽刃向强者的博客</title>
  
  <subtitle>梦是现实的延续，现实是梦的终结</subtitle>
  <link href="https://icheng281.github.io/atom.xml" rel="self"/>
  
  <link href="https://icheng281.github.io/"/>
  <updated>2024-05-28T05:43:57.062Z</updated>
  <id>https://icheng281.github.io/</id>
  
  <author>
    <name>icheng281</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>寻找旋转排序数组中的最小值</title>
    <link href="https://icheng281.github.io/2024/05/28/%E5%AF%BB%E6%89%BE%E6%97%8B%E8%BD%AC%E6%8E%92%E5%BA%8F%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E6%9C%80%E5%B0%8F%E5%80%BC/"/>
    <id>https://icheng281.github.io/2024/05/28/%E5%AF%BB%E6%89%BE%E6%97%8B%E8%BD%AC%E6%8E%92%E5%BA%8F%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E6%9C%80%E5%B0%8F%E5%80%BC/</id>
    <published>2024-05-28T05:42:25.000Z</published>
    <updated>2024-05-28T05:43:57.062Z</updated>
    
    <content type="html"><![CDATA[<h1 id="寻找旋转排序数组中的最小值"><a href="#寻找旋转排序数组中的最小值" class="headerlink" title="寻找旋转排序数组中的最小值"></a>寻找旋转排序数组中的最小值</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>已知一个长度为 <code>n</code> 的数组，预先按照升序排列，经由 <code>1</code> 到 <code>n</code> 次 <strong>旋转</strong> 后，得到输入数组。例如，原数组 <code>nums = [0,1,2,4,5,6,7]</code> 在变化后可能得到：</p><ul><li>若旋转 <code>4</code> 次，则可以得到 <code>[4,5,6,7,0,1,2]</code></li><li>若旋转 <code>7</code> 次，则可以得到 <code>[0,1,2,4,5,6,7]</code></li></ul><p>注意，数组 <code>[a[0], a[1], a[2], ..., a[n-1]]</code> <strong>旋转一次</strong> 的结果为数组 <code>[a[n-1], a[0], a[1], a[2], ..., a[n-2]]</code> 。</p><p>给你一个元素值 <strong>互不相同</strong> 的数组 <code>nums</code> ，它原来是一个升序排列的数组，并按上述情形进行了多次旋转。请你找出并返回数组中的 <strong>最小元素</strong> 。</p><p>你必须设计一个时间复杂度为 <code>O(log n)</code> 的算法解决此问题。</p><p><strong>示例 1：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [3,4,5,1,2]</span><br><span class="line">输出：1</span><br><span class="line">解释：原数组为 [1,2,3,4,5] ，旋转 3 次得到输入数组。</span><br></pre></td></tr></table></figure><p><strong>示例 2：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [4,5,6,7,0,1,2]</span><br><span class="line">输出：0</span><br><span class="line">解释：原数组为 [0,1,2,4,5,6,7] ，旋转 3 次得到输入数组。</span><br></pre></td></tr></table></figure><p><strong>示例 3：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [11,13,15,17]</span><br><span class="line">输出：11</span><br><span class="line">解释：原数组为 [11,13,15,17] ，旋转 4 次得到输入数组。</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>n == nums.length</code></li><li><code>1 &lt;= n &lt;= 5000</code></li><li><code>-5000 &lt;= nums[i] &lt;= 5000</code></li><li><code>nums</code> 中的所有整数 <strong>互不相同</strong></li><li><code>nums</code> 原来是一个升序排序的数组，并进行了 <code>1</code> 至 <code>n</code> 次旋转</li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="二分查找"><a href="#二分查找" class="headerlink" title="二分查找"></a>二分查找</h3><p><strong>思路及算法</strong></p><p>一个不包含重复元素的升序数组在经过旋转之后，可以得到下面可视化的折线图：<img src="https://assets.leetcode-cn.com/solution-static/153/1.png" alt="fig1"></p><p>其中横轴表示数组元素的下标，纵轴表示数组元素的值。图中标出了最小值的位置，是我们需要查找的目标。</p><p>我们考虑数组中的最后一个元素 x：在最小值右侧的元素（不包括最后一个元素本身），它们的值一定都严格小于 x；而在最小值左侧的元素，它们的值一定都严格大于 x。因此，我们可以根据这一条性质，通过二分查找的方法找出最小值。</p><p>在二分查找的每一步中，左边界为 low，右边界为 high，区间的中点为 pivot，最小值就在该区间内。我们将中轴元素 nums[pivot] 与右边界元素 nums[high] 进行比较，可能会有以下的三种情况：</p><p>第一种情况是 nums[pivot]&lt;nums[high]。如下图所示，这说明 nums[pivot] 是最小值右侧的元素，因此我们可以忽略二分查找区间的右半部分。</p><p><img src="https://assets.leetcode-cn.com/solution-static/153/2.png" alt="fig2"></p><p>第二种情况是 nums[pivot]&gt;nums[high]。如下图所示，这说明 nums[pivot] 是最小值左侧的元素，因此我们可以忽略二分查找区间的左半部分。</p><p><img src="https://assets.leetcode-cn.com/solution-static/153/3.png" alt="fig3"></p><p>由于数组不包含重复元素，并且只要当前的区间长度不为 1，pivot 就不会与 high 重合；而如果当前的区间长度为 1，这说明我们已经可以结束二分查找了。因此不会存在 nums[pivot]&#x3D;nums[high] 的情况。</p><p>当二分查找结束时，我们就得到了最小值所在的位置。</p><p> <strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">class Solution &#123;</span><br><span class="line">public:</span><br><span class="line">    int findMin(vector&lt;int&gt;&amp; nums) &#123;</span><br><span class="line">        int low = 0;</span><br><span class="line">        int high = nums.size() - 1;</span><br><span class="line">        while (low &lt; high) &#123;</span><br><span class="line">            int pivot = low + (high - low) / 2;</span><br><span class="line">            if (nums[pivot] &lt; nums[high]) &#123;</span><br><span class="line">                high = pivot;</span><br><span class="line">            &#125;</span><br><span class="line">            else &#123;</span><br><span class="line">                low = pivot + 1;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return nums[low];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    public int findMin(int[] nums) &#123;</span><br><span class="line">        int low = 0;</span><br><span class="line">        int high = nums.length - 1;</span><br><span class="line">        while (low &lt; high) &#123;</span><br><span class="line">            int pivot = low + (high - low) / 2;</span><br><span class="line">            if (nums[pivot] &lt; nums[high]) &#123;</span><br><span class="line">                high = pivot;</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                low = pivot + 1;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return nums[low];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li><p>时间复杂度：O(log⁡n)，其中 n 是数组 nums 的长度。在二分查找的过程中，每一步会忽略一半的区间，因此时间复杂度为 O(log⁡n)。</p></li><li><p>空间复杂度：O(1)。</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;寻找旋转排序数组中的最小值&quot;&gt;&lt;a href=&quot;#寻找旋转排序数组中的最小值&quot; class=&quot;headerlink&quot; title=&quot;寻找旋转排序数组中的最小值&quot;&gt;&lt;/a&gt;寻找旋转排序数组中的最小值&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题</summary>
      
    
    
    
    
    <category term="二分查找" scheme="https://icheng281.github.io/tags/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/"/>
    
  </entry>
  
  <entry>
    <title>搜索插入位置</title>
    <link href="https://icheng281.github.io/2024/05/28/%E6%90%9C%E7%B4%A2%E6%8F%92%E5%85%A5%E4%BD%8D%E7%BD%AE-1/"/>
    <id>https://icheng281.github.io/2024/05/28/%E6%90%9C%E7%B4%A2%E6%8F%92%E5%85%A5%E4%BD%8D%E7%BD%AE-1/</id>
    <published>2024-05-28T05:41:50.000Z</published>
    <updated>2024-05-28T05:44:18.474Z</updated>
    
    <content type="html"><![CDATA[<h1 id="搜索插入位置"><a href="#搜索插入位置" class="headerlink" title="搜索插入位置"></a>搜索插入位置</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>定一个排序数组和一个目标值，在数组中找到目标值，并返回其索引。如果目标值不存在于数组中，返回它将会被按顺序插入的位置。</p><p>请必须使用时间复杂度为 <code>O(log n)</code> 的算法。</p><p><strong>示例 1:</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [1,3,5,6], target = 5</span><br><span class="line">输出: 2</span><br></pre></td></tr></table></figure><p><strong>示例 2:</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [1,3,5,6], target = 2</span><br><span class="line">输出: 1</span><br></pre></td></tr></table></figure><p><strong>示例 3:</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [1,3,5,6], target = 7</span><br><span class="line">输出: 4</span><br></pre></td></tr></table></figure><p><strong>提示:</strong></p><ul><li><code>1 &lt;= nums.length &lt;= 104</code></li><li><code>-104 &lt;= nums[i] &lt;= 104</code></li><li><code>nums</code> 为 <strong>无重复元素</strong> 的 <strong>升序</strong> 排列数组</li><li><code>-104 &lt;= target &lt;= 104</code></li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="二分查找"><a href="#二分查找" class="headerlink" title="二分查找"></a>二分查找</h3><p><strong>思路及解法</strong></p><p>考虑这个插入的位置 pos，它成立的条件为：</p><p>nums[pos−1]&lt;target≤nums[pos]<br>其中 nums 代表排序数组。由于如果存在这个目标值，我们返回的索引也是 pos，因此我们可以将两个条件合并得出最后的目标：「在一个有序数组中找第一个大于等于 target 的下标」。</p><p>问题转化到这里，直接套用二分法即可，即不断用二分法逼近查找第一个大于等于 target 的下标 。下文给出的代码是笔者习惯的二分写法，ans初值设置为数组长度可以省略边界条件的判断，因为存在一种情况是 target 大于数组中的所有数，此时需要插入到数组长度的位置。</p><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">class Solution &#123;</span><br><span class="line">public:</span><br><span class="line">    int searchInsert(vector&lt;int&gt;&amp; nums, int target) &#123;</span><br><span class="line">        int n = nums.size();</span><br><span class="line">        int left = 0, right = n - 1, ans = n;</span><br><span class="line">        while (left &lt;= right) &#123;</span><br><span class="line">            int mid = ((right - left) &gt;&gt; 1) + left;</span><br><span class="line">            if (target &lt;= nums[mid]) &#123;</span><br><span class="line">                ans = mid;</span><br><span class="line">                right = mid - 1;</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                left = mid + 1;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    public int searchInsert(int[] nums, int target) &#123;</span><br><span class="line">        int n = nums.length;</span><br><span class="line">        int left = 0, right = n - 1, ans = n;</span><br><span class="line">        while (left &lt;= right) &#123;</span><br><span class="line">            int mid = ((right - left) &gt;&gt; 1) + left;</span><br><span class="line">            if (target &lt;= nums[mid]) &#123;</span><br><span class="line">                ans = mid;</span><br><span class="line">                right = mid - 1;</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                left = mid + 1;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li><p>时间复杂度：O(log⁡n)，其中 n 为数组的长度。二分查找所需的时间复杂度为 O(log⁡n)。</p></li><li><p>空间复杂度：O(1)。我们只需要常数空间存放若干变量。</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;搜索插入位置&quot;&gt;&lt;a href=&quot;#搜索插入位置&quot; class=&quot;headerlink&quot; title=&quot;搜索插入位置&quot;&gt;&lt;/a&gt;搜索插入位置&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot; class=&quot;headerlink&quot; titl</summary>
      
    
    
    
    
    <category term="二分查找" scheme="https://icheng281.github.io/tags/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/"/>
    
  </entry>
  
  <entry>
    <title>寻找峰值</title>
    <link href="https://icheng281.github.io/2024/05/28/%E5%AF%BB%E6%89%BE%E5%B3%B0%E5%80%BC/"/>
    <id>https://icheng281.github.io/2024/05/28/%E5%AF%BB%E6%89%BE%E5%B3%B0%E5%80%BC/</id>
    <published>2024-05-28T05:41:31.000Z</published>
    <updated>2024-05-28T05:44:34.528Z</updated>
    
    <content type="html"><![CDATA[<h1 id="寻找峰值"><a href="#寻找峰值" class="headerlink" title="寻找峰值"></a>寻找峰值</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>峰值元素是指其值严格大于左右相邻值的元素。</p><p>给你一个整数数组 <code>nums</code>，找到峰值元素并返回其索引。数组可能包含多个峰值，在这种情况下，返回 <strong>任何一个峰值</strong> 所在位置即可。</p><p>你可以假设 <code>nums[-1] = nums[n] = -∞</code> 。</p><p>你必须实现时间复杂度为 <code>O(log n)</code> 的算法来解决此问题。</p><p><strong>示例 1：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [1,2,3,1]</span><br><span class="line">输出：2</span><br><span class="line">解释：3 是峰值元素，你的函数应该返回其索引 2。</span><br></pre></td></tr></table></figure><p><strong>示例 2：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [1,2,1,3,5,6,4]</span><br><span class="line">输出：1 或 5 </span><br><span class="line">解释：你的函数可以返回索引 1，其峰值元素为 2；</span><br><span class="line">     或者返回索引 5， 其峰值元素为 6。</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= nums.length &lt;= 1000</code></li><li><code>-231 &lt;= nums[i] &lt;= 231 - 1</code></li><li>对于所有有效的 <code>i</code> 都有 <code>nums[i] != nums[i + 1]</code></li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="寻找最大值"><a href="#寻找最大值" class="headerlink" title="寻找最大值"></a>寻找最大值</h3><p><strong>思路及算法</strong></p><p>由于题目保证了 nums[i]≠nums[i+1]，那么数组 nums 中最大值两侧的元素一定严格小于最大值本身。因此，最大值所在的位置就是一个可行的峰值位置。</p><p>我们对数组 nums进行一次遍历，找到最大值对应的位置即可。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">class Solution &#123;</span><br><span class="line">public:</span><br><span class="line">    int findPeakElement(vector&lt;int&gt;&amp; nums) &#123;</span><br><span class="line">        return max_element(nums.begin(), nums.end()) - nums.begin();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    public int findPeakElement(int[] nums) &#123;</span><br><span class="line">        int idx = 0;</span><br><span class="line">        for (int i = 1; i &lt; nums.length; ++i) &#123;</span><br><span class="line">            if (nums[i] &gt; nums[idx]) &#123;</span><br><span class="line">                idx = i;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return idx;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li>时间复杂度：O(n)，其中 n 是数组 nums 的长度。</li><li>空间复杂度：O(1)。</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;寻找峰值&quot;&gt;&lt;a href=&quot;#寻找峰值&quot; class=&quot;headerlink&quot; title=&quot;寻找峰值&quot;&gt;&lt;/a&gt;寻找峰值&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot; class=&quot;headerlink&quot; title=&quot;1.题目内</summary>
      
    
    
    
    
    <category term="二分查找" scheme="https://icheng281.github.io/tags/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/"/>
    
  </entry>
  
  <entry>
    <title>在排序数组中查找元素的第一个和最后一个位置</title>
    <link href="https://icheng281.github.io/2024/05/27/%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%AE/"/>
    <id>https://icheng281.github.io/2024/05/27/%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%AE/</id>
    <published>2024-05-27T02:43:55.000Z</published>
    <updated>2024-05-27T02:52:16.652Z</updated>
    
    <content type="html"><![CDATA[<h1 id="在排序数组中查找元素的第一个和最后一个位置"><a href="#在排序数组中查找元素的第一个和最后一个位置" class="headerlink" title="在排序数组中查找元素的第一个和最后一个位置"></a>在排序数组中查找元素的第一个和最后一个位置</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>给你一个按照非递减顺序排列的整数数组 <code>nums</code>，和一个目标值 <code>target</code>。请你找出给定目标值在数组中的开始位置和结束位置。</p><p>如果数组中不存在目标值 <code>target</code>，返回 <code>[-1, -1]</code>。</p><p>你必须设计并实现时间复杂度为 <code>O(log n)</code> 的算法解决此问题。</p><p><strong>示例 1：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [5,7,7,8,8,10], target = 8</span><br><span class="line">输出：[3,4]</span><br></pre></td></tr></table></figure><p><strong>示例 2：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [5,7,7,8,8,10], target = 6</span><br><span class="line">输出：[-1,-1]</span><br></pre></td></tr></table></figure><p><strong>示例 3：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [], target = 0</span><br><span class="line">输出：[-1,-1]</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>0 &lt;= nums.length &lt;= 105</code></li><li><code>-109 &lt;= nums[i] &lt;= 109</code></li><li><code>nums</code> 是一个非递减数组</li><li><code>-109 &lt;= target &lt;= 109</code></li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="二分查找"><a href="#二分查找" class="headerlink" title="二分查找"></a>二分查找</h3><p><strong>思路及算法</strong></p><p>直观的思路肯定是从前往后遍历一遍。用两个变量记录第一次和最后一次遇见 target 的下标，但这个方法的时间复杂度为 O(n)，没有利用到数组升序排列的条件。</p><p>由于数组已经排序，因此整个数组是单调递增的，我们可以利用二分法来加速查找的过程。</p><p>考虑 target 开始和结束位置，其实我们要找的就是数组中「第一个等于 target 的位置」（记为 leftIdx）和「第一个大于 target的位置减一」（记为 rightIdx）。</p><p>二分查找中，寻找 leftIdx 即为在数组中寻找第一个大于等于 target 的下标，寻找 rightIdx 即为在数组中寻找第一个大于 target 的下标，然后将下标减一。两者的判断条件不同，为了代码的复用，我们定义 binarySearch(nums, target, lower) 表示在 nums 数组中二分查找 target 的位置，如果 lower 为 true\rm truetrue，则查找第一个大于等于 target 的下标，否则查找第一个大于 target 的下标。</p><p>最后，因为 target 可能不存在数组中，因此我们需要重新校验我们得到的两个下标 leftIdx 和 rightIdx，看是否符合条件，如果符合条件就返回 [leftIdx,rightIdx]，不符合就返回 [−1,−1]。</p><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">class Solution &#123; </span><br><span class="line">public:</span><br><span class="line">    int binarySearch(vector&lt;int&gt;&amp; nums, int target, bool lower) &#123;</span><br><span class="line">        int left = 0, right = (int)nums.size() - 1, ans = (int)nums.size();</span><br><span class="line">        while (left &lt;= right) &#123;</span><br><span class="line">            int mid = (left + right) / 2;</span><br><span class="line">            if (nums[mid] &gt; target || (lower &amp;&amp; nums[mid] &gt;= target)) &#123;</span><br><span class="line">                right = mid - 1;</span><br><span class="line">                ans = mid;</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                left = mid + 1;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return ans;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    vector&lt;int&gt; searchRange(vector&lt;int&gt;&amp; nums, int target) &#123;</span><br><span class="line">        int leftIdx = binarySearch(nums, target, true);</span><br><span class="line">        int rightIdx = binarySearch(nums, target, false) - 1;</span><br><span class="line">        if (leftIdx &lt;= rightIdx &amp;&amp; rightIdx &lt; nums.size() &amp;&amp; nums[leftIdx] == target &amp;&amp; nums[rightIdx] == target) &#123;</span><br><span class="line">            return vector&lt;int&gt;&#123;leftIdx, rightIdx&#125;;</span><br><span class="line">        &#125; </span><br><span class="line">        return vector&lt;int&gt;&#123;-1, -1&#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    public int[] searchRange(int[] nums, int target) &#123;</span><br><span class="line">        int leftIdx = binarySearch(nums, target, true);</span><br><span class="line">        int rightIdx = binarySearch(nums, target, false) - 1;</span><br><span class="line">        if (leftIdx &lt;= rightIdx &amp;&amp; rightIdx &lt; nums.length &amp;&amp; nums[leftIdx] == target &amp;&amp; nums[rightIdx] == target) &#123;</span><br><span class="line">            return new int[]&#123;leftIdx, rightIdx&#125;;</span><br><span class="line">        &#125; </span><br><span class="line">        return new int[]&#123;-1, -1&#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public int binarySearch(int[] nums, int target, boolean lower) &#123;</span><br><span class="line">        int left = 0, right = nums.length - 1, ans = nums.length;</span><br><span class="line">        while (left &lt;= right) &#123;</span><br><span class="line">            int mid = (left + right) / 2;</span><br><span class="line">            if (nums[mid] &gt; target || (lower &amp;&amp; nums[mid] &gt;= target)) &#123;</span><br><span class="line">                right = mid - 1;</span><br><span class="line">                ans = mid;</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                left = mid + 1;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li><p>时间复杂度： O(log⁡n) ，其中 n 为数组的长度。二分查找的时间复杂度为 O(log⁡n)，一共会执行两次，因此总时间复杂度为 O(log⁡n)。</p></li><li><p>空间复杂度：O(1) 。只需要常数空间存放若干变量。</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;在排序数组中查找元素的第一个和最后一个位置&quot;&gt;&lt;a href=&quot;#在排序数组中查找元素的第一个和最后一个位置&quot; class=&quot;headerlink&quot; title=&quot;在排序数组中查找元素的第一个和最后一个位置&quot;&gt;&lt;/a&gt;在排序数组中查找元素的第一个和最后一个位置&lt;/</summary>
      
    
    
    
    
    <category term="二分查找" scheme="https://icheng281.github.io/tags/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/"/>
    
  </entry>
  
  <entry>
    <title>搜索二维矩阵</title>
    <link href="https://icheng281.github.io/2024/05/27/%E6%90%9C%E7%B4%A2%E4%BA%8C%E7%BB%B4%E7%9F%A9%E9%98%B5/"/>
    <id>https://icheng281.github.io/2024/05/27/%E6%90%9C%E7%B4%A2%E4%BA%8C%E7%BB%B4%E7%9F%A9%E9%98%B5/</id>
    <published>2024-05-27T02:40:54.000Z</published>
    <updated>2024-05-27T02:50:22.741Z</updated>
    
    <content type="html"><![CDATA[<h1 id="搜索二维矩阵"><a href="#搜索二维矩阵" class="headerlink" title="搜索二维矩阵"></a>搜索二维矩阵</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>给你一个满足下述两条属性的 <code>m x n</code> 整数矩阵：</p><ul><li>每行中的整数从左到右按非严格递增顺序排列。</li><li>每行的第一个整数大于前一行的最后一个整数。</li></ul><p>给你一个整数 <code>target</code> ，如果 <code>target</code> 在矩阵中，返回 <code>true</code> ；否则，返回 <code>false</code> 。</p><p><strong>示例 1：</strong></p><p><img src="https://assets.leetcode.com/uploads/2020/10/05/mat.jpg" alt="img"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3</span><br><span class="line">输出：true</span><br></pre></td></tr></table></figure><p><strong>示例 2：</strong></p><p><img src="https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2020/11/25/mat2.jpg" alt="img"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13</span><br><span class="line">输出：false</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>m == matrix.length</code></li><li><code>n == matrix[i].length</code></li><li><code>1 &lt;= m, n &lt;= 100</code></li><li><code>-104 &lt;= matrix[i][j], target &lt;= 104</code></li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="二分查找"><a href="#二分查找" class="headerlink" title="二分查找"></a>二分查找</h3><p><strong>思路及算法</strong></p><p>若将矩阵每一行拼接在上一行的末尾，则会得到一个升序数组，我们可以在该数组上二分找到目标元素。</p><p>代码实现时，可以二分升序数组的下标，将其映射到原矩阵的行和列上。</p><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">class Solution &#123;</span><br><span class="line">public:</span><br><span class="line">    bool searchMatrix(vector&lt;vector&lt;int&gt;&gt;&amp; matrix, int target) &#123;</span><br><span class="line">        int m = matrix.size(), n = matrix[0].size();</span><br><span class="line">        int low = 0, high = m * n - 1;</span><br><span class="line">        while (low &lt;= high) &#123;</span><br><span class="line">            int mid = (high - low) / 2 + low;</span><br><span class="line">            int x = matrix[mid / n][mid % n];</span><br><span class="line">            if (x &lt; target) &#123;</span><br><span class="line">                low = mid + 1;</span><br><span class="line">            &#125; else if (x &gt; target) &#123;</span><br><span class="line">                high = mid - 1;</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                return true;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return false;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    public boolean searchMatrix(int[][] matrix, int target) &#123;</span><br><span class="line">        int m = matrix.length, n = matrix[0].length;</span><br><span class="line">        int low = 0, high = m * n - 1;</span><br><span class="line">        while (low &lt;= high) &#123;</span><br><span class="line">            int mid = (high - low) / 2 + low;</span><br><span class="line">            int x = matrix[mid / n][mid % n];</span><br><span class="line">            if (x &lt; target) &#123;</span><br><span class="line">                low = mid + 1;</span><br><span class="line">            &#125; else if (x &gt; target) &#123;</span><br><span class="line">                high = mid - 1;</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                return true;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return false;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li><p>时间复杂度：O(log⁡mn)，其中 m 和 n 分别是矩阵的行数和列数。</p></li><li><p>空间复杂度：O(1)。</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;搜索二维矩阵&quot;&gt;&lt;a href=&quot;#搜索二维矩阵&quot; class=&quot;headerlink&quot; title=&quot;搜索二维矩阵&quot;&gt;&lt;/a&gt;搜索二维矩阵&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot; class=&quot;headerlink&quot; titl</summary>
      
    
    
    
    
    <category term="二分查找" scheme="https://icheng281.github.io/tags/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/"/>
    
  </entry>
  
  <entry>
    <title>搜索插入位置</title>
    <link href="https://icheng281.github.io/2024/05/21/%E6%90%9C%E7%B4%A2%E6%8F%92%E5%85%A5%E4%BD%8D%E7%BD%AE/"/>
    <id>https://icheng281.github.io/2024/05/21/%E6%90%9C%E7%B4%A2%E6%8F%92%E5%85%A5%E4%BD%8D%E7%BD%AE/</id>
    <published>2024-05-21T02:49:25.000Z</published>
    <updated>2024-05-21T02:51:21.363Z</updated>
    
    <content type="html"><![CDATA[<h1 id="搜索插入位置"><a href="#搜索插入位置" class="headerlink" title="搜索插入位置"></a>搜索插入位置</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>定一个排序数组和一个目标值，在数组中找到目标值，并返回其索引。如果目标值不存在于数组中，返回它将会被按顺序插入的位置。</p><p>请必须使用时间复杂度为 <code>O(log n)</code> 的算法。</p><p><strong>示例 1:</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [1,3,5,6], target = 5</span><br><span class="line">输出: 2</span><br></pre></td></tr></table></figure><p><strong>示例 2:</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [1,3,5,6], target = 2</span><br><span class="line">输出: 1</span><br></pre></td></tr></table></figure><p><strong>示例 3:</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [1,3,5,6], target = 7</span><br><span class="line">输出: 4</span><br></pre></td></tr></table></figure><p><strong>提示:</strong></p><ul><li><code>1 &lt;= nums.length &lt;= 104</code></li><li><code>-104 &lt;= nums[i] &lt;= 104</code></li><li><code>nums</code> 为 <strong>无重复元素</strong> 的 <strong>升序</strong> 排列数组</li><li><code>-104 &lt;= target &lt;= 104</code></li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="二分查找"><a href="#二分查找" class="headerlink" title="二分查找"></a>二分查找</h3><p><strong>思路及解法</strong></p><p>考虑这个插入的位置 pos，它成立的条件为：</p><p>nums[pos−1]&lt;target≤nums[pos]<br>其中 nums 代表排序数组。由于如果存在这个目标值，我们返回的索引也是 pos，因此我们可以将两个条件合并得出最后的目标：「在一个有序数组中找第一个大于等于 target 的下标」。</p><p>问题转化到这里，直接套用二分法即可，即不断用二分法逼近查找第一个大于等于 target 的下标 。下文给出的代码是笔者习惯的二分写法，ans初值设置为数组长度可以省略边界条件的判断，因为存在一种情况是 target 大于数组中的所有数，此时需要插入到数组长度的位置。</p><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">class Solution &#123;</span><br><span class="line">public:</span><br><span class="line">    int searchInsert(vector&lt;int&gt;&amp; nums, int target) &#123;</span><br><span class="line">        int n = nums.size();</span><br><span class="line">        int left = 0, right = n - 1, ans = n;</span><br><span class="line">        while (left &lt;= right) &#123;</span><br><span class="line">            int mid = ((right - left) &gt;&gt; 1) + left;</span><br><span class="line">            if (target &lt;= nums[mid]) &#123;</span><br><span class="line">                ans = mid;</span><br><span class="line">                right = mid - 1;</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                left = mid + 1;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    public int searchInsert(int[] nums, int target) &#123;</span><br><span class="line">        int n = nums.length;</span><br><span class="line">        int left = 0, right = n - 1, ans = n;</span><br><span class="line">        while (left &lt;= right) &#123;</span><br><span class="line">            int mid = ((right - left) &gt;&gt; 1) + left;</span><br><span class="line">            if (target &lt;= nums[mid]) &#123;</span><br><span class="line">                ans = mid;</span><br><span class="line">                right = mid - 1;</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                left = mid + 1;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li><p>时间复杂度：O(log⁡n)，其中 n 为数组的长度。二分查找所需的时间复杂度为 O(log⁡n)。</p></li><li><p>空间复杂度：O(1)。我们只需要常数空间存放若干变量。</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;搜索插入位置&quot;&gt;&lt;a href=&quot;#搜索插入位置&quot; class=&quot;headerlink&quot; title=&quot;搜索插入位置&quot;&gt;&lt;/a&gt;搜索插入位置&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot; class=&quot;headerlink&quot; titl</summary>
      
    
    
    
    
    <category term="二分查找" scheme="https://icheng281.github.io/tags/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/"/>
    
  </entry>
  
  <entry>
    <title>寻找两个正序数组的中位数</title>
    <link href="https://icheng281.github.io/2024/05/21/%E5%AF%BB%E6%89%BE%E4%B8%A4%E4%B8%AA%E6%AD%A3%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E4%B8%AD%E4%BD%8D%E6%95%B0/"/>
    <id>https://icheng281.github.io/2024/05/21/%E5%AF%BB%E6%89%BE%E4%B8%A4%E4%B8%AA%E6%AD%A3%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E4%B8%AD%E4%BD%8D%E6%95%B0/</id>
    <published>2024-05-21T02:48:48.000Z</published>
    <updated>2024-05-21T02:50:21.008Z</updated>
    
    <content type="html"><![CDATA[<h1 id="寻找两个正序数组的中位数"><a href="#寻找两个正序数组的中位数" class="headerlink" title="寻找两个正序数组的中位数"></a>寻找两个正序数组的中位数</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>给定两个大小分别为 <code>m</code> 和 <code>n</code> 的正序（从小到大）数组 <code>nums1</code> 和 <code>nums2</code>。请你找出并返回这两个正序数组的 <strong>中位数</strong> 。</p><p>算法的时间复杂度应该为 <code>O(log (m+n))</code> 。</p><p><strong>示例 1：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：nums1 = [1,3], nums2 = [2]</span><br><span class="line">输出：2.00000</span><br><span class="line">解释：合并数组 = [1,2,3] ，中位数 2</span><br></pre></td></tr></table></figure><p><strong>示例 2：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：nums1 = [1,2], nums2 = [3,4]</span><br><span class="line">输出：2.50000</span><br><span class="line">解释：合并数组 = [1,2,3,4] ，中位数 (2 + 3) / 2 = 2.5</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>nums1.length == m</code></li><li><code>nums2.length == n</code></li><li><code>0 &lt;= m &lt;= 1000</code></li><li><code>0 &lt;= n &lt;= 1000</code></li><li><code>1 &lt;= m + n &lt;= 2000</code></li><li><code>-106 &lt;= nums1[i], nums2[i] &lt;= 106</code></li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="二分查找"><a href="#二分查找" class="headerlink" title="二分查找"></a>二分查找</h3><p><strong>思路及解法</strong></p><p>题目是求中位数，其实就是求第 k 小数的一种特殊情况，而求第 k 小数有一种算法。由于数列是有序的，其实我们完全可以一半儿一半儿的排除。假设我们要找第 k 小数，我们可以每次循环排除掉 k&#x2F;2 个数。看下边一个例子。</p><p>假设我们要找第 7 小的数字。</p><p><img src="https://pic.leetcode-cn.com/735ea8129ab5b56b7058c6286217fa4bb5f8a198e4c8b2172fe0f75b29a966cd-image.png" alt="image.png"></p><p>我们比较两个数组的第 k&#x2F;2 个数字，如果 k 是奇数，向下取整。也就是比较第 3 个数字，上边数组中的 4 和下边数组中的 3，如果哪个小，就表明该数组的前 k&#x2F;2 个数字都不是第 k 小数字，所以可以排除。也就是 1，2，3 这三个数字不可能是第 7 小的数字，我们可以把它排除掉。将 1349 和 45678910两个数组作为新的数组进行比较。</p><p>更一般的情况 A[1] ，A[2] ，A[3]，A[k&#x2F;2] … ，B[1]，B[2]，B[3]，B[k&#x2F;2] … ，如果 A[k&#x2F;2]&lt;B[k&#x2F;2] ，那么A[1]，A[2]，A[3]，A[k&#x2F;2]都不可能是第 k 小的数字。</p><p>A 数组中比 A[k&#x2F;2] 小的数有 k&#x2F;2-1 个，B 数组中，B[k&#x2F;2] 比 A[k&#x2F;2] 小，假设 B[k&#x2F;2] 前边的数字都比 A[k&#x2F;2] 小，也只有 k&#x2F;2-1 个，所以比 A[k&#x2F;2] 小的数字最多有 k&#x2F;1-1+k&#x2F;2-1&#x3D;k-2个，所以 A[k&#x2F;2] 最多是第 k-1 小的数。而比 A[k&#x2F;2] 小的数更不可能是第 k 小的数了，所以可以把它们排除。</p><p>橙色的部分表示已经去掉的数字。</p><p><img src="https://pic.leetcode-cn.com/09b8649cd2b8bbea74f7f632b098fed5f8404530ff44b5a0b54a360b3cf7dd8f-image.png" alt="image.png"></p><p>由于我们已经排除掉了 3 个数字，就是这 3 个数字一定在最前边，所以在两个新数组中，我们只需要找第 7 - 3 &#x3D; 4 小的数字就可以了，也就是 k &#x3D; 4。此时两个数组，比较第 2 个数字，3 &lt; 5，所以我们可以把小的那个数组中的 1 ，3 排除掉了。</p><p><img src="https://pic.leetcode-cn.com/f2d72fd3dff109ad810895b9a0c8d8782f47df6b2f24f9de72704961bc547fcb-image.png" alt="image.png"></p><p>我们又排除掉 2 个数字，所以现在找第 4 - 2 &#x3D; 2 小的数字就可以了。此时比较两个数组中的第 k &#x2F; 2 &#x3D; 1 个数，4 &#x3D;&#x3D; 4，怎么办呢？由于两个数相等，所以我们无论去掉哪个数组中的都行，因为去掉 1 个总会保留 1 个的，所以没有影响。为了统一，我们就假设 4 &gt; 4 吧，所以此时将下边的 4 去掉。</p><p><img src="https://pic.leetcode-cn.com/3c89a8ea29f2e19057b57242c8bc37c5f09b6796b96c30f3d42caea21c12f294-image.png" alt="image.png"></p><p>由于又去掉 1 个数字，此时我们要找第 1 小的数字，所以只需判断两个数组中第一个数字哪个小就可以了，也就是 4。</p><p>所以第 7 小的数字是 4。</p><p>我们每次都是取 k&#x2F;2 的数进行比较，有时候可能会遇到数组长度小于 k&#x2F;2的时候。</p><p><img src="https://pic.leetcode-cn.com/ad87d1f63a9bbd99e12605686290800ce61b03f9fb98d87f1d8c020d404421ac-image.png" alt="image.png"></p><p>此时 k &#x2F; 2 等于 3，而上边的数组长度是 2，我们此时将箭头指向它的末尾就可以了。这样的话，由于 2 &lt; 3，所以就会导致上边的数组 1，2 都被排除。造成下边的情况。</p><p><img src="https://pic.leetcode-cn.com/7ea1963f184b1dcaddf951326ccbe7aa09cfbb9ebee7fffb2ede131853b3d1de-image.png" alt="image.png"></p><p>由于 2 个元素被排除，所以此时 k &#x3D; 5，又由于上边的数组已经空了，我们只需要返回下边的数组的第 5 个数字就可以了。</p><p>从上边可以看到，无论是找第奇数个还是第偶数个数字，对我们的算法并没有影响，而且在算法进行中，k 的值都有可能从奇数变为偶数，最终都会变为 1 或者由于一个数组空了，直接返回结果。</p><p>所以我们采用递归的思路，为了防止数组长度小于 k&#x2F;2，所以每次比较 min(k&#x2F;2，len(数组) 对应的数字，把小的那个对应的数组的数字排除，将两个新数组进入递归，并且 k 要减去排除的数字的个数。递归出口就是当 k&#x3D;1 或者其中一个数字长度是 0 了。</p><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">public double findMedianSortedArrays(int[] nums1, int[] nums2) &#123;</span><br><span class="line">    int n = nums1.length;</span><br><span class="line">    int m = nums2.length;</span><br><span class="line">    int left = (n + m + 1) / 2;</span><br><span class="line">    int right = (n + m + 2) / 2;</span><br><span class="line">    //将偶数和奇数的情况合并，如果是奇数，会求两次同样的 k 。</span><br><span class="line">    return (getKth(nums1, 0, n - 1, nums2, 0, m - 1, left) + getKth(nums1, 0, n - 1, nums2, 0, m - 1, right)) * 0.5;  </span><br><span class="line">&#125;</span><br><span class="line">    </span><br><span class="line">    private int getKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k) &#123;</span><br><span class="line">        int len1 = end1 - start1 + 1;</span><br><span class="line">        int len2 = end2 - start2 + 1;</span><br><span class="line">        //让 len1 的长度小于 len2，这样就能保证如果有数组空了，一定是 len1 </span><br><span class="line">        if (len1 &gt; len2) return getKth(nums2, start2, end2, nums1, start1, end1, k);</span><br><span class="line">        if (len1 == 0) return nums2[start2 + k - 1];</span><br><span class="line"></span><br><span class="line">        if (k == 1) return Math.min(nums1[start1], nums2[start2]);</span><br><span class="line"></span><br><span class="line">        int i = start1 + Math.min(len1, k / 2) - 1;</span><br><span class="line">        int j = start2 + Math.min(len2, k / 2) - 1;</span><br><span class="line"></span><br><span class="line">        if (nums1[i] &gt; nums2[j]) &#123;</span><br><span class="line">            return getKth(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1));</span><br><span class="line">        &#125;</span><br><span class="line">        else &#123;</span><br><span class="line">            return getKth(nums1, i + 1, end1, nums2, start2, end2, k - (i - start1 + 1));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li><p>时间复杂度：每进行一次循环，我们就减少 k&#x2F;2 个元素，所以时间复杂度是 O(log(k)，而 k&#x3D;(m+n)&#x2F;2，所以最终的复杂也就是 O(log(m+n））。</p></li><li><p>空间复杂度：虽然我们用到了递归，但是可以看到这个递归属于尾递归，所以编译器不需要不停地堆栈，所以空间复杂度为 O(1)。</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;寻找两个正序数组的中位数&quot;&gt;&lt;a href=&quot;#寻找两个正序数组的中位数&quot; class=&quot;headerlink&quot; title=&quot;寻找两个正序数组的中位数&quot;&gt;&lt;/a&gt;寻找两个正序数组的中位数&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot;</summary>
      
    
    
    
    
    <category term="二分查找" scheme="https://icheng281.github.io/tags/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/"/>
    
  </entry>
  
  <entry>
    <title>组合</title>
    <link href="https://icheng281.github.io/2024/05/17/%E7%BB%84%E5%90%88/"/>
    <id>https://icheng281.github.io/2024/05/17/%E7%BB%84%E5%90%88/</id>
    <published>2024-05-17T02:16:02.000Z</published>
    <updated>2024-05-17T02:18:06.670Z</updated>
    
    <content type="html"><![CDATA[<h1 id="组合"><a href="#组合" class="headerlink" title="组合"></a>组合</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>给定两个整数 <code>n</code> 和 <code>k</code>，返回范围 <code>[1, n]</code> 中所有可能的 <code>k</code> 个数的组合。</p><p>你可以按 <strong>任何顺序</strong> 返回答案。</p><p><strong>示例 1：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">输入：n = 4, k = 2</span><br><span class="line">输出：</span><br><span class="line">[</span><br><span class="line">  [2,4],</span><br><span class="line">  [3,4],</span><br><span class="line">  [2,3],</span><br><span class="line">  [1,2],</span><br><span class="line">  [1,3],</span><br><span class="line">  [1,4],</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p><strong>示例 2：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：n = 1, k = 1</span><br><span class="line">输出：[[1]]</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= n &lt;= 20</code></li><li><code>1 &lt;= k &lt;= n</code></li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="回溯"><a href="#回溯" class="headerlink" title="回溯"></a>回溯</h3><p><strong>思路及算法</strong></p><p>n 表示范围为 1…n，balance 表示剩余空间，start 表示开始位置，list 为回溯列表。判断 balance &#x3D;&#x3D; 0，如果为 0 则代表 list 中已经存入 k 个数，拷贝 list 存入结果 ans 中。如果不为 0，从 start 位置开始递归调用，现将当前位置数据加入 list 中，并进入下一层，等待返回后将本层加入的数据移除，本质就是树的构造过程<br>其中循环结束条件默认为最大值到 n，这里可以优化进行剪枝，比如 n&#x3D;4，k&#x3D;3 时，如果列表从 start&#x3D;3 也就是 [3] 开始，那么该组合一定不存在，因为至少要 k&#x3D;3 个数据，所以剪枝临界点为 n-balance+1</p><p><img src="https://pic.leetcode-cn.com/56fc86462db842d44a7e3e7983a7b5b01c8c25d106a9f4296c500d6d84478d74-frame_00001.png" alt="img"></p><p><img src="https://pic.leetcode-cn.com/e4bec9a5d9a76fa4e369ce9bd1422c606155d92fdf81014426b41f5479c7faff-frame_00002.png" alt="img"></p><p><img src="https://pic.leetcode-cn.com/4fbddb60995860f7b4c74f915cf220ce164ee4043b1a60a635d22f969c9c0dbb-frame_00003.png" alt="img"></p><p><img src="https://pic.leetcode-cn.com/df42c567454065d283b99fb51ce74ba780c14e2193873b734a5c4266567465a8-frame_00004.png" alt="img"></p><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    private List&lt;List&lt;Integer&gt;&gt; ans = new ArrayList&lt;&gt;();</span><br><span class="line">    public List&lt;List&lt;Integer&gt;&gt; combine(int n, int k) &#123;</span><br><span class="line">        getCombine(n, k, 1, new ArrayList&lt;&gt;());</span><br><span class="line">        return ans;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    public void getCombine(int n, int k, int start, List&lt;Integer&gt; list) &#123;</span><br><span class="line">        if(k == 0) &#123;</span><br><span class="line">            ans.add(new ArrayList&lt;&gt;(list));</span><br><span class="line">            return;</span><br><span class="line">        &#125;</span><br><span class="line">        for(int i = start;i &lt;= n - k + 1;i++) &#123;</span><br><span class="line">            list.add(i);</span><br><span class="line">            getCombine(n, k - 1, i+1, list);</span><br><span class="line">            list.remove(list.size() - 1);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;组合&quot;&gt;&lt;a href=&quot;#组合&quot; class=&quot;headerlink&quot; title=&quot;组合&quot;&gt;&lt;/a&gt;组合&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot; class=&quot;headerlink&quot; title=&quot;1.题目内容&quot;&gt;&lt;/a&gt;1</summary>
      
    
    
    
    
    <category term="回溯" scheme="https://icheng281.github.io/tags/%E5%9B%9E%E6%BA%AF/"/>
    
  </entry>
  
  <entry>
    <title>组合总和</title>
    <link href="https://icheng281.github.io/2024/05/15/%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8C/"/>
    <id>https://icheng281.github.io/2024/05/15/%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8C/</id>
    <published>2024-05-15T02:36:27.000Z</published>
    <updated>2024-05-15T02:38:14.190Z</updated>
    
    <content type="html"><![CDATA[<h1 id="组合总和"><a href="#组合总和" class="headerlink" title="组合总和"></a>组合总和</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>给你一个 <strong>无重复元素</strong> 的整数数组 <code>candidates</code> 和一个目标整数 <code>target</code> ，找出 <code>candidates</code> 中可以使数字和为目标数 <code>target</code> 的 所有 <strong>不同组合</strong> ，并以列表形式返回。你可以按 <strong>任意顺序</strong> 返回这些组合。</p><p><code>candidates</code> 中的 <strong>同一个</strong> 数字可以 <strong>无限制重复被选取</strong> 。如果至少一个数字的被选数量不同，则两种组合是不同的。 </p><p>对于给定的输入，保证和为 <code>target</code> 的不同组合数少于 <code>150</code> 个。</p><p><strong>示例 1：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">输入：candidates = [2,3,6,7], target = 7</span><br><span class="line">输出：[[2,2,3],[7]]</span><br><span class="line">解释：</span><br><span class="line">2 和 3 可以形成一组候选，2 + 2 + 3 = 7 。注意 2 可以使用多次。</span><br><span class="line">7 也是一个候选， 7 = 7 。</span><br><span class="line">仅有这两种组合。</span><br></pre></td></tr></table></figure><p><strong>示例 2：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: candidates = [2,3,5], target = 8</span><br><span class="line">输出: [[2,2,2,2],[2,3,3],[3,5]]</span><br></pre></td></tr></table></figure><p><strong>示例 3：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: candidates = [2], target = 1</span><br><span class="line">输出: []</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= candidates.length &lt;= 30</code></li><li><code>2 &lt;= candidates[i] &lt;= 40</code></li><li><code>candidates</code> 的所有元素 <strong>互不相同</strong></li><li><code>1 &lt;= target &lt;= 40</code></li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="回溯"><a href="#回溯" class="headerlink" title="回溯"></a>回溯</h3><p><strong>思路及算法</strong></p><p>对于这类寻找所有可行解的题，我们都可以尝试用「搜索回溯」的方法来解决。</p><p>回到本题，我们定义递归函数 dfs(target,combine,idx) 表示当前在 candidates 数组的第 idx 位，还剩 target 要组合，已经组合的列表为 combine。递归的终止条件为 target≤0 或者 candidates 数组被全部用完。那么在当前的函数中，每次我们可以选择跳过不用第 idx 个数，即执行 dfs(target,combine,idx+1)。也可以选择使用第 idx 个数，即执行 dfs(target−candidates[idx],combine,idx)，注意到每个数字可以被无限制重复选取，因此搜索的下标仍为 idx。</p><p>更形象化地说，如果我们将整个搜索过程用一个树来表达，即如下图呈现，每次的搜索都会延伸出两个分叉，直到递归的终止条件，这样我们就能不重复且不遗漏地找到所有可行解：</p><p><img src="https://assets.leetcode-cn.com/solution-static/39/39_fig1.png" alt="fig1"></p><p>当然，搜索回溯的过程一定存在一些优秀的剪枝方法来使得程序运行得更快，而这里只给出了最朴素不含剪枝的写法，因此欢迎各位读者在评论区分享自己的见解。</p><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">class Solution &#123;</span><br><span class="line">public:</span><br><span class="line">    void dfs(vector&lt;int&gt;&amp; candidates, int target, vector&lt;vector&lt;int&gt;&gt;&amp; ans, vector&lt;int&gt;&amp; combine, int idx) &#123;</span><br><span class="line">        if (idx == candidates.size()) &#123;</span><br><span class="line">            return;</span><br><span class="line">        &#125;</span><br><span class="line">        if (target == 0) &#123;</span><br><span class="line">            ans.emplace_back(combine);</span><br><span class="line">            return;</span><br><span class="line">        &#125;</span><br><span class="line">        // 直接跳过</span><br><span class="line">        dfs(candidates, target, ans, combine, idx + 1);</span><br><span class="line">        // 选择当前数</span><br><span class="line">        if (target - candidates[idx] &gt;= 0) &#123;</span><br><span class="line">            combine.emplace_back(candidates[idx]);</span><br><span class="line">            dfs(candidates, target - candidates[idx], ans, combine, idx);</span><br><span class="line">            combine.pop_back();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    vector&lt;vector&lt;int&gt;&gt; combinationSum(vector&lt;int&gt;&amp; candidates, int target) &#123;</span><br><span class="line">        vector&lt;vector&lt;int&gt;&gt; ans;</span><br><span class="line">        vector&lt;int&gt; combine;</span><br><span class="line">        dfs(candidates, target, ans, combine, 0);</span><br><span class="line">        return ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    public List&lt;List&lt;Integer&gt;&gt; combinationSum(int[] candidates, int target) &#123;</span><br><span class="line">        List&lt;List&lt;Integer&gt;&gt; ans = new ArrayList&lt;List&lt;Integer&gt;&gt;();</span><br><span class="line">        List&lt;Integer&gt; combine = new ArrayList&lt;Integer&gt;();</span><br><span class="line">        dfs(candidates, target, ans, combine, 0);</span><br><span class="line">        return ans;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void dfs(int[] candidates, int target, List&lt;List&lt;Integer&gt;&gt; ans, List&lt;Integer&gt; combine, int idx) &#123;</span><br><span class="line">        if (idx == candidates.length) &#123;</span><br><span class="line">            return;</span><br><span class="line">        &#125;</span><br><span class="line">        if (target == 0) &#123;</span><br><span class="line">            ans.add(new ArrayList&lt;Integer&gt;(combine));</span><br><span class="line">            return;</span><br><span class="line">        &#125;</span><br><span class="line">        // 直接跳过</span><br><span class="line">        dfs(candidates, target, ans, combine, idx + 1);</span><br><span class="line">        // 选择当前数</span><br><span class="line">        if (target - candidates[idx] &gt;= 0) &#123;</span><br><span class="line">            combine.add(candidates[idx]);</span><br><span class="line">            dfs(candidates, target - candidates[idx], ans, combine, idx);</span><br><span class="line">            combine.remove(combine.size() - 1);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li><p>时间复杂度：O(S)，其中 S 为所有可行解的长度之和。从分析给出的搜索树我们可以看出时间复杂度取决于搜索树所有叶子节点的深度之和，即所有可行解的长度之和。在这题中，我们很难给出一个比较紧的上界，我们知道 O(n×2^n^) 是一个比较松的上界，即在这份代码中，n 个位置每次考虑选或者不选，如果符合条件，就加入答案的时间代价。但是实际运行的时候，因为不可能所有的解都满足条件，递归的时候我们还会用 target−candidates[idx]≥0 进行剪枝，所以实际运行情况是远远小于这个上界的。</p></li><li><p>空间复杂度：O(target)。除答案数组外，空间复杂度取决于递归的栈深度，在最差情况下需要递归 O(target) 层。</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;组合总和&quot;&gt;&lt;a href=&quot;#组合总和&quot; class=&quot;headerlink&quot; title=&quot;组合总和&quot;&gt;&lt;/a&gt;组合总和&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot; class=&quot;headerlink&quot; title=&quot;1.题目内</summary>
      
    
    
    
    
    <category term="回溯" scheme="https://icheng281.github.io/tags/%E5%9B%9E%E6%BA%AF/"/>
    
  </entry>
  
  <entry>
    <title>分割回文串</title>
    <link href="https://icheng281.github.io/2024/05/15/%E5%88%86%E5%89%B2%E5%9B%9E%E6%96%87%E4%B8%B2/"/>
    <id>https://icheng281.github.io/2024/05/15/%E5%88%86%E5%89%B2%E5%9B%9E%E6%96%87%E4%B8%B2/</id>
    <published>2024-05-15T02:07:27.000Z</published>
    <updated>2024-05-15T02:08:34.609Z</updated>
    
    <content type="html"><![CDATA[<h1 id="分割回文串"><a href="#分割回文串" class="headerlink" title="分割回文串"></a>分割回文串</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>给你一个字符串 <code>s</code>，请你将 <code>s</code> 分割成一些子串，使每个子串都是 <strong>回文串</strong> 。返回 <code>s</code> 所有可能的分割方案。</p><p><strong>示例 1：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：s = &quot;aab&quot;</span><br><span class="line">输出：[[&quot;a&quot;,&quot;a&quot;,&quot;b&quot;],[&quot;aa&quot;,&quot;b&quot;]]</span><br></pre></td></tr></table></figure><p><strong>示例 2：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：s = &quot;a&quot;</span><br><span class="line">输出：[[&quot;a&quot;]]</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= s.length &lt;= 16</code></li><li><code>s</code> 仅由小写英文字母组成</li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="回溯"><a href="#回溯" class="headerlink" title="回溯"></a>回溯</h3><p><strong>思路及算法</strong></p><p>由于需要求出字符串 s 的所有分割方案，因此我们考虑使用搜索 + 回溯的方法枚举所有可能的分割方法并进行判断。</p><p>假设我们当前搜索到字符串的第 iii 个字符，且 s[0..i−1] 位置的所有字符已经被分割成若干个回文串，并且分割结果被放入了答案数组 ans 中，那么我们就需要枚举下一个回文串的右边界 j，使得 s[i..j] 是一个回文串。</p><p>因此，我们可以从 i 开始，从小到大依次枚举 j。对于当前枚举的 j 值，我们使用双指针的方法判断 s[i..j] 是否为回文串：如果 s[i..j] 是回文串，那么就将其加入答案数组 ans 中，并以 j+1 作为新的 i 进行下一层搜索，并在未来的回溯时将 s[i..j] 从 ans 中移除。</p><p>如果我们已经搜索完了字符串的最后一个字符，那么就找到了一种满足要求的分割方法。</p><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">class Solution &#123;</span><br><span class="line">private:</span><br><span class="line">    vector&lt;vector&lt;int&gt;&gt; f;</span><br><span class="line">    vector&lt;vector&lt;string&gt;&gt; ret;</span><br><span class="line">    vector&lt;string&gt; ans;</span><br><span class="line">    int n;</span><br><span class="line"></span><br><span class="line">public:</span><br><span class="line">    void dfs(const string&amp; s, int i) &#123;</span><br><span class="line">        if (i == n) &#123;</span><br><span class="line">            ret.push_back(ans);</span><br><span class="line">            return;</span><br><span class="line">        &#125;</span><br><span class="line">        for (int j = i; j &lt; n; ++j) &#123;</span><br><span class="line">            if (f[i][j]) &#123;</span><br><span class="line">                ans.push_back(s.substr(i, j - i + 1));</span><br><span class="line">                dfs(s, j + 1);</span><br><span class="line">                ans.pop_back();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    vector&lt;vector&lt;string&gt;&gt; partition(string s) &#123;</span><br><span class="line">        n = s.size();</span><br><span class="line">        f.assign(n, vector&lt;int&gt;(n, true));</span><br><span class="line"></span><br><span class="line">        for (int i = n - 1; i &gt;= 0; --i) &#123;</span><br><span class="line">            for (int j = i + 1; j &lt; n; ++j) &#123;</span><br><span class="line">                f[i][j] = (s[i] == s[j]) &amp;&amp; f[i + 1][j - 1];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        dfs(s, 0);</span><br><span class="line">        return ret;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    boolean[][] f;</span><br><span class="line">    List&lt;List&lt;String&gt;&gt; ret = new ArrayList&lt;List&lt;String&gt;&gt;();</span><br><span class="line">    List&lt;String&gt; ans = new ArrayList&lt;String&gt;();</span><br><span class="line">    int n;</span><br><span class="line"></span><br><span class="line">    public List&lt;List&lt;String&gt;&gt; partition(String s) &#123;</span><br><span class="line">        n = s.length();</span><br><span class="line">        f = new boolean[n][n];</span><br><span class="line">        for (int i = 0; i &lt; n; ++i) &#123;</span><br><span class="line">            Arrays.fill(f[i], true);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        for (int i = n - 1; i &gt;= 0; --i) &#123;</span><br><span class="line">            for (int j = i + 1; j &lt; n; ++j) &#123;</span><br><span class="line">                f[i][j] = (s.charAt(i) == s.charAt(j)) &amp;&amp; f[i + 1][j - 1];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        dfs(s, 0);</span><br><span class="line">        return ret;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void dfs(String s, int i) &#123;</span><br><span class="line">        if (i == n) &#123;</span><br><span class="line">            ret.add(new ArrayList&lt;String&gt;(ans));</span><br><span class="line">            return;</span><br><span class="line">        &#125;</span><br><span class="line">        for (int j = i; j &lt; n; ++j) &#123;</span><br><span class="line">            if (f[i][j]) &#123;</span><br><span class="line">                ans.add(s.substring(i, j + 1));</span><br><span class="line">                dfs(s, j + 1);</span><br><span class="line">                ans.remove(ans.size() - 1);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li><p>时间复杂度：O(n⋅2^n^)，其中 n 是字符串 s 的长度。在最坏情况下，s 包含 n 个完全相同的字符，因此它的任意一种划分方法都满足要求。而长度为 n 的字符串的划分方案数为 2^n−1^&#x3D;O(2^n^)，每一种划分方法需要 O(n) 的时间求出对应的划分结果并放入答案，因此总时间复杂度为 O(n⋅2^n^)。尽管动态规划预处理需要 O(n^2^) 的时间，但在渐进意义下小于 O(n⋅2^n^)，因此可以忽略。</p></li><li><p>空间复杂度：O(n^2^)，这里不计算返回答案占用的空间。数组 f 需要使用的空间为 O(n^2^)，而在回溯的过程中，我们需要使用 O(n) 的栈空间以及 O(n)O(n)O(n) 的用来存储当前字符串分割方法的空间。由于 O(n) 在渐进意义下小于 O(n^2^)，因此空间复杂度为 O(n^2^)。</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;分割回文串&quot;&gt;&lt;a href=&quot;#分割回文串&quot; class=&quot;headerlink&quot; title=&quot;分割回文串&quot;&gt;&lt;/a&gt;分割回文串&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot; class=&quot;headerlink&quot; title=&quot;1</summary>
      
    
    
    
    
    <category term="回溯" scheme="https://icheng281.github.io/tags/%E5%9B%9E%E6%BA%AF/"/>
    
  </entry>
  
  <entry>
    <title>单词搜索</title>
    <link href="https://icheng281.github.io/2024/05/15/%E5%8D%95%E8%AF%8D%E6%90%9C%E7%B4%A2/"/>
    <id>https://icheng281.github.io/2024/05/15/%E5%8D%95%E8%AF%8D%E6%90%9C%E7%B4%A2/</id>
    <published>2024-05-15T02:03:41.000Z</published>
    <updated>2024-05-15T02:08:15.391Z</updated>
    
    <content type="html"><![CDATA[<h1 id="单词搜索"><a href="#单词搜索" class="headerlink" title="单词搜索"></a>单词搜索</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>给定一个 <code>m x n</code> 二维字符网格 <code>board</code> 和一个字符串单词 <code>word</code> 。如果 <code>word</code> 存在于网格中，返回 <code>true</code> ；否则，返回 <code>false</code> 。</p><p>单词必须按照字母顺序，通过相邻的单元格内的字母构成，其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。</p><p><strong>示例 1：</strong></p><p><img src="https://assets.leetcode.com/uploads/2020/11/04/word2.jpg" alt="img"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：board = [[&quot;A&quot;,&quot;B&quot;,&quot;C&quot;,&quot;E&quot;],[&quot;S&quot;,&quot;F&quot;,&quot;C&quot;,&quot;S&quot;],[&quot;A&quot;,&quot;D&quot;,&quot;E&quot;,&quot;E&quot;]], word = &quot;ABCCED&quot;</span><br><span class="line">输出：true</span><br></pre></td></tr></table></figure><p><strong>示例 2：</strong></p><p><img src="https://assets.leetcode.com/uploads/2020/11/04/word-1.jpg" alt="img"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：board = [[&quot;A&quot;,&quot;B&quot;,&quot;C&quot;,&quot;E&quot;],[&quot;S&quot;,&quot;F&quot;,&quot;C&quot;,&quot;S&quot;],[&quot;A&quot;,&quot;D&quot;,&quot;E&quot;,&quot;E&quot;]], word = &quot;SEE&quot;</span><br><span class="line">输出：true</span><br></pre></td></tr></table></figure><p><strong>示例 3：</strong></p><p><img src="https://assets.leetcode.com/uploads/2020/10/15/word3.jpg" alt="img"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：board = [[&quot;A&quot;,&quot;B&quot;,&quot;C&quot;,&quot;E&quot;],[&quot;S&quot;,&quot;F&quot;,&quot;C&quot;,&quot;S&quot;],[&quot;A&quot;,&quot;D&quot;,&quot;E&quot;,&quot;E&quot;]], word = &quot;ABCB&quot;</span><br><span class="line">输出：false</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>m == board.length</code></li><li><code>n = board[i].length</code></li><li><code>1 &lt;= m, n &lt;= 6</code></li><li><code>1 &lt;= word.length &lt;= 15</code></li><li><code>board</code> 和 <code>word</code> 仅由大小写英文字母组成</li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="回溯"><a href="#回溯" class="headerlink" title="回溯"></a>回溯</h3><p><strong>思路及算法</strong></p><p>设函数 check(i,j,k) 表示判断以网格的 (i,j) 位置出发，能否搜索到单词 word[k..]，其中 word[k..] 表示字符串 word 从第 k 个字符开始的后缀子串。如果能搜索到，则返回 true，反之返回 false。函数 check(i,j,k) 的执行步骤如下：</p><ul><li>如果 board[i] [j]≠s[k]，当前字符不匹配，直接返回 false。</li><li>如果当前已经访问到字符串的末尾，且对应字符依然匹配，此时直接返回 true。</li><li>否则，遍历当前位置的所有相邻位置。如果从某个相邻位置出发，能够搜索到子串 word[k+1..]，则返回 true，否则返回 false。</li><li>这样，我们对每一个位置 (i,j) 都调用函数 check(i,j,0) 进行检查：只要有一处返回 true，就说明网格中能够找到相应的单词，否则说明不能找到。</li></ul><p>为了防止重复遍历相同的位置，需要额外维护一个与 board 等大的 visited 数组，用于标识每个位置是否被访问过。每次遍历相邻位置时，需要跳过已经被访问的位置。</p><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    public boolean exist(char[][] board, String word) &#123;</span><br><span class="line">        int h = board.length, w = board[0].length;</span><br><span class="line">        boolean[][] visited = new boolean[h][w];</span><br><span class="line">        for (int i = 0; i &lt; h; i++) &#123;</span><br><span class="line">            for (int j = 0; j &lt; w; j++) &#123;</span><br><span class="line">                boolean flag = check(board, visited, i, j, word, 0);</span><br><span class="line">                if (flag) &#123;</span><br><span class="line">                    return true;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return false;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public boolean check(char[][] board, boolean[][] visited, int i, int j, String s, int k) &#123;</span><br><span class="line">        if (board[i][j] != s.charAt(k)) &#123;</span><br><span class="line">            return false;</span><br><span class="line">        &#125; else if (k == s.length() - 1) &#123;</span><br><span class="line">            return true;</span><br><span class="line">        &#125;</span><br><span class="line">        visited[i][j] = true;</span><br><span class="line">        int[][] directions = &#123;&#123;0, 1&#125;, &#123;0, -1&#125;, &#123;1, 0&#125;, &#123;-1, 0&#125;&#125;;</span><br><span class="line">        boolean result = false;</span><br><span class="line">        for (int[] dir : directions) &#123;</span><br><span class="line">            int newi = i + dir[0], newj = j + dir[1];</span><br><span class="line">            if (newi &gt;= 0 &amp;&amp; newi &lt; board.length &amp;&amp; newj &gt;= 0 &amp;&amp; newj &lt; board[0].length) &#123;</span><br><span class="line">                if (!visited[newi][newj]) &#123;</span><br><span class="line">                    boolean flag = check(board, visited, newi, newj, s, k + 1);</span><br><span class="line">                    if (flag) &#123;</span><br><span class="line">                        result = true;</span><br><span class="line">                        break;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        visited[i][j] = false;</span><br><span class="line">        return result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li><p>时间复杂度：一个非常宽松的上界为 O(MN⋅3^L^)，其中 M,N 为网格的长度与宽度，L 为字符串 word 的长度。在每次调用函数 check 时，除了第一次可以进入 4 个分支以外，其余时间我们最多会进入 3 个分支（因为每个位置只能使用一次，所以走过来的分支没法走回去）。由于单词长为 L，故 check(i,j,0) 的时间复杂度为 O(3^L^)，而我们要执行 O(MN) 次检查。然而，由于剪枝的存在，我们在遇到不匹配或已访问的字符时会提前退出，终止递归流程。因此，实际的时间复杂度会远远小于 Θ(MN⋅3^L^)。</p></li><li><p>空间复杂度：O(MN)。我们额外开辟了 O(MN) 的 visited 数组，同时栈的深度最大为 O(min⁡(L,MN))。</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;单词搜索&quot;&gt;&lt;a href=&quot;#单词搜索&quot; class=&quot;headerlink&quot; title=&quot;单词搜索&quot;&gt;&lt;/a&gt;单词搜索&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot; class=&quot;headerlink&quot; title=&quot;1.题目内</summary>
      
    
    
    
    
    <category term="回溯" scheme="https://icheng281.github.io/tags/%E5%9B%9E%E6%BA%AF/"/>
    
  </entry>
  
  <entry>
    <title>全排列</title>
    <link href="https://icheng281.github.io/2024/05/14/%E5%85%A8%E6%8E%92%E5%88%97/"/>
    <id>https://icheng281.github.io/2024/05/14/%E5%85%A8%E6%8E%92%E5%88%97/</id>
    <published>2024-05-14T02:25:14.000Z</published>
    <updated>2024-05-14T02:26:13.703Z</updated>
    
    <content type="html"><![CDATA[<h1 id="全排列"><a href="#全排列" class="headerlink" title="全排列"></a>全排列</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>给定一个不含重复数字的数组 <code>nums</code> ，返回其 <em>所有可能的全排列</em> 。你可以 <strong>按任意顺序</strong> 返回答案。</p><p><strong>示例 1：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [1,2,3]</span><br><span class="line">输出：[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]</span><br></pre></td></tr></table></figure><p><strong>示例 2：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [0,1]</span><br><span class="line">输出：[[0,1],[1,0]]</span><br></pre></td></tr></table></figure><p><strong>示例 3：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [1]</span><br><span class="line">输出：[[1]]</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= nums.length &lt;= 6</code></li><li><code>-10 &lt;= nums[i] &lt;= 10</code></li><li><code>nums</code> 中的所有整数 <strong>互不相同</strong></li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="回溯"><a href="#回溯" class="headerlink" title="回溯"></a>回溯</h3><p><strong>思路及算法</strong></p><p>这个问题可以看作有 n 个排列成一行的空格，我们需要从左往右依此填入题目给定的 n 个数，每个数只能使用一次。那么很直接的可以想到一种穷举的算法，即从左往右每一个位置都依此尝试填入一个数，看能不能填完这 n 个空格，在程序中我们可以用「回溯法」来模拟这个过程。</p><p>我们定义递归函数 backtrack(first,output) 表示从左往右填到第 first 个位置，当前排列为 output。 那么整个递归函数分为两个情况：</p><p>如果 first&#x3D;n，说明我们已经填完了 n 个位置（注意下标从 0 开始），找到了一个可行的解，我们将 output 放入答案数组中，递归结束。<br>如果 first&lt;n，我们要考虑这第 first 个位置我们要填哪个数。根据题目要求我们肯定不能填已经填过的数，因此很容易想到的一个处理手段是我们定义一个标记数组 vis 来标记已经填过的数，那么在填第 first 个数的时候我们遍历题目给定的 n 个数，如果这个数没有被标记过，我们就尝试填入，并将其标记，继续尝试填下一个位置，即调用函数 backtrack(first+1,output)。回溯的时候要撤销这一个位置填的数以及标记，并继续尝试其他没被标记过的数。<br>使用标记数组来处理填过的数是一个很直观的思路，但是可不可以去掉这个标记数组呢？毕竟标记数组也增加了我们算法的空间复杂度。</p><p>答案是可以的，我们可以将题目给定的 n 个数的数组 nums  划分成左右两个部分，左边的表示已经填过的数，右边表示待填的数，我们在回溯的时候只要动态维护这个数组即可。</p><p>具体来说，假设我们已经填到第 first 个位置，那么 nums 数组中 [0,first−1] 是已填过的数的集合，[first,n−1] 是待填的数的集合。我们肯定是尝试用 [first,n−1] 里的数去填第 first 个数，假设待填的数的下标为 iii，那么填完以后我们将第 i 个数和第 first 个数交换，即能使得在填第 first+1 个数的时候 nums 数组的 [0,first] 部分为已填过的数，[first+1,n−1] 为待填的数，回溯的时候交换回来即能完成撤销操作。</p><p>举个简单的例子，假设我们有 [2,5,8,9,10] 这 5 个数要填入，已经填到第 3 个位置，已经填了 [8,9] 两个数，那么这个数组目前为 [8,9 ∣ 2,5,10] 这样的状态，分隔符区分了左右两个部分。假设这个位置我们要填 10 这个数，为了维护数组，我们将 2 和 10 交换，即能使得数组继续保持分隔符左边的数已经填过，右边的待填 [8,9,10 ∣ 2,5] 。</p><p>当然善于思考的读者肯定已经发现这样生成的全排列并不是按字典序存储在答案数组中的，如果题目要求按字典序输出，那么请还是用标记数组或者其他方法。</p><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">class Solution &#123;</span><br><span class="line">public:</span><br><span class="line">    void backtrack(vector&lt;vector&lt;int&gt;&gt;&amp; res, vector&lt;int&gt;&amp; output, int first, int len)&#123;</span><br><span class="line">        // 所有数都填完了</span><br><span class="line">        if (first == len) &#123;</span><br><span class="line">            res.emplace_back(output);</span><br><span class="line">            return;</span><br><span class="line">        &#125;</span><br><span class="line">        for (int i = first; i &lt; len; ++i) &#123;</span><br><span class="line">            // 动态维护数组</span><br><span class="line">            swap(output[i], output[first]);</span><br><span class="line">            // 继续递归填下一个数</span><br><span class="line">            backtrack(res, output, first + 1, len);</span><br><span class="line">            // 撤销操作</span><br><span class="line">            swap(output[i], output[first]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    vector&lt;vector&lt;int&gt;&gt; permute(vector&lt;int&gt;&amp; nums) &#123;</span><br><span class="line">        vector&lt;vector&lt;int&gt; &gt; res;</span><br><span class="line">        backtrack(res, nums, 0, (int)nums.size());</span><br><span class="line">        return res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    public List&lt;List&lt;Integer&gt;&gt; permute(int[] nums) &#123;</span><br><span class="line">        List&lt;List&lt;Integer&gt;&gt; res = new ArrayList&lt;List&lt;Integer&gt;&gt;();</span><br><span class="line"></span><br><span class="line">        List&lt;Integer&gt; output = new ArrayList&lt;Integer&gt;();</span><br><span class="line">        for (int num : nums) &#123;</span><br><span class="line">            output.add(num);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        int n = nums.length;</span><br><span class="line">        backtrack(n, output, res, 0);</span><br><span class="line">        return res;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void backtrack(int n, List&lt;Integer&gt; output, List&lt;List&lt;Integer&gt;&gt; res, int first) &#123;</span><br><span class="line">        // 所有数都填完了</span><br><span class="line">        if (first == n) &#123;</span><br><span class="line">            res.add(new ArrayList&lt;Integer&gt;(output));</span><br><span class="line">        &#125;</span><br><span class="line">        for (int i = first; i &lt; n; i++) &#123;</span><br><span class="line">            // 动态维护数组</span><br><span class="line">            Collections.swap(output, first, i);</span><br><span class="line">            // 继续递归填下一个数</span><br><span class="line">            backtrack(n, output, res, first + 1);</span><br><span class="line">            // 撤销操作</span><br><span class="line">            Collections.swap(output, first, i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li>时间复杂度：O(n×n!)，其中 n 为序列的长度。</li></ul><ul><li>空间复杂度：O(n)，其中 n 为序列的长度。除答案数组以外，递归函数在递归过程中需要为每一层递归函数分配栈空间，所以这里需要额外的空间且该空间取决于递归的深度，这里可知递归调用深度为 O(n)。</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;全排列&quot;&gt;&lt;a href=&quot;#全排列&quot; class=&quot;headerlink&quot; title=&quot;全排列&quot;&gt;&lt;/a&gt;全排列&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot; class=&quot;headerlink&quot; title=&quot;1.题目内容&quot;&gt;&lt;</summary>
      
    
    
    
    
    <category term="回溯" scheme="https://icheng281.github.io/tags/%E5%9B%9E%E6%BA%AF/"/>
    
  </entry>
  
  <entry>
    <title>子集</title>
    <link href="https://icheng281.github.io/2024/05/14/%E5%AD%90%E9%9B%86/"/>
    <id>https://icheng281.github.io/2024/05/14/%E5%AD%90%E9%9B%86/</id>
    <published>2024-05-14T02:24:54.000Z</published>
    <updated>2024-05-14T02:26:34.663Z</updated>
    
    <content type="html"><![CDATA[<h1 id="子集"><a href="#子集" class="headerlink" title="子集"></a>子集</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>给你一个整数数组 <code>nums</code> ，数组中的元素 <strong>互不相同</strong> 。返回该数组所有可能的子集（幂集）。</p><p>解集 <strong>不能</strong> 包含重复的子集。你可以按 <strong>任意顺序</strong> 返回解集。</p><p><strong>示例 1：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [1,2,3]</span><br><span class="line">输出：[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]</span><br></pre></td></tr></table></figure><p><strong>示例 2：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [0]</span><br><span class="line">输出：[[],[0]]</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= nums.length &lt;= 10</code></li><li><code>-10 &lt;= nums[i] &lt;= 10</code></li><li><code>nums</code> 中的所有元素 <strong>互不相同</strong></li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h4 id="递归法实现子集枚举"><a href="#递归法实现子集枚举" class="headerlink" title="递归法实现子集枚举"></a>递归法实现子集枚举</h4><p><strong>思路及算法</strong></p><p>我们也可以用递归来实现子集枚举。</p><p>假设我们需要找到一个长度为 n 的序列 a 的所有子序列，代码框架是这样的：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">vector&lt;int&gt; t;</span><br><span class="line">void dfs(int cur, int n) &#123;</span><br><span class="line">    if (cur == n) &#123;</span><br><span class="line">        // 记录答案</span><br><span class="line">        // ...</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line">    // 考虑选择当前位置</span><br><span class="line">    t.push_back(cur);</span><br><span class="line">    dfs(cur + 1, n, k);</span><br><span class="line">    t.pop_back();</span><br><span class="line">    // 考虑不选择当前位置</span><br><span class="line">    dfs(cur + 1, n, k);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的代码中，dfs(cur,n) 参数表示当前位置是 cur，原序列总长度为 n。原序列的每个位置在答案序列中的状态有被选中和不被选中两种，我们用 t 数组存放已经被选出的数字。在进入 dfs(cur,n) 之前 [0,cur−1] 位置的状态是确定的，而 [cur,n−1] 内位置的状态是不确定的，dfs(cur,n) 需要确定 cur 位置的状态，然后求解子问题 dfs(cur+1,n)。对于 cur 位置，我们需要考虑 a[cur] 取或者不取，如果取，我们需要把 a[cur] 放入一个临时的答案数组中（即上面代码中的 t），再执行 dfs(cur+1,n)，执行结束后需要对 t 进行回溯；如果不取，则直接执行 dfs(cur+1,n)。在整个递归调用的过程中，cur 是从小到大递增的，当 cur 增加到 n 的时候，记录答案并终止递归。可以看出二进制枚举的时间复杂度是 O(2^n^)。</p><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">class Solution &#123;</span><br><span class="line">public:</span><br><span class="line">    vector&lt;int&gt; t;</span><br><span class="line">    vector&lt;vector&lt;int&gt;&gt; ans;</span><br><span class="line"></span><br><span class="line">    void dfs(int cur, vector&lt;int&gt;&amp; nums) &#123;</span><br><span class="line">        if (cur == nums.size()) &#123;</span><br><span class="line">            ans.push_back(t);</span><br><span class="line">            return;</span><br><span class="line">        &#125;</span><br><span class="line">        t.push_back(nums[cur]);</span><br><span class="line">        dfs(cur + 1, nums);</span><br><span class="line">        t.pop_back();</span><br><span class="line">        dfs(cur + 1, nums);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    vector&lt;vector&lt;int&gt;&gt; subsets(vector&lt;int&gt;&amp; nums) &#123;</span><br><span class="line">        dfs(0, nums);</span><br><span class="line">        return ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    List&lt;Integer&gt; t = new ArrayList&lt;Integer&gt;();</span><br><span class="line">    List&lt;List&lt;Integer&gt;&gt; ans = new ArrayList&lt;List&lt;Integer&gt;&gt;();</span><br><span class="line"></span><br><span class="line">    public List&lt;List&lt;Integer&gt;&gt; subsets(int[] nums) &#123;</span><br><span class="line">        dfs(0, nums);</span><br><span class="line">        return ans;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void dfs(int cur, int[] nums) &#123;</span><br><span class="line">        if (cur == nums.length) &#123;</span><br><span class="line">            ans.add(new ArrayList&lt;Integer&gt;(t));</span><br><span class="line">            return;</span><br><span class="line">        &#125;</span><br><span class="line">        t.add(nums[cur]);</span><br><span class="line">        dfs(cur + 1, nums);</span><br><span class="line">        t.remove(t.size() - 1);</span><br><span class="line">        dfs(cur + 1, nums);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li><p>时间复杂度：O(n×2^n^)。一共 2^n^个状态，每种状态需要 O(n) 的时间来构造子集。</p></li><li><p>空间复杂度：O(n)。临时数组 t 的空间代价是 O(n)，递归时栈空间的代价为 O(n)。</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;子集&quot;&gt;&lt;a href=&quot;#子集&quot; class=&quot;headerlink&quot; title=&quot;子集&quot;&gt;&lt;/a&gt;子集&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot; class=&quot;headerlink&quot; title=&quot;1.题目内容&quot;&gt;&lt;/a&gt;1</summary>
      
    
    
    
    
    <category term="回溯" scheme="https://icheng281.github.io/tags/%E5%9B%9E%E6%BA%AF/"/>
    
  </entry>
  
  <entry>
    <title>电话号码的字母组合</title>
    <link href="https://icheng281.github.io/2024/05/14/%E7%94%B5%E8%AF%9D%E5%8F%B7%E7%A0%81%E7%9A%84%E5%AD%97%E6%AF%8D%E7%BB%84%E5%90%88/"/>
    <id>https://icheng281.github.io/2024/05/14/%E7%94%B5%E8%AF%9D%E5%8F%B7%E7%A0%81%E7%9A%84%E5%AD%97%E6%AF%8D%E7%BB%84%E5%90%88/</id>
    <published>2024-05-14T02:24:02.000Z</published>
    <updated>2024-05-14T02:25:48.538Z</updated>
    
    <content type="html"><![CDATA[<h1 id="电话号码的字母组合"><a href="#电话号码的字母组合" class="headerlink" title="电话号码的字母组合"></a>电话号码的字母组合</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>给定一个仅包含数字 <code>2-9</code> 的字符串，返回所有它能表示的字母组合。答案可以按 <strong>任意顺序</strong> 返回。</p><p>给出数字到字母的映射如下（与电话按键相同）。注意 1 不对应任何字母。</p><p><img src="https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2021/11/09/200px-telephone-keypad2svg.png" alt="img"></p><p><strong>示例 1：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：digits = &quot;23&quot;</span><br><span class="line">输出：[&quot;ad&quot;,&quot;ae&quot;,&quot;af&quot;,&quot;bd&quot;,&quot;be&quot;,&quot;bf&quot;,&quot;cd&quot;,&quot;ce&quot;,&quot;cf&quot;]</span><br></pre></td></tr></table></figure><p><strong>示例 2：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：digits = &quot;&quot;</span><br><span class="line">输出：[]</span><br></pre></td></tr></table></figure><p><strong>示例 3：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：digits = &quot;2&quot;</span><br><span class="line">输出：[&quot;a&quot;,&quot;b&quot;,&quot;c&quot;]</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>0 &lt;= digits.length &lt;= 4</code></li><li><code>digits[i]</code> 是范围 <code>[&#39;2&#39;, &#39;9&#39;]</code> 的一个数字。</li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="回溯"><a href="#回溯" class="headerlink" title="回溯"></a>回溯</h3><p><strong>思路及算法</strong></p><p>首先使用哈希表存储每个数字对应的所有可能的字母，然后进行回溯操作。</p><p>回溯过程中维护一个字符串，表示已有的字母排列（如果未遍历完电话号码的所有数字，则已有的字母排列是不完整的）。该字符串初始为空。每次取电话号码的一位数字，从哈希表中获得该数字对应的所有可能的字母，并将其中的一个字母插入到已有的字母排列后面，然后继续处理电话号码的后一位数字，直到处理完电话号码中的所有数字，即得到一个完整的字母排列。然后进行回退操作，遍历其余的字母排列。</p><p>回溯算法用于寻找所有的可行解，如果发现一个解不可行，则会舍弃不可行的解。在这道题中，由于每个数字对应的每个字母都可能进入字母组合，因此不存在不可行的解，直接穷举所有的解即可。</p><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    public List&lt;String&gt; letterCombinations(String digits) &#123;</span><br><span class="line">        List&lt;String&gt; combinations = new ArrayList&lt;String&gt;();</span><br><span class="line">        if (digits.length() == 0) &#123;</span><br><span class="line">            return combinations;</span><br><span class="line">        &#125;</span><br><span class="line">        Map&lt;Character, String&gt; phoneMap = new HashMap&lt;Character, String&gt;() &#123;&#123;</span><br><span class="line">            put(&#x27;2&#x27;, &quot;abc&quot;);</span><br><span class="line">            put(&#x27;3&#x27;, &quot;def&quot;);</span><br><span class="line">            put(&#x27;4&#x27;, &quot;ghi&quot;);</span><br><span class="line">            put(&#x27;5&#x27;, &quot;jkl&quot;);</span><br><span class="line">            put(&#x27;6&#x27;, &quot;mno&quot;);</span><br><span class="line">            put(&#x27;7&#x27;, &quot;pqrs&quot;);</span><br><span class="line">            put(&#x27;8&#x27;, &quot;tuv&quot;);</span><br><span class="line">            put(&#x27;9&#x27;, &quot;wxyz&quot;);</span><br><span class="line">        &#125;&#125;;</span><br><span class="line">        backtrack(combinations, phoneMap, digits, 0, new StringBuffer());</span><br><span class="line">        return combinations;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void backtrack(List&lt;String&gt; combinations, Map&lt;Character, String&gt; phoneMap, String digits, int index, StringBuffer combination) &#123;</span><br><span class="line">        if (index == digits.length()) &#123;</span><br><span class="line">            combinations.add(combination.toString());</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            char digit = digits.charAt(index);</span><br><span class="line">            String letters = phoneMap.get(digit);</span><br><span class="line">            int lettersCount = letters.length();</span><br><span class="line">            for (int i = 0; i &lt; lettersCount; i++) &#123;</span><br><span class="line">                combination.append(letters.charAt(i));</span><br><span class="line">                backtrack(combinations, phoneMap, digits, index + 1, combination);</span><br><span class="line">                combination.deleteCharAt(index);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">class Solution &#123;</span><br><span class="line">public:</span><br><span class="line">    vector&lt;string&gt; letterCombinations(string digits) &#123;</span><br><span class="line">        vector&lt;string&gt; combinations;</span><br><span class="line">        if (digits.empty()) &#123;</span><br><span class="line">            return combinations;</span><br><span class="line">        &#125;</span><br><span class="line">        unordered_map&lt;char, string&gt; phoneMap&#123;</span><br><span class="line">            &#123;&#x27;2&#x27;, &quot;abc&quot;&#125;,</span><br><span class="line">            &#123;&#x27;3&#x27;, &quot;def&quot;&#125;,</span><br><span class="line">            &#123;&#x27;4&#x27;, &quot;ghi&quot;&#125;,</span><br><span class="line">            &#123;&#x27;5&#x27;, &quot;jkl&quot;&#125;,</span><br><span class="line">            &#123;&#x27;6&#x27;, &quot;mno&quot;&#125;,</span><br><span class="line">            &#123;&#x27;7&#x27;, &quot;pqrs&quot;&#125;,</span><br><span class="line">            &#123;&#x27;8&#x27;, &quot;tuv&quot;&#125;,</span><br><span class="line">            &#123;&#x27;9&#x27;, &quot;wxyz&quot;&#125;</span><br><span class="line">        &#125;;</span><br><span class="line">        string combination;</span><br><span class="line">        backtrack(combinations, phoneMap, digits, 0, combination);</span><br><span class="line">        return combinations;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    void backtrack(vector&lt;string&gt;&amp; combinations, const unordered_map&lt;char, string&gt;&amp; phoneMap, const string&amp; digits, int index, string&amp; combination) &#123;</span><br><span class="line">        if (index == digits.length()) &#123;</span><br><span class="line">            combinations.push_back(combination);</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            char digit = digits[index];</span><br><span class="line">            const string&amp; letters = phoneMap.at(digit);</span><br><span class="line">            for (const char&amp; letter: letters) &#123;</span><br><span class="line">                combination.push_back(letter);</span><br><span class="line">                backtrack(combinations, phoneMap, digits, index + 1, combination);</span><br><span class="line">                combination.pop_back();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li><p>时间复杂度：O(3^m^×4^n^)，其中 m 是输入中对应 3 个字母的数字个数（包括数字 2、3、4、5、6、8），n 是输入中对应 4 个字母的数字个数（包括数字 7、9），m+n 是输入数字的总个数。当输入包含 m 个对应 3 个字母的数字和 n 个对应 4 个字母的数字时，不同的字母组合一共有 3^m^×4^n^ 种，需要遍历每一种字母组合。</p></li><li><p>空间复杂度：O(m+n)，其中 m 是输入中对应 3 个字母的数字个数，n 是输入中对应 4 个字母的数字个数，m+n 是输入数字的总个数。除了返回值以外，空间复杂度主要取决于哈希表以及回溯过程中的递归调用层数，哈希表的大小与输入无关，可以看成常数，递归调用层数最大为 m+n。</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;电话号码的字母组合&quot;&gt;&lt;a href=&quot;#电话号码的字母组合&quot; class=&quot;headerlink&quot; title=&quot;电话号码的字母组合&quot;&gt;&lt;/a&gt;电话号码的字母组合&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot; class=&quot;head</summary>
      
    
    
    
    
    <category term="回溯" scheme="https://icheng281.github.io/tags/%E5%9B%9E%E6%BA%AF/"/>
    
  </entry>
  
  <entry>
    <title>单词搜索II</title>
    <link href="https://icheng281.github.io/2024/05/13/%E5%8D%95%E8%AF%8D%E6%90%9C%E7%B4%A2II/"/>
    <id>https://icheng281.github.io/2024/05/13/%E5%8D%95%E8%AF%8D%E6%90%9C%E7%B4%A2II/</id>
    <published>2024-05-13T02:14:12.000Z</published>
    <updated>2024-05-13T02:16:38.448Z</updated>
    
    <content type="html"><![CDATA[<h1 id="单词搜索II"><a href="#单词搜索II" class="headerlink" title="单词搜索II"></a>单词搜索II</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>给定一个 <code>m x n</code> 二维字符网格 <code>board</code> 和一个单词（字符串）列表 <code>words</code>， <em>返回所有二维网格上的单词</em> 。</p><p>单词必须按照字母顺序，通过 <strong>相邻的单元格</strong> 内的字母构成，其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。</p><p><strong>示例 1：</strong></p><p><img src="https://assets.leetcode.com/uploads/2020/11/07/search1.jpg" alt="img"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：board = [[&quot;o&quot;,&quot;a&quot;,&quot;a&quot;,&quot;n&quot;],[&quot;e&quot;,&quot;t&quot;,&quot;a&quot;,&quot;e&quot;],[&quot;i&quot;,&quot;h&quot;,&quot;k&quot;,&quot;r&quot;],[&quot;i&quot;,&quot;f&quot;,&quot;l&quot;,&quot;v&quot;]], words = [&quot;oath&quot;,&quot;pea&quot;,&quot;eat&quot;,&quot;rain&quot;]</span><br><span class="line">输出：[&quot;eat&quot;,&quot;oath&quot;]</span><br></pre></td></tr></table></figure><p><strong>示例 2：</strong></p><p><img src="https://assets.leetcode.com/uploads/2020/11/07/search2.jpg" alt="img"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：board = [[&quot;a&quot;,&quot;b&quot;],[&quot;c&quot;,&quot;d&quot;]], words = [&quot;abcb&quot;]</span><br><span class="line">输出：[]</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>m == board.length</code></li><li><code>n == board[i].length</code></li><li><code>1 &lt;= m, n &lt;= 12</code></li><li><code>board[i][j]</code> 是一个小写英文字母</li><li><code>1 &lt;= words.length &lt;= 3 * 104</code></li><li><code>1 &lt;= words[i].length &lt;= 10</code></li><li><code>words[i]</code> 由小写英文字母组成</li><li><code>words</code> 中的所有字符串互不相同</li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="回溯-字典树"><a href="#回溯-字典树" class="headerlink" title="回溯+字典树"></a>回溯+字典树</h3><p><strong>思路及算法</strong></p><p>根据题意，我们需要逐个遍历二维网格中的每一个单元格；然后搜索从该单元格出发的所有路径，找到其中对应 words 中的单词的路径。因为这是一个回溯的过程，所以我们有如下算法：</p><ul><li><p>遍历二维网格中的所有单元格。</p></li><li><p>深度优先搜索所有从当前正在遍历的单元格出发的、由相邻且不重复的单元格组成的路径。因为题目要求同一个单元格内的字母在一个单词中不能被重复使用；所以我们在深度优先搜索的过程中，每经过一个单元格，都将该单元格的字母临时修改为特殊字符（例如 #），以避免再次经过该单元格。</p></li><li><p>如果当前路径是 words 中的单词，则将其添加到结果集中。如果当前路径是 words 中任意一个单词的前缀，则继续搜索；反之，如果当前路径不是 words 中任意一个单词的前缀，则剪枝。我们可以将 words 中的所有字符串先添加到前缀树中，而后用 O(∣S∣) 的时间复杂度查询当前路径是否为 words 中任意一个单词的前缀。</p></li></ul><p>在具体实现中，我们需要注意如下情况：</p><ul><li><p>因为同一个单词可能在多个不同的路径中出现，所以我们需要使用哈希集合对结果集去重。</p></li><li><p>在回溯的过程中，我们不需要每一步都判断完整的当前路径是否是 words 中任意一个单词的前缀；而是可以记录下路径中每个单元格所对应的前缀树结点，每次只需要判断新增单元格的字母是否是上一个单元格对应前缀树结点的子结点即可。</p></li></ul><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    int[][] dirs = &#123;&#123;1, 0&#125;, &#123;-1, 0&#125;, &#123;0, 1&#125;, &#123;0, -1&#125;&#125;;</span><br><span class="line"></span><br><span class="line">    public List&lt;String&gt; findWords(char[][] board, String[] words) &#123;</span><br><span class="line">        Trie trie = new Trie();</span><br><span class="line">        for (String word : words) &#123;</span><br><span class="line">            trie.insert(word);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        Set&lt;String&gt; ans = new HashSet&lt;String&gt;();</span><br><span class="line">        for (int i = 0; i &lt; board.length; ++i) &#123;</span><br><span class="line">            for (int j = 0; j &lt; board[0].length; ++j) &#123;</span><br><span class="line">                dfs(board, trie, i, j, ans);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        return new ArrayList&lt;String&gt;(ans);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void dfs(char[][] board, Trie now, int i1, int j1, Set&lt;String&gt; ans) &#123;</span><br><span class="line">        if (!now.children.containsKey(board[i1][j1])) &#123;</span><br><span class="line">            return;</span><br><span class="line">        &#125;</span><br><span class="line">        char ch = board[i1][j1];</span><br><span class="line">        now = now.children.get(ch);</span><br><span class="line">        if (!&quot;&quot;.equals(now.word)) &#123;</span><br><span class="line">            ans.add(now.word);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        board[i1][j1] = &#x27;#&#x27;;</span><br><span class="line">        for (int[] dir : dirs) &#123;</span><br><span class="line">            int i2 = i1 + dir[0], j2 = j1 + dir[1];</span><br><span class="line">            if (i2 &gt;= 0 &amp;&amp; i2 &lt; board.length &amp;&amp; j2 &gt;= 0 &amp;&amp; j2 &lt; board[0].length) &#123;</span><br><span class="line">                dfs(board, now, i2, j2, ans);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        board[i1][j1] = ch;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class Trie &#123;</span><br><span class="line">    String word;</span><br><span class="line">    Map&lt;Character, Trie&gt; children;</span><br><span class="line">    boolean isWord;</span><br><span class="line"></span><br><span class="line">    public Trie() &#123;</span><br><span class="line">        this.word = &quot;&quot;;</span><br><span class="line">        this.children = new HashMap&lt;Character, Trie&gt;();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void insert(String word) &#123;</span><br><span class="line">        Trie cur = this;</span><br><span class="line">        for (int i = 0; i &lt; word.length(); ++i) &#123;</span><br><span class="line">            char c = word.charAt(i);</span><br><span class="line">            if (!cur.children.containsKey(c)) &#123;</span><br><span class="line">                cur.children.put(c, new Trie());</span><br><span class="line">            &#125;</span><br><span class="line">            cur = cur.children.get(c);</span><br><span class="line">        &#125;</span><br><span class="line">        cur.word = word;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">struct TrieNode &#123;</span><br><span class="line">    string word;</span><br><span class="line">    unordered_map&lt;char,TrieNode *&gt; children;</span><br><span class="line">    TrieNode() &#123;</span><br><span class="line">        this-&gt;word = &quot;&quot;;</span><br><span class="line">    &#125;   </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">void insertTrie(TrieNode * root,const string &amp; word) &#123;</span><br><span class="line">    TrieNode * node = root;</span><br><span class="line">    for (auto c : word)&#123;</span><br><span class="line">        if (!node-&gt;children.count(c)) &#123;</span><br><span class="line">            node-&gt;children[c] = new TrieNode();</span><br><span class="line">        &#125;</span><br><span class="line">        node = node-&gt;children[c];</span><br><span class="line">    &#125;</span><br><span class="line">    node-&gt;word = word;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class Solution &#123;</span><br><span class="line">public:</span><br><span class="line">    int dirs[4][2] = &#123;&#123;1, 0&#125;, &#123;-1, 0&#125;, &#123;0, 1&#125;, &#123;0, -1&#125;&#125;;</span><br><span class="line"></span><br><span class="line">    bool dfs(vector&lt;vector&lt;char&gt;&gt;&amp; board, int x, int y, TrieNode * root, set&lt;string&gt; &amp; res) &#123;</span><br><span class="line">        char ch = board[x][y];        </span><br><span class="line">        if (!root-&gt;children.count(ch)) &#123;</span><br><span class="line">            return false;</span><br><span class="line">        &#125;</span><br><span class="line">        root = root-&gt;children[ch];</span><br><span class="line">        if (root-&gt;word.size() &gt; 0) &#123;</span><br><span class="line">            res.insert(root-&gt;word);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        board[x][y] = &#x27;#&#x27;;</span><br><span class="line">        for (int i = 0; i &lt; 4; ++i) &#123;</span><br><span class="line">            int nx = x + dirs[i][0];</span><br><span class="line">            int ny = y + dirs[i][1];</span><br><span class="line">            if (nx &gt;= 0 &amp;&amp; nx &lt; board.size() &amp;&amp; ny &gt;= 0 &amp;&amp; ny &lt; board[0].size()) &#123;</span><br><span class="line">                if (board[nx][ny] != &#x27;#&#x27;) &#123;</span><br><span class="line">                    dfs(board, nx, ny, root,res);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        board[x][y] = ch;</span><br><span class="line"></span><br><span class="line">        return true;      </span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    vector&lt;string&gt; findWords(vector&lt;vector&lt;char&gt;&gt; &amp; board, vector&lt;string&gt; &amp; words) &#123;</span><br><span class="line">        TrieNode * root = new TrieNode();</span><br><span class="line">        set&lt;string&gt; res;</span><br><span class="line">        vector&lt;string&gt; ans;</span><br><span class="line"></span><br><span class="line">        for (auto &amp; word: words)&#123;</span><br><span class="line">            insertTrie(root,word);</span><br><span class="line">        &#125;</span><br><span class="line">        for (int i = 0; i &lt; board.size(); ++i) &#123;</span><br><span class="line">            for (int j = 0; j &lt; board[0].size(); ++j) &#123;</span><br><span class="line">                dfs(board, i, j, root, res);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;        </span><br><span class="line">        for (auto &amp; word: res) &#123;</span><br><span class="line">            ans.emplace_back(word);</span><br><span class="line">        &#125;</span><br><span class="line">        return ans;        </span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li><p>时间复杂度：O(m×n×3^l−1^)，其中 m 是二维网格的高度，n 是二维网格的宽度，l 是最长单词的长度。我们需要遍历 m×n 个单元格，每个单元格最多需要遍历 4×3^l−1^ 条路径。</p></li><li><p>空间复杂度：O(k×l)，其中 k 是 words 的长度，l 是最长单词的长度。最坏情况下，我们需要 O(k×l) 用于存储前缀树。</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;单词搜索II&quot;&gt;&lt;a href=&quot;#单词搜索II&quot; class=&quot;headerlink&quot; title=&quot;单词搜索II&quot;&gt;&lt;/a&gt;单词搜索II&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot; class=&quot;headerlink&quot; titl</summary>
      
    
    
    
    
    <category term="数据结构-图" scheme="https://icheng281.github.io/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E5%9B%BE/"/>
    
  </entry>
  
  <entry>
    <title>添加与搜索单词</title>
    <link href="https://icheng281.github.io/2024/05/13/%E6%B7%BB%E5%8A%A0%E4%B8%8E%E6%90%9C%E7%B4%A2%E5%8D%95%E8%AF%8D/"/>
    <id>https://icheng281.github.io/2024/05/13/%E6%B7%BB%E5%8A%A0%E4%B8%8E%E6%90%9C%E7%B4%A2%E5%8D%95%E8%AF%8D/</id>
    <published>2024-05-13T02:13:47.000Z</published>
    <updated>2024-05-13T02:15:42.284Z</updated>
    
    <content type="html"><![CDATA[<h1 id="添加与搜索单词"><a href="#添加与搜索单词" class="headerlink" title="添加与搜索单词"></a>添加与搜索单词</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>请你设计一个数据结构，支持 添加新单词 和 查找字符串是否与任何先前添加的字符串匹配 。</p><p>实现词典类 <code>WordDictionary</code> ：</p><ul><li><code>WordDictionary()</code> 初始化词典对象</li><li><code>void addWord(word)</code> 将 <code>word</code> 添加到数据结构中，之后可以对它进行匹配</li><li><code>bool search(word)</code> 如果数据结构中存在字符串与 <code>word</code> 匹配，则返回 <code>true</code> ；否则，返回 <code>false</code> 。<code>word</code> 中可能包含一些 <code>&#39;.&#39;</code> ，每个 <code>.</code> 都可以表示任何一个字母。</li></ul><p><strong>示例：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">输入：</span><br><span class="line">[&quot;WordDictionary&quot;,&quot;addWord&quot;,&quot;addWord&quot;,&quot;addWord&quot;,&quot;search&quot;,&quot;search&quot;,&quot;search&quot;,&quot;search&quot;]</span><br><span class="line">[[],[&quot;bad&quot;],[&quot;dad&quot;],[&quot;mad&quot;],[&quot;pad&quot;],[&quot;bad&quot;],[&quot;.ad&quot;],[&quot;b..&quot;]]</span><br><span class="line">输出：</span><br><span class="line">[null,null,null,null,false,true,true,true]</span><br><span class="line"></span><br><span class="line">解释：</span><br><span class="line">WordDictionary wordDictionary = new WordDictionary();</span><br><span class="line">wordDictionary.addWord(&quot;bad&quot;);</span><br><span class="line">wordDictionary.addWord(&quot;dad&quot;);</span><br><span class="line">wordDictionary.addWord(&quot;mad&quot;);</span><br><span class="line">wordDictionary.search(&quot;pad&quot;); // 返回 False</span><br><span class="line">wordDictionary.search(&quot;bad&quot;); // 返回 True</span><br><span class="line">wordDictionary.search(&quot;.ad&quot;); // 返回 True</span><br><span class="line">wordDictionary.search(&quot;b..&quot;); // 返回 True</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= word.length &lt;= 25</code></li><li><code>addWord</code> 中的 <code>word</code> 由小写英文字母组成</li><li><code>search</code> 中的 <code>word</code> 由 ‘.’ 或小写英文字母组成</li><li>最多调用 <code>104</code> 次 <code>addWord</code> 和 <code>search</code></li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="字典树"><a href="#字典树" class="headerlink" title="字典树"></a>字典树</h3><p><strong>思路及算法</strong></p><p>根据题意，WordDictionary 类需要支持添加单词和搜索单词的操作，可以使用字典树实现。</p><p>对于添加单词，将单词添加到字典树中即可。</p><p>对于搜索单词，从字典树的根结点开始搜索。由于待搜索的单词可能包含点号，因此在搜索过程中需要考虑点号的处理。对于当前字符是字母和点号的情况，分别按照如下方式处理：</p><ul><li><p>如果当前字符是字母，则判断当前字符对应的子结点是否存在，如果子结点存在则移动到子结点，继续搜索下一个字符，如果子结点不存在则说明单词不存在，返回 false；</p></li><li><p>如果当前字符是点号，由于点号可以表示任何字母，因此需要对当前结点的所有非空子结点继续搜索下一个字符。</p></li></ul><p>重复上述步骤，直到返回 false\text{false}false 或搜索完给定单词的最后一个字符。</p><p>如果搜索完给定的单词的最后一个字符，则当搜索到的最后一个结点的 isEnd 为 true 时，给定的单词存在。</p><p>特别地，当搜索到点号时，只要存在一个非空子结点可以搜索到给定的单词，即返回 true。</p><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class WordDictionary &#123;</span><br><span class="line">    private Trie root;</span><br><span class="line"></span><br><span class="line">    public WordDictionary() &#123;</span><br><span class="line">        root = new Trie();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    public void addWord(String word) &#123;</span><br><span class="line">        root.insert(word);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    public boolean search(String word) &#123;</span><br><span class="line">        return dfs(word, 0, root);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private boolean dfs(String word, int index, Trie node) &#123;</span><br><span class="line">        if (index == word.length()) &#123;</span><br><span class="line">            return node.isEnd();</span><br><span class="line">        &#125;</span><br><span class="line">        char ch = word.charAt(index);</span><br><span class="line">        if (Character.isLetter(ch)) &#123;</span><br><span class="line">            int childIndex = ch - &#x27;a&#x27;;</span><br><span class="line">            Trie child = node.getChildren()[childIndex];</span><br><span class="line">            if (child != null &amp;&amp; dfs(word, index + 1, child)) &#123;</span><br><span class="line">                return true;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            for (int i = 0; i &lt; 26; i++) &#123;</span><br><span class="line">                Trie child = node.getChildren()[i];</span><br><span class="line">                if (child != null &amp;&amp; dfs(word, index + 1, child)) &#123;</span><br><span class="line">                    return true;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return false;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class Trie &#123;</span><br><span class="line">    private Trie[] children;</span><br><span class="line">    private boolean isEnd;</span><br><span class="line"></span><br><span class="line">    public Trie() &#123;</span><br><span class="line">        children = new Trie[26];</span><br><span class="line">        isEnd = false;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    public void insert(String word) &#123;</span><br><span class="line">        Trie node = this;</span><br><span class="line">        for (int i = 0; i &lt; word.length(); i++) &#123;</span><br><span class="line">            char ch = word.charAt(i);</span><br><span class="line">            int index = ch - &#x27;a&#x27;;</span><br><span class="line">            if (node.children[index] == null) &#123;</span><br><span class="line">                node.children[index] = new Trie();</span><br><span class="line">            &#125;</span><br><span class="line">            node = node.children[index];</span><br><span class="line">        &#125;</span><br><span class="line">        node.isEnd = true;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public Trie[] getChildren() &#123;</span><br><span class="line">        return children;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public boolean isEnd() &#123;</span><br><span class="line">        return isEnd;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">struct TrieNode&#123;</span><br><span class="line">    vector&lt;TrieNode *&gt; child;</span><br><span class="line">    bool isEnd;</span><br><span class="line">    TrieNode() &#123;</span><br><span class="line">        this-&gt;child = vector&lt;TrieNode *&gt;(26,nullptr);</span><br><span class="line">        this-&gt;isEnd = false;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">void insert(TrieNode * root, const string &amp; word) &#123;</span><br><span class="line">    TrieNode * node = root;</span><br><span class="line">    for (auto c : word) &#123;</span><br><span class="line">        if (node-&gt;child[c - &#x27;a&#x27;] == nullptr) &#123;</span><br><span class="line">            node-&gt;child[c - &#x27;a&#x27;] = new TrieNode();</span><br><span class="line">        &#125;</span><br><span class="line">        node = node-&gt;child[c - &#x27;a&#x27;];</span><br><span class="line">    &#125;</span><br><span class="line">    node-&gt;isEnd = true;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class WordDictionary &#123;</span><br><span class="line">public:</span><br><span class="line">    WordDictionary() &#123;</span><br><span class="line">        trie = new TrieNode();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    void addWord(string word) &#123;</span><br><span class="line">        insert(trie,word);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    bool search(string word) &#123;</span><br><span class="line">        return dfs(word, 0, trie);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    bool dfs(const string &amp; word,int index,TrieNode * node) &#123;</span><br><span class="line">　　　　if (index == word.size()) &#123;</span><br><span class="line">            return node-&gt;isEnd;    </span><br><span class="line">        &#125;</span><br><span class="line">        char ch = word[index];</span><br><span class="line">        if (ch &gt;= &#x27;a&#x27; &amp;&amp; ch &lt;= &#x27;z&#x27;) &#123;</span><br><span class="line">            TrieNode * child = node-&gt;child[ch - &#x27;a&#x27;];</span><br><span class="line">            if (child != nullptr &amp;&amp; dfs(word, index + 1, child)) &#123;</span><br><span class="line">                return true;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; else if (ch == &#x27;.&#x27;) &#123;</span><br><span class="line">            for (int i = 0; i &lt; 26; i++) &#123;</span><br><span class="line">                TrieNode * child = node-&gt;child[i];</span><br><span class="line">                if (child != nullptr &amp;&amp; dfs(word, index + 1, child)) &#123;</span><br><span class="line">                    return true;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return false;</span><br><span class="line">    &#125;</span><br><span class="line">private:</span><br><span class="line">    TrieNode * trie;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li><p>时间复杂度：初始化为 O(1)，添加单词为 O(∣S∣)，搜索单词为 O(∣Σ∣∣S∣)，其中 ∣S∣ 是每次添加或搜索的单词的长度，Σ 是字符集，这道题中的字符集为全部小写英语字母，∣Σ∣&#x3D;26。最坏情况下，待搜索的单词中的每个字符都是点号，则每个字符都有 ∣Σ∣ 种可能。</p></li><li><p>空间复杂度：O(∣T∣⋅∣Σ∣)，其中 ∣T∣ 是所有添加的单词的长度之和，Σ是字符集，这道题中的字符集为全部小写英语字母，∣Σ∣&#x3D;26 。</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;添加与搜索单词&quot;&gt;&lt;a href=&quot;#添加与搜索单词&quot; class=&quot;headerlink&quot; title=&quot;添加与搜索单词&quot;&gt;&lt;/a&gt;添加与搜索单词&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot; class=&quot;headerlink&quot; </summary>
      
    
    
    
    
    <category term="数据结构-图" scheme="https://icheng281.github.io/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E5%9B%BE/"/>
    
  </entry>
  
  <entry>
    <title>蛇梯棋</title>
    <link href="https://icheng281.github.io/2024/05/12/%E8%9B%87%E6%A2%AF%E6%A3%8B/"/>
    <id>https://icheng281.github.io/2024/05/12/%E8%9B%87%E6%A2%AF%E6%A3%8B/</id>
    <published>2024-05-12T02:57:52.000Z</published>
    <updated>2024-05-12T02:59:38.330Z</updated>
    
    <content type="html"><![CDATA[<h1 id="蛇梯棋"><a href="#蛇梯棋" class="headerlink" title="蛇梯棋"></a>蛇梯棋</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>给你一个大小为 <code>n x n</code> 的整数矩阵 <code>board</code> ，方格按从 <code>1</code> 到 <code>n2</code> 编号，编号遵循 <a href="https://baike.baidu.com/item/%E7%89%9B%E8%80%95%E5%BC%8F%E8%BD%AC%E8%A1%8C%E4%B9%A6%E5%86%99%E6%B3%95/17195786">转行交替方式</a> ，<strong>从左下角开始</strong> （即，从 <code>board[n - 1][0]</code> 开始）每一行交替方向。</p><p>玩家从棋盘上的方格 <code>1</code> （总是在最后一行、第一列）开始出发。</p><p>每一回合，玩家需要从当前方格 <code>curr</code> 开始出发，按下述要求前进：</p><ul><li><p>选定目标方格，</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">next</span><br></pre></td></tr></table></figure><p>目标方格的编号符合范围 。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[curr + 1, min(curr + 6, n2)]</span><br></pre></td></tr></table></figure><ul><li>该选择模拟了掷 <strong>六面体骰子</strong> 的情景，无论棋盘大小如何，玩家最多只能有 6 个目的地。</li></ul></li><li><p>传送玩家：如果目标方格 <code>next</code> 处存在蛇或梯子，那么玩家会传送到蛇或梯子的目的地。否则，玩家传送到目标方格 <code>next</code> 。 </p></li><li><p>当玩家到达编号 <code>n2</code> 的方格时，游戏结束。</p></li></ul><p><code>r</code> 行 <code>c</code> 列的棋盘，按前述方法编号，棋盘格中可能存在 “蛇” 或 “梯子”；如果 <code>board[r][c] != -1</code>，那个蛇或梯子的目的地将会是 <code>board[r][c]</code>。编号为 <code>1</code> 和 <code>n2</code> 的方格上没有蛇或梯子。</p><p>注意，玩家在每回合的前进过程中最多只能爬过蛇或梯子一次：就算目的地是另一条蛇或梯子的起点，玩家也 <strong>不能</strong> 继续移动。</p><ul><li>举个例子，假设棋盘是 <code>[[-1,4],[-1,3]]</code> ，第一次移动，玩家的目标方格是 <code>2</code> 。那么这个玩家将会顺着梯子到达方格 <code>3</code> ，但 <strong>不能</strong> 顺着方格 <code>3</code> 上的梯子前往方格 <code>4</code> 。</li></ul><p>返回达到编号为 <code>n2</code> 的方格所需的最少移动次数，如果不可能，则返回 <code>-1</code>。</p><p><strong>示例 1：</strong></p><p><img src="https://assets.leetcode.com/uploads/2018/09/23/snakes.png" alt="img"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">输入：board = [[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,35,-1,-1,13,-1],[-1,-1,-1,-1,-1,-1],[-1,15,-1,-1,-1,-1]]</span><br><span class="line">输出：4</span><br><span class="line">解释：</span><br><span class="line">首先，从方格 1 [第 5 行，第 0 列] 开始。 </span><br><span class="line">先决定移动到方格 2 ，并必须爬过梯子移动到到方格 15 。</span><br><span class="line">然后决定移动到方格 17 [第 3 行，第 4 列]，必须爬过蛇到方格 13 。</span><br><span class="line">接着决定移动到方格 14 ，且必须通过梯子移动到方格 35 。 </span><br><span class="line">最后决定移动到方格 36 , 游戏结束。 </span><br><span class="line">可以证明需要至少 4 次移动才能到达最后一个方格，所以答案是 4 。 </span><br></pre></td></tr></table></figure><p><strong>示例 2：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：board = [[-1,-1],[-1,3]]</span><br><span class="line">输出：1</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>n == board.length == board[i].length</code></li><li><code>2 &lt;= n &lt;= 20</code></li><li><code>grid[i][j]</code> 的值是 <code>-1</code> 或在范围 <code>[1, n2]</code> 内</li><li>编号为 <code>1</code> 和 <code>n2</code> 的方格上没有蛇或梯子</li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="广度优先搜索"><a href="#广度优先搜索" class="headerlink" title="广度优先搜索"></a>广度优先搜索</h3><p><strong>思路及算法</strong></p><p>可以将棋盘抽象成一个包含 N^2^ 个节点的有向图，对于每个节点 x，若 x+i (1≤i≤6) 上没有蛇或梯子，则连一条从 x 到 x+i 的有向边；否则记蛇梯的目的地为 y，连一条从 x 到 y 的有向边。如此转换后，原问题等价于在这张有向图上求出从 1 到 N^2^ 的最短路长度。对于该问题，我们可以使用广度优先搜索。将节点编号和到达该节点的移动次数作为搜索状态，顺着该节点的出边扩展新状态，直至到达终点 N^2^ ，返回此时的移动次数。若无法到达终点则返回 −1。</p><p>代码实现时，我们可以用一个队列来存储搜索状态，初始时将起点状态 (1,0) 加入队列，表示当前位于起点 1，移动次数为 0。然后不断取出队首，每次取出队首元素时扩展新状态，即遍历该节点的出边，若出边对应节点未被访问，则将该节点和移动次数加一的结果作为新状态，加入队列。如此循环直至到达终点或队列为空。此外，我们需要计算出编号在棋盘中的对应行列，以便从 board 中得到目的地。设编号为 id，由于每行有 n 个数字，其位于棋盘从下往上数的第 $\dfrac{\textit{id}-1}{n} $ 行，记作 r。由于棋盘的每一行会交替方向，若 r 为偶数，则编号方向从左向右，列号为 (id−1) mod  n；若 r 为奇数，则编号方向从右向左，列号为 n−1−((id−1) mod n)。</p><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">class Solution &#123;</span><br><span class="line">    pair&lt;int, int&gt; id2rc(int id, int n) &#123;</span><br><span class="line">        int r = (id - 1) / n, c = (id - 1) % n;</span><br><span class="line">        if (r % 2 == 1) &#123;</span><br><span class="line">            c = n - 1 - c;</span><br><span class="line">        &#125;</span><br><span class="line">        return &#123;n - 1 - r, c&#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">public:</span><br><span class="line">    int snakesAndLadders(vector&lt;vector&lt;int&gt;&gt; &amp;board) &#123;</span><br><span class="line">        int n = board.size();</span><br><span class="line">        vector&lt;int&gt; vis(n * n + 1);</span><br><span class="line">        queue&lt;pair&lt;int, int&gt;&gt; q;</span><br><span class="line">        q.emplace(1, 0);</span><br><span class="line">        while (!q.empty()) &#123;</span><br><span class="line">            auto p = q.front();</span><br><span class="line">            q.pop();</span><br><span class="line">            for (int i = 1; i &lt;= 6; ++i) &#123;</span><br><span class="line">                int nxt = p.first + i;</span><br><span class="line">                if (nxt &gt; n * n) &#123; // 超出边界</span><br><span class="line">                    break;</span><br><span class="line">                &#125;</span><br><span class="line">                auto rc = id2rc(nxt, n); // 得到下一步的行列</span><br><span class="line">                if (board[rc.first][rc.second] &gt; 0) &#123; // 存在蛇或梯子</span><br><span class="line">                    nxt = board[rc.first][rc.second];</span><br><span class="line">                &#125;</span><br><span class="line">                if (nxt == n * n) &#123; // 到达终点</span><br><span class="line">                    return p.second + 1;</span><br><span class="line">                &#125;</span><br><span class="line">                if (!vis[nxt]) &#123;</span><br><span class="line">                    vis[nxt] = true;</span><br><span class="line">                    q.emplace(nxt, p.second + 1); // 扩展新状态</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return -1;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    public int snakesAndLadders(int[][] board) &#123;</span><br><span class="line">        int n = board.length;</span><br><span class="line">        boolean[] vis = new boolean[n * n + 1];</span><br><span class="line">        Queue&lt;int[]&gt; queue = new LinkedList&lt;int[]&gt;();</span><br><span class="line">        queue.offer(new int[]&#123;1, 0&#125;);</span><br><span class="line">        while (!queue.isEmpty()) &#123;</span><br><span class="line">            int[] p = queue.poll();</span><br><span class="line">            for (int i = 1; i &lt;= 6; ++i) &#123;</span><br><span class="line">                int nxt = p[0] + i;</span><br><span class="line">                if (nxt &gt; n * n) &#123; // 超出边界</span><br><span class="line">                    break;</span><br><span class="line">                &#125;</span><br><span class="line">                int[] rc = id2rc(nxt, n); // 得到下一步的行列</span><br><span class="line">                if (board[rc[0]][rc[1]] &gt; 0) &#123; // 存在蛇或梯子</span><br><span class="line">                    nxt = board[rc[0]][rc[1]];</span><br><span class="line">                &#125;</span><br><span class="line">                if (nxt == n * n) &#123; // 到达终点</span><br><span class="line">                    return p[1] + 1;</span><br><span class="line">                &#125;</span><br><span class="line">                if (!vis[nxt]) &#123;</span><br><span class="line">                    vis[nxt] = true;</span><br><span class="line">                    queue.offer(new int[]&#123;nxt, p[1] + 1&#125;); // 扩展新状态</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return -1;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public int[] id2rc(int id, int n) &#123;</span><br><span class="line">        int r = (id - 1) / n, c = (id - 1) % n;</span><br><span class="line">        if (r % 2 == 1) &#123;</span><br><span class="line">            c = n - 1 - c;</span><br><span class="line">        &#125;</span><br><span class="line">        return new int[]&#123;n - 1 - r, c&#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li><p>时间复杂度：O(N^2^)，其中 NNN 为棋盘 board 的边长。棋盘的每个格子至多入队一次，因此时间复杂度为 O(N^2^)。</p></li><li><p>空间复杂度：O(N^2^)。我们需要 O(N^2^) 的空间来存储每个格子是否被访问过。</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;蛇梯棋&quot;&gt;&lt;a href=&quot;#蛇梯棋&quot; class=&quot;headerlink&quot; title=&quot;蛇梯棋&quot;&gt;&lt;/a&gt;蛇梯棋&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot; class=&quot;headerlink&quot; title=&quot;1.题目内容&quot;&gt;&lt;</summary>
      
    
    
    
    
    <category term="数据结构-图" scheme="https://icheng281.github.io/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E5%9B%BE/"/>
    
  </entry>
  
  <entry>
    <title>最小基因变化</title>
    <link href="https://icheng281.github.io/2024/05/12/%E6%9C%80%E5%B0%8F%E5%9F%BA%E5%9B%A0%E5%8F%98%E5%8C%96/"/>
    <id>https://icheng281.github.io/2024/05/12/%E6%9C%80%E5%B0%8F%E5%9F%BA%E5%9B%A0%E5%8F%98%E5%8C%96/</id>
    <published>2024-05-12T02:57:27.000Z</published>
    <updated>2024-05-12T02:59:07.478Z</updated>
    
    <content type="html"><![CDATA[<h1 id="最小基因变化"><a href="#最小基因变化" class="headerlink" title="最小基因变化"></a>最小基因变化</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>基因序列可以表示为一条由 8 个字符组成的字符串，其中每个字符都是 <code>&#39;A&#39;</code>、<code>&#39;C&#39;</code>、<code>&#39;G&#39;</code> 和 <code>&#39;T&#39;</code> 之一。</p><p>假设我们需要调查从基因序列 <code>start</code> 变为 <code>end</code> 所发生的基因变化。一次基因变化就意味着这个基因序列中的一个字符发生了变化。</p><ul><li>例如，<code>&quot;AACCGGTT&quot; --&gt; &quot;AACCGGTA&quot;</code> 就是一次基因变化。</li></ul><p>另有一个基因库 <code>bank</code> 记录了所有有效的基因变化，只有基因库中的基因才是有效的基因序列。（变化后的基因必须位于基因库 <code>bank</code> 中）</p><p>给你两个基因序列 <code>start</code> 和 <code>end</code> ，以及一个基因库 <code>bank</code> ，请你找出并返回能够使 <code>start</code> 变化为 <code>end</code> 所需的最少变化次数。如果无法完成此基因变化，返回 <code>-1</code> 。</p><p>注意：起始基因序列 <code>start</code> 默认是有效的，但是它并不一定会出现在基因库中。</p><p><strong>示例 1：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：start = &quot;AACCGGTT&quot;, end = &quot;AACCGGTA&quot;, bank = [&quot;AACCGGTA&quot;]</span><br><span class="line">输出：1</span><br></pre></td></tr></table></figure><p><strong>示例 2：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：start = &quot;AACCGGTT&quot;, end = &quot;AAACGGTA&quot;, bank = [&quot;AACCGGTA&quot;,&quot;AACCGCTA&quot;,&quot;AAACGGTA&quot;]</span><br><span class="line">输出：2</span><br></pre></td></tr></table></figure><p><strong>示例 3：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：start = &quot;AAAAACCC&quot;, end = &quot;AACCCCCC&quot;, bank = [&quot;AAAACCCC&quot;,&quot;AAACCCCC&quot;,&quot;AACCCCCC&quot;]</span><br><span class="line">输出：3</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>start.length == 8</code></li><li><code>end.length == 8</code></li><li><code>0 &lt;= bank.length &lt;= 10</code></li><li><code>bank[i].length == 8</code></li><li><code>start</code>、<code>end</code> 和 <code>bank[i]</code> 仅由字符 <code>[&#39;A&#39;, &#39;C&#39;, &#39;G&#39;, &#39;T&#39;]</code> 组成</li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="广度优先搜索"><a href="#广度优先搜索" class="headerlink" title="广度优先搜索"></a>广度优先搜索</h3><p><strong>思路及算法</strong></p><p>经过分析可知，题目要求将一个基因序列 A 变化至另一个基因序列 B，需要满足以下条件：</p><ul><li>序列 A 与 序列 B 之间只有一个字符不同；</li><li>变化字符只能从 ‘A’, ‘C’, ‘G’, ‘T’ 中进行选择；</li><li>变换后的序列 B 一定要在字符串数组 bank 中。</li></ul><p>根据以上变换规则，我们可以进行尝试所有合法的基因变化，并找到最小的变换次数即可。步骤如下：</p><ul><li><p>如果 start 与 end 相等，此时直接返回 0；如果最终的基因序列不在 bank 中，则此时按照题意要求，无法生成，直接返回 −1；</p></li><li><p>首先我们将可能变换的基因 s 从队列中取出，按照上述的变换规则，尝试所有可能的变化后的基因，比如一个 AACCGGTA，我们依次尝试改变基因 s 的一个字符，并尝试所有可能的基因变化序列 s0,s1,s2,⋯ ,si,⋯ ,s23，变化一次最多可能会生成 3×8&#x3D;24 种不同的基因序列。</p></li><li><p>我们需要检测当前生成的基因序列的合法性 si，首先利用哈希表检测 si 是否在数组 bank 中，如果是则认为该基因合法，否则该变化非法直接丢弃；其次我们还需要用哈希表记录已经遍历过的基因序列，如果该基因序列已经遍历过，则此时直接跳过；如果合法且未遍历过的基因序列，则我们将其加入到队列中。</p></li><li><p>如果当前变换后的基因序列与 end 相等，则此时我们直接返回最小的变化次数即可；如果队列中所有的元素都已经遍历完成还无法变成 end，则此时无法实现目标变化，返回 −1。</p></li></ul><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">class Solution &#123;</span><br><span class="line">public:    </span><br><span class="line">    int minMutation(string start, string end, vector&lt;string&gt;&amp; bank) &#123;</span><br><span class="line">        unordered_set&lt;string&gt; cnt;</span><br><span class="line">        unordered_set&lt;string&gt; visited;</span><br><span class="line">        char keys[4] = &#123;&#x27;A&#x27;, &#x27;C&#x27;, &#x27;G&#x27;, &#x27;T&#x27;&#125;;        </span><br><span class="line">        for (auto &amp; w : bank) &#123;</span><br><span class="line">            cnt.emplace(w);</span><br><span class="line">        &#125;</span><br><span class="line">        if (start == end) &#123;</span><br><span class="line">            return 0;</span><br><span class="line">        &#125;</span><br><span class="line">        if (!cnt.count(end)) &#123;</span><br><span class="line">            return -1;</span><br><span class="line">        &#125;</span><br><span class="line">        queue&lt;string&gt; qu;</span><br><span class="line">        qu.emplace(start);</span><br><span class="line">        visited.emplace(start);</span><br><span class="line">        int step = 1;</span><br><span class="line">        while (!qu.empty()) &#123;</span><br><span class="line">            int sz = qu.size();</span><br><span class="line">            for (int i = 0; i &lt; sz; i++) &#123;</span><br><span class="line">                string curr = qu.front();</span><br><span class="line">                qu.pop();</span><br><span class="line">                for (int j = 0; j &lt; 8; j++) &#123;</span><br><span class="line">                    for (int k = 0; k &lt; 4; k++) &#123;</span><br><span class="line">                        if (keys[k] != curr[j]) &#123;</span><br><span class="line">                            string next = curr;</span><br><span class="line">                            next[j] = keys[k];</span><br><span class="line">                            if (!visited.count(next) &amp;&amp; cnt.count(next)) &#123;</span><br><span class="line">                                if (next == end) &#123;</span><br><span class="line">                                    return step;</span><br><span class="line">                                &#125;</span><br><span class="line">                                qu.emplace(next);</span><br><span class="line">                                visited.emplace(next);</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            step++;</span><br><span class="line">        &#125;</span><br><span class="line">        return -1;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    public int minMutation(String start, String end, String[] bank) &#123;</span><br><span class="line">        Set&lt;String&gt; cnt = new HashSet&lt;String&gt;();</span><br><span class="line">        Set&lt;String&gt; visited = new HashSet&lt;String&gt;();</span><br><span class="line">        char[] keys = &#123;&#x27;A&#x27;, &#x27;C&#x27;, &#x27;G&#x27;, &#x27;T&#x27;&#125;;        </span><br><span class="line">        for (String w : bank) &#123;</span><br><span class="line">            cnt.add(w);</span><br><span class="line">        &#125;</span><br><span class="line">        if (start.equals(end)) &#123;</span><br><span class="line">            return 0;</span><br><span class="line">        &#125;</span><br><span class="line">        if (!cnt.contains(end)) &#123;</span><br><span class="line">            return -1;</span><br><span class="line">        &#125;</span><br><span class="line">        Queue&lt;String&gt; queue = new ArrayDeque&lt;String&gt;();</span><br><span class="line">        queue.offer(start);</span><br><span class="line">        visited.add(start);</span><br><span class="line">        int step = 1;</span><br><span class="line">        while (!queue.isEmpty()) &#123;</span><br><span class="line">            int sz = queue.size();</span><br><span class="line">            for (int i = 0; i &lt; sz; i++) &#123;</span><br><span class="line">                String curr = queue.poll();</span><br><span class="line">                for (int j = 0; j &lt; 8; j++) &#123;</span><br><span class="line">                    for (int k = 0; k &lt; 4; k++) &#123;</span><br><span class="line">                        if (keys[k] != curr.charAt(j)) &#123;</span><br><span class="line">                            StringBuffer sb = new StringBuffer(curr);</span><br><span class="line">                            sb.setCharAt(j, keys[k]);</span><br><span class="line">                            String next = sb.toString();</span><br><span class="line">                            if (!visited.contains(next) &amp;&amp; cnt.contains(next)) &#123;</span><br><span class="line">                                if (next.equals(end)) &#123;</span><br><span class="line">                                    return step;</span><br><span class="line">                                &#125;</span><br><span class="line">                                queue.offer(next);</span><br><span class="line">                                visited.add(next);</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            step++;</span><br><span class="line">        &#125;</span><br><span class="line">        return -1;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li><p>时间复杂度：O(C×n×m)，其中 n 为基因序列的长度，m 为数组 bank 的长度。对于队列中的每个合法的基因序列每次都需要计算 C×n 种变化，在这里 C&#x3D;4；队列中最多有 m 个元素，因此时间复杂度为 O(C×n×m)。</p></li><li><p>空间复杂度：O(n×m)，其中 n 为基因序列的长度，m 为数组 bank 的长度。合法性的哈希表中一共存有 m 个元素，队列中最多有 m 个元素，每个元素的空间为 O(n)；队列中最多有 m 个元素，每个元素的空间为 O(n)，因此空间复杂度为 O(n×m)。</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;最小基因变化&quot;&gt;&lt;a href=&quot;#最小基因变化&quot; class=&quot;headerlink&quot; title=&quot;最小基因变化&quot;&gt;&lt;/a&gt;最小基因变化&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot; class=&quot;headerlink&quot; titl</summary>
      
    
    
    
    
    <category term="数据结构-图" scheme="https://icheng281.github.io/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E5%9B%BE/"/>
    
  </entry>
  
  <entry>
    <title>单词接龙</title>
    <link href="https://icheng281.github.io/2024/05/12/%E5%8D%95%E8%AF%8D%E6%8E%A5%E9%BE%99/"/>
    <id>https://icheng281.github.io/2024/05/12/%E5%8D%95%E8%AF%8D%E6%8E%A5%E9%BE%99/</id>
    <published>2024-05-12T02:55:22.000Z</published>
    <updated>2024-05-12T02:58:34.271Z</updated>
    
    <content type="html"><![CDATA[<h1 id="单词接龙"><a href="#单词接龙" class="headerlink" title="单词接龙"></a>单词接龙</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>字典 <code>wordList</code> 中从单词 <code>beginWord</code> 和 <code>endWord</code> 的 <strong>转换序列</strong> 是一个按下述规格形成的序列 <code>beginWord -&gt; s1 -&gt; s2 -&gt; ... -&gt; sk</code>：</p><ul><li>每一对相邻的单词只差一个字母。</li><li>对于 <code>1 &lt;= i &lt;= k</code> 时，每个 <code>si</code> 都在 <code>wordList</code> 中。注意， <code>beginWord</code> 不需要在 <code>wordList</code> 中。</li><li><code>sk == endWord</code></li></ul><p>给你两个单词 <code>beginWord</code> 和 <code>endWord</code> 和一个字典 <code>wordList</code> ，返回 <em>从 <code>beginWord</code> 到 <code>endWord</code> 的 <strong>最短转换序列</strong> 中的 <strong>单词数目</strong></em> 。如果不存在这样的转换序列，返回 <code>0</code> 。</p><p><strong>示例 1：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：beginWord = &quot;hit&quot;, endWord = &quot;cog&quot;, wordList = [&quot;hot&quot;,&quot;dot&quot;,&quot;dog&quot;,&quot;lot&quot;,&quot;log&quot;,&quot;cog&quot;]</span><br><span class="line">输出：5</span><br><span class="line">解释：一个最短转换序列是 &quot;hit&quot; -&gt; &quot;hot&quot; -&gt; &quot;dot&quot; -&gt; &quot;dog&quot; -&gt; &quot;cog&quot;, 返回它的长度 5。</span><br></pre></td></tr></table></figure><p><strong>示例 2：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：beginWord = &quot;hit&quot;, endWord = &quot;cog&quot;, wordList = [&quot;hot&quot;,&quot;dot&quot;,&quot;dog&quot;,&quot;lot&quot;,&quot;log&quot;]</span><br><span class="line">输出：0</span><br><span class="line">解释：endWord &quot;cog&quot; 不在字典中，所以无法进行转换。</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= beginWord.length &lt;= 10</code></li><li><code>endWord.length == beginWord.length</code></li><li><code>1 &lt;= wordList.length &lt;= 5000</code></li><li><code>wordList[i].length == beginWord.length</code></li><li><code>beginWord</code>、<code>endWord</code> 和 <code>wordList[i]</code> 由小写英文字母组成</li><li><code>beginWord != endWord</code></li><li><code>wordList</code> 中的所有字符串 <strong>互不相同</strong></li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="（1）广度优先遍历"><a href="#（1）广度优先遍历" class="headerlink" title="（1）广度优先遍历"></a>（1）广度优先遍历</h3><p>「转换」意即：两个单词对应位置只有一个字符不同，例如 “hit” 与 “hot”，这种转换是可以逆向的，因此，根据题目给出的单词列表，可以构建出一个无向（无权）图；</p><p><img src="https://pic.leetcode-cn.com/ec8f7e4f40134b932a9ff2e306d885e427bd8ee912801361849d92ddae6226f3-image.png" alt="image.png"></p><ul><li>如果一开始就构建图，每一个单词都需要和除它以外的另外的单词进行比较，复杂度是 O(NwordLen)，这里 N 是单词列表的长度；</li><li>为此，我们在遍历一开始，把所有的单词列表放进一个哈希表中，然后在遍历的时候构建图，每一次得到在单词列表里可以转换的单词，复杂度是 O(26×wordLen)，借助哈希表，找到邻居与 N 无关；</li><li>使用 BFS 进行遍历，需要的辅助数据结构是：<ul><li>队列；</li><li>visited 集合。说明：可以直接在 wordSet (由 wordList 放进集合中得到)里做删除。但更好的做法是新开一个哈希表，遍历过的字符串放进哈希表里。这种做法具有普遍意义。绝大多数在线测评系统和应用场景都不会在意空间开销。</li></ul></li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.Collections;</span><br><span class="line">import java.util.HashSet;</span><br><span class="line">import java.util.LinkedList;</span><br><span class="line">import java.util.List;</span><br><span class="line">import java.util.Queue;</span><br><span class="line">import java.util.Set;</span><br><span class="line"></span><br><span class="line">public class Solution &#123;</span><br><span class="line"></span><br><span class="line">    public int ladderLength(String beginWord, String endWord, List&lt;String&gt; wordList) &#123;</span><br><span class="line">        // 第 1 步：先将 wordList 放到哈希表里，便于判断某个单词是否在 wordList 里</span><br><span class="line">        Set&lt;String&gt; wordSet = new HashSet&lt;&gt;(wordList);</span><br><span class="line">        if (wordSet.size() == 0 || !wordSet.contains(endWord)) &#123;</span><br><span class="line">            return 0;</span><br><span class="line">        &#125;</span><br><span class="line">        wordSet.remove(beginWord);</span><br><span class="line">        </span><br><span class="line">        // 第 2 步：图的广度优先遍历，必须使用队列和表示是否访问过的 visited 哈希表</span><br><span class="line">        Queue&lt;String&gt; queue = new LinkedList&lt;&gt;();</span><br><span class="line">        queue.offer(beginWord);</span><br><span class="line">        Set&lt;String&gt; visited = new HashSet&lt;&gt;();</span><br><span class="line">        visited.add(beginWord);</span><br><span class="line">        </span><br><span class="line">        // 第 3 步：开始广度优先遍历，包含起点，因此初始化的时候步数为 1</span><br><span class="line">        int step = 1;</span><br><span class="line">        while (!queue.isEmpty()) &#123;</span><br><span class="line">            int currentSize = queue.size();</span><br><span class="line">            for (int i = 0; i &lt; currentSize; i++) &#123;</span><br><span class="line">                // 依次遍历当前队列中的单词</span><br><span class="line">                String currentWord = queue.poll();</span><br><span class="line">                // 如果 currentWord 能够修改 1 个字符与 endWord 相同，则返回 step + 1</span><br><span class="line">                if (changeWordEveryOneLetter(currentWord, endWord, queue, visited, wordSet)) &#123;</span><br><span class="line">                    return step + 1;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            step++;</span><br><span class="line">        &#125;</span><br><span class="line">        return 0;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 尝试对 currentWord 修改每一个字符，看看是不是能与 endWord 匹配</span><br><span class="line">     *</span><br><span class="line">     * @param currentWord</span><br><span class="line">     * @param endWord</span><br><span class="line">     * @param queue</span><br><span class="line">     * @param visited</span><br><span class="line">     * @param wordSet</span><br><span class="line">     * @return</span><br><span class="line">     */</span><br><span class="line">    private boolean changeWordEveryOneLetter(String currentWord, String endWord,</span><br><span class="line">                                             Queue&lt;String&gt; queue, Set&lt;String&gt; visited, Set&lt;String&gt; wordSet) &#123;</span><br><span class="line">        char[] charArray = currentWord.toCharArray();</span><br><span class="line">        for (int i = 0; i &lt; endWord.length(); i++) &#123;</span><br><span class="line">            // 先保存，然后恢复</span><br><span class="line">            char originChar = charArray[i];</span><br><span class="line">            for (char k = &#x27;a&#x27;; k &lt;= &#x27;z&#x27;; k++) &#123;</span><br><span class="line">                if (k == originChar) &#123;</span><br><span class="line">                    continue;</span><br><span class="line">                &#125;</span><br><span class="line">                charArray[i] = k;</span><br><span class="line">                String nextWord = String.valueOf(charArray);</span><br><span class="line">                if (wordSet.contains(nextWord)) &#123;</span><br><span class="line">                    if (nextWord.equals(endWord)) &#123;</span><br><span class="line">                        return true;</span><br><span class="line">                    &#125;</span><br><span class="line">                    if (!visited.contains(nextWord)) &#123;</span><br><span class="line">                        queue.add(nextWord);</span><br><span class="line">                        // 注意：添加到队列以后，必须马上标记为已经访问</span><br><span class="line">                        visited.add(nextWord);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            // 恢复</span><br><span class="line">            charArray[i] = originChar;</span><br><span class="line">        &#125;</span><br><span class="line">        return false;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="（2）双向广度优先遍历"><a href="#（2）双向广度优先遍历" class="headerlink" title="（2）双向广度优先遍历"></a>（2）双向广度优先遍历</h3><ul><li>已知目标顶点的情况下，可以分别从起点和目标顶点（终点）执行广度优先遍历，直到遍历的部分有交集。这种方式搜索的单词数量会更小一些；</li><li>更合理的做法是，每次从单词数量小的集合开始扩散；</li><li>这里 beginVisited 和 endVisited 交替使用，等价于单向 BFS 里使用队列，每次扩散都要加到总的 visited 里。</li></ul><p><img src="https://pic.leetcode-cn.com/38dc5897de2b554ea606a92c5eada14b0e0030195334e9fd65943ed6d0f77c1d-image.png" alt="image.png"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.Collections;</span><br><span class="line">import java.util.HashSet;</span><br><span class="line">import java.util.List;</span><br><span class="line">import java.util.Set;</span><br><span class="line"></span><br><span class="line">public class Solution &#123;</span><br><span class="line"></span><br><span class="line">    public int ladderLength(String beginWord, String endWord, List&lt;String&gt; wordList) &#123;</span><br><span class="line">        // 第 1 步：先将 wordList 放到哈希表里，便于判断某个单词是否在 wordList 里</span><br><span class="line">        Set&lt;String&gt; wordSet = new HashSet&lt;&gt;(wordList);</span><br><span class="line">        if (wordSet.size() == 0 || !wordSet.contains(endWord)) &#123;</span><br><span class="line">            return 0;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        // 第 2 步：已经访问过的 word 添加到 visited 哈希表里</span><br><span class="line">        Set&lt;String&gt; visited = new HashSet&lt;&gt;();</span><br><span class="line">        // 分别用左边和右边扩散的哈希表代替单向 BFS 里的队列，它们在双向 BFS 的过程中交替使用</span><br><span class="line">        Set&lt;String&gt; beginVisited = new HashSet&lt;&gt;();</span><br><span class="line">        beginVisited.add(beginWord);</span><br><span class="line">        Set&lt;String&gt; endVisited = new HashSet&lt;&gt;();</span><br><span class="line">        endVisited.add(endWord);</span><br><span class="line"></span><br><span class="line">        // 第 3 步：执行双向 BFS，左右交替扩散的步数之和为所求</span><br><span class="line">        int step = 1;</span><br><span class="line">        while (!beginVisited.isEmpty() &amp;&amp; !endVisited.isEmpty()) &#123;</span><br><span class="line">            // 优先选择小的哈希表进行扩散，考虑到的情况更少</span><br><span class="line">            if (beginVisited.size() &gt; endVisited.size()) &#123;</span><br><span class="line">                Set&lt;String&gt; temp = beginVisited;</span><br><span class="line">                beginVisited = endVisited;</span><br><span class="line">                endVisited = temp;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            // 逻辑到这里，保证 beginVisited 是相对较小的集合，nextLevelVisited 在扩散完成以后，会成为新的 beginVisited</span><br><span class="line">            Set&lt;String&gt; nextLevelVisited = new HashSet&lt;&gt;();</span><br><span class="line">            for (String word : beginVisited) &#123;</span><br><span class="line">                if (changeWordEveryOneLetter(word, endVisited, visited, wordSet, nextLevelVisited)) &#123;</span><br><span class="line">                    return step + 1;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            // 原来的 beginVisited 废弃，从 nextLevelVisited 开始新的双向 BFS</span><br><span class="line">            beginVisited = nextLevelVisited;</span><br><span class="line">            step++;</span><br><span class="line">        &#125;</span><br><span class="line">        return 0;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 尝试对 word 修改每一个字符，看看是不是能落在 endVisited 中，扩展得到的新的 word 添加到 nextLevelVisited 里</span><br><span class="line">     *</span><br><span class="line">     * @param word</span><br><span class="line">     * @param endVisited</span><br><span class="line">     * @param visited</span><br><span class="line">     * @param wordSet</span><br><span class="line">     * @param nextLevelVisited</span><br><span class="line">     * @return</span><br><span class="line">     */</span><br><span class="line">    private boolean changeWordEveryOneLetter(String word, Set&lt;String&gt; endVisited,</span><br><span class="line">                                             Set&lt;String&gt; visited,</span><br><span class="line">                                             Set&lt;String&gt; wordSet,</span><br><span class="line">                                             Set&lt;String&gt; nextLevelVisited) &#123;</span><br><span class="line">        char[] charArray = word.toCharArray();</span><br><span class="line">        for (int i = 0; i &lt; word.length(); i++) &#123;</span><br><span class="line">            char originChar = charArray[i];</span><br><span class="line">            for (char c = &#x27;a&#x27;; c &lt;= &#x27;z&#x27;; c++) &#123;</span><br><span class="line">                if (originChar == c) &#123;</span><br><span class="line">                    continue;</span><br><span class="line">                &#125;</span><br><span class="line">                charArray[i] = c;</span><br><span class="line">                String nextWord = String.valueOf(charArray);</span><br><span class="line">                if (wordSet.contains(nextWord)) &#123;</span><br><span class="line">                    if (endVisited.contains(nextWord)) &#123;</span><br><span class="line">                        return true;</span><br><span class="line">                    &#125;</span><br><span class="line">                    if (!visited.contains(nextWord)) &#123;</span><br><span class="line">                        nextLevelVisited.add(nextWord);</span><br><span class="line">                        visited.add(nextWord);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            // 恢复，下次再用</span><br><span class="line">            charArray[i] = originChar;</span><br><span class="line">        &#125;</span><br><span class="line">        return false;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;单词接龙&quot;&gt;&lt;a href=&quot;#单词接龙&quot; class=&quot;headerlink&quot; title=&quot;单词接龙&quot;&gt;&lt;/a&gt;单词接龙&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot; class=&quot;headerlink&quot; title=&quot;1.题目内</summary>
      
    
    
    
    
    <category term="数据结构-图" scheme="https://icheng281.github.io/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E5%9B%BE/"/>
    
  </entry>
  
  <entry>
    <title>课程表II</title>
    <link href="https://icheng281.github.io/2024/05/10/%E8%AF%BE%E7%A8%8B%E8%A1%A8II/"/>
    <id>https://icheng281.github.io/2024/05/10/%E8%AF%BE%E7%A8%8B%E8%A1%A8II/</id>
    <published>2024-05-10T02:34:54.000Z</published>
    <updated>2024-05-10T02:37:40.019Z</updated>
    
    <content type="html"><![CDATA[<h1 id="课程表II"><a href="#课程表II" class="headerlink" title="课程表II"></a>课程表II</h1><h2 id="1-题目内容"><a href="#1-题目内容" class="headerlink" title="1.题目内容"></a>1.题目内容</h2><p>现在你总共有 <code>numCourses</code> 门课需要选，记为 <code>0</code> 到 <code>numCourses - 1</code>。给你一个数组 <code>prerequisites</code> ，其中 <code>prerequisites[i] = [ai, bi]</code> ，表示在选修课程 <code>ai</code> 前 <strong>必须</strong> 先选修 <code>bi</code> 。</p><ul><li>例如，想要学习课程 <code>0</code> ，你需要先完成课程 <code>1</code> ，我们用一个匹配来表示：<code>[0,1]</code> 。</li></ul><p>返回你为了学完所有课程所安排的学习顺序。可能会有多个正确的顺序，你只要返回 <strong>任意一种</strong> 就可以了。如果不可能完成所有课程，返回 <strong>一个空数组</strong> 。</p><p><strong>示例 1：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：numCourses = 2, prerequisites = [[1,0]]</span><br><span class="line">输出：[0,1]</span><br><span class="line">解释：总共有 2 门课程。要学习课程 1，你需要先完成课程 0。因此，正确的课程顺序为 [0,1] 。</span><br></pre></td></tr></table></figure><p><strong>示例 2：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">输入：numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]</span><br><span class="line">输出：[0,2,1,3]</span><br><span class="line">解释：总共有 4 门课程。要学习课程 3，你应该先完成课程 1 和课程 2。并且课程 1 和课程 2 都应该排在课程 0 之后。</span><br><span class="line">因此，一个正确的课程顺序是 [0,1,2,3] 。另一个正确的排序是 [0,2,1,3] 。</span><br></pre></td></tr></table></figure><p><strong>示例 3：</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：numCourses = 1, prerequisites = []</span><br><span class="line">输出：[0]</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= numCourses &lt;= 2000</code></li><li><code>0 &lt;= prerequisites.length &lt;= numCourses * (numCourses - 1)</code></li><li><code>prerequisites[i].length == 2</code></li><li><code>0 &lt;= ai, bi &lt; numCourses</code></li><li><code>ai != bi</code></li><li>所有<code>[ai, bi]</code> <strong>互不相同</strong></li></ul><h2 id="2-解法"><a href="#2-解法" class="headerlink" title="2.解法"></a>2.解法</h2><h3 id="（1）深度优先搜索"><a href="#（1）深度优先搜索" class="headerlink" title="（1）深度优先搜索"></a>（1）深度优先搜索</h3><p><strong>思路及算法</strong></p><p>我们可以将深度优先搜索的流程与拓扑排序的求解联系起来，用一个栈来存储所有<strong>已经搜索完成的节点</strong>。</p><p>假设我们当前搜索到了节点 u，如果它的所有相邻节点都已经搜索完成，那么这些节点都已经在栈中了，此时我们就可以把 u 入栈。可以发现，如果我们从栈顶往栈底的顺序看，由于 u 处于栈顶的位置，那么 u 出现在所有 u 的相邻节点的前面。因此对于 u 这个节点而言，它是满足拓扑排序的要求的。</p><p>这样以来，我们对图进行一遍深度优先搜索。当每个节点进行回溯的时候，我们把该节点放入栈中。最终从栈顶到栈底的序列就是一种拓扑排序。</p><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">class Solution &#123;</span><br><span class="line">private:</span><br><span class="line">    // 存储有向图</span><br><span class="line">    vector&lt;vector&lt;int&gt;&gt; edges;</span><br><span class="line">    // 标记每个节点的状态：0=未搜索，1=搜索中，2=已完成</span><br><span class="line">    vector&lt;int&gt; visited;</span><br><span class="line">    // 用数组来模拟栈，下标 0 为栈底，n-1 为栈顶</span><br><span class="line">    vector&lt;int&gt; result;</span><br><span class="line">    // 判断有向图中是否有环</span><br><span class="line">    bool valid = true;</span><br><span class="line"></span><br><span class="line">public:</span><br><span class="line">    void dfs(int u) &#123;</span><br><span class="line">        // 将节点标记为「搜索中」</span><br><span class="line">        visited[u] = 1;</span><br><span class="line">        // 搜索其相邻节点</span><br><span class="line">        // 只要发现有环，立刻停止搜索</span><br><span class="line">        for (int v: edges[u]) &#123;</span><br><span class="line">            // 如果「未搜索」那么搜索相邻节点</span><br><span class="line">            if (visited[v] == 0) &#123;</span><br><span class="line">                dfs(v);</span><br><span class="line">                if (!valid) &#123;</span><br><span class="line">                    return;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            // 如果「搜索中」说明找到了环</span><br><span class="line">            else if (visited[v] == 1) &#123;</span><br><span class="line">                valid = false;</span><br><span class="line">                return;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        // 将节点标记为「已完成」</span><br><span class="line">        visited[u] = 2;</span><br><span class="line">        // 将节点入栈</span><br><span class="line">        result.push_back(u);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    vector&lt;int&gt; findOrder(int numCourses, vector&lt;vector&lt;int&gt;&gt;&amp; prerequisites) &#123;</span><br><span class="line">        edges.resize(numCourses);</span><br><span class="line">        visited.resize(numCourses);</span><br><span class="line">        for (const auto&amp; info: prerequisites) &#123;</span><br><span class="line">            edges[info[1]].push_back(info[0]);</span><br><span class="line">        &#125;</span><br><span class="line">        // 每次挑选一个「未搜索」的节点，开始进行深度优先搜索</span><br><span class="line">        for (int i = 0; i &lt; numCourses &amp;&amp; valid; ++i) &#123;</span><br><span class="line">            if (!visited[i]) &#123;</span><br><span class="line">                dfs(i);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        if (!valid) &#123;</span><br><span class="line">            return &#123;&#125;;</span><br><span class="line">        &#125;</span><br><span class="line">        // 如果没有环，那么就有拓扑排序</span><br><span class="line">        // 注意下标 0 为栈底，因此需要将数组反序输出</span><br><span class="line">        reverse(result.begin(), result.end());</span><br><span class="line">        return result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    // 存储有向图</span><br><span class="line">    List&lt;List&lt;Integer&gt;&gt; edges;</span><br><span class="line">    // 标记每个节点的状态：0=未搜索，1=搜索中，2=已完成</span><br><span class="line">    int[] visited;</span><br><span class="line">    // 用数组来模拟栈，下标 n-1 为栈底，0 为栈顶</span><br><span class="line">    int[] result;</span><br><span class="line">    // 判断有向图中是否有环</span><br><span class="line">    boolean valid = true;</span><br><span class="line">    // 栈下标</span><br><span class="line">    int index;</span><br><span class="line"></span><br><span class="line">    public int[] findOrder(int numCourses, int[][] prerequisites) &#123;</span><br><span class="line">        edges = new ArrayList&lt;List&lt;Integer&gt;&gt;();</span><br><span class="line">        for (int i = 0; i &lt; numCourses; ++i) &#123;</span><br><span class="line">            edges.add(new ArrayList&lt;Integer&gt;());</span><br><span class="line">        &#125;</span><br><span class="line">        visited = new int[numCourses];</span><br><span class="line">        result = new int[numCourses];</span><br><span class="line">        index = numCourses - 1;</span><br><span class="line">        for (int[] info : prerequisites) &#123;</span><br><span class="line">            edges.get(info[1]).add(info[0]);</span><br><span class="line">        &#125;</span><br><span class="line">        // 每次挑选一个「未搜索」的节点，开始进行深度优先搜索</span><br><span class="line">        for (int i = 0; i &lt; numCourses &amp;&amp; valid; ++i) &#123;</span><br><span class="line">            if (visited[i] == 0) &#123;</span><br><span class="line">                dfs(i);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        if (!valid) &#123;</span><br><span class="line">            return new int[0];</span><br><span class="line">        &#125;</span><br><span class="line">        // 如果没有环，那么就有拓扑排序</span><br><span class="line">        return result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void dfs(int u) &#123;</span><br><span class="line">        // 将节点标记为「搜索中」</span><br><span class="line">        visited[u] = 1;</span><br><span class="line">        // 搜索其相邻节点</span><br><span class="line">        // 只要发现有环，立刻停止搜索</span><br><span class="line">        for (int v: edges.get(u)) &#123;</span><br><span class="line">            // 如果「未搜索」那么搜索相邻节点</span><br><span class="line">            if (visited[v] == 0) &#123;</span><br><span class="line">                dfs(v);</span><br><span class="line">                if (!valid) &#123;</span><br><span class="line">                    return;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            // 如果「搜索中」说明找到了环</span><br><span class="line">            else if (visited[v] == 1) &#123;</span><br><span class="line">                valid = false;</span><br><span class="line">                return;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        // 将节点标记为「已完成」</span><br><span class="line">        visited[u] = 2;</span><br><span class="line">        // 将节点入栈</span><br><span class="line">        result[index--] = u;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li><p>时间复杂度: O(n+m)，其中 n 为课程数，m 为先修课程的要求数。这其实就是对图进行深度优先搜索的时间复杂度。</p></li><li><p>空间复杂度: O(n+m)。题目中是以列表形式给出的先修课程关系，为了对图进行深度优先搜索，我们需要存储成邻接表的形式，空间复杂度为 O(n+m)。在深度优先搜索的过程中，我们需要最多 O(n) 的栈空间（递归）进行深度优先搜索，并且还需要若干个 O(n) 的空间存储节点状态、最终答案等。</p></li></ul><h3 id="（2）广度优先搜索"><a href="#（2）广度优先搜索" class="headerlink" title="（2）广度优先搜索"></a>（2）广度优先搜索</h3><p><strong>思路及算法</strong></p><p>我们考虑拓扑排序中最前面的节点，该节点一定不会有任何入边，也就是它没有任何的先修课程要求。当我们将一个节点加入答案中后，我们就可以移除它的所有出边，代表着它的相邻节点少了一门先修课程的要求。如果某个相邻节点变成了「没有任何入边的节点」，那么就代表着这门课可以开始学习了。按照这样的流程，我们不断地将没有入边的节点加入答案，直到答案中包含所有的节点（得到了一种拓扑排序）或者不存在没有入边的节点。</p><p>上面的想法类似于广度优先搜索，因此我们可以将广度优先搜索的流程与拓扑排序的求解联系起来。</p><p><strong>代码</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line">//C++</span><br><span class="line">class Solution &#123;</span><br><span class="line">private:</span><br><span class="line">    // 存储有向图</span><br><span class="line">    vector&lt;vector&lt;int&gt;&gt; edges;</span><br><span class="line">    // 存储每个节点的入度</span><br><span class="line">    vector&lt;int&gt; indeg;</span><br><span class="line">    // 存储答案</span><br><span class="line">    vector&lt;int&gt; result;</span><br><span class="line"></span><br><span class="line">public:</span><br><span class="line">    vector&lt;int&gt; findOrder(int numCourses, vector&lt;vector&lt;int&gt;&gt;&amp; prerequisites) &#123;</span><br><span class="line">        edges.resize(numCourses);</span><br><span class="line">        indeg.resize(numCourses);</span><br><span class="line">        for (const auto&amp; info: prerequisites) &#123;</span><br><span class="line">            edges[info[1]].push_back(info[0]);</span><br><span class="line">            ++indeg[info[0]];</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        queue&lt;int&gt; q;</span><br><span class="line">        // 将所有入度为 0 的节点放入队列中</span><br><span class="line">        for (int i = 0; i &lt; numCourses; ++i) &#123;</span><br><span class="line">            if (indeg[i] == 0) &#123;</span><br><span class="line">                q.push(i);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        while (!q.empty()) &#123;</span><br><span class="line">            // 从队首取出一个节点</span><br><span class="line">            int u = q.front();</span><br><span class="line">            q.pop();</span><br><span class="line">            // 放入答案中</span><br><span class="line">            result.push_back(u);</span><br><span class="line">            for (int v: edges[u]) &#123;</span><br><span class="line">                --indeg[v];</span><br><span class="line">                // 如果相邻节点 v 的入度为 0，就可以选 v 对应的课程了</span><br><span class="line">                if (indeg[v] == 0) &#123;</span><br><span class="line">                    q.push(v);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        if (result.size() != numCourses) &#123;</span><br><span class="line">            return &#123;&#125;;</span><br><span class="line">        &#125;</span><br><span class="line">        return result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line">//Java</span><br><span class="line">class Solution &#123;</span><br><span class="line">    // 存储有向图</span><br><span class="line">    List&lt;List&lt;Integer&gt;&gt; edges;</span><br><span class="line">    // 存储每个节点的入度</span><br><span class="line">    int[] indeg;</span><br><span class="line">    // 存储答案</span><br><span class="line">    int[] result;</span><br><span class="line">    // 答案下标</span><br><span class="line">    int index;</span><br><span class="line"></span><br><span class="line">    public int[] findOrder(int numCourses, int[][] prerequisites) &#123;</span><br><span class="line">        edges = new ArrayList&lt;List&lt;Integer&gt;&gt;();</span><br><span class="line">        for (int i = 0; i &lt; numCourses; ++i) &#123;</span><br><span class="line">            edges.add(new ArrayList&lt;Integer&gt;());</span><br><span class="line">        &#125;</span><br><span class="line">        indeg = new int[numCourses];</span><br><span class="line">        result = new int[numCourses];</span><br><span class="line">        index = 0;</span><br><span class="line">        for (int[] info : prerequisites) &#123;</span><br><span class="line">            edges.get(info[1]).add(info[0]);</span><br><span class="line">            ++indeg[info[0]];</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        Queue&lt;Integer&gt; queue = new LinkedList&lt;Integer&gt;();</span><br><span class="line">        // 将所有入度为 0 的节点放入队列中</span><br><span class="line">        for (int i = 0; i &lt; numCourses; ++i) &#123;</span><br><span class="line">            if (indeg[i] == 0) &#123;</span><br><span class="line">                queue.offer(i);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        while (!queue.isEmpty()) &#123;</span><br><span class="line">            // 从队首取出一个节点</span><br><span class="line">            int u = queue.poll();</span><br><span class="line">            // 放入答案中</span><br><span class="line">            result[index++] = u;</span><br><span class="line">            for (int v: edges.get(u)) &#123;</span><br><span class="line">                --indeg[v];</span><br><span class="line">                // 如果相邻节点 v 的入度为 0，就可以选 v 对应的课程了</span><br><span class="line">                if (indeg[v] == 0) &#123;</span><br><span class="line">                    queue.offer(v);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        if (index != numCourses) &#123;</span><br><span class="line">            return new int[0];</span><br><span class="line">        &#125;</span><br><span class="line">        return result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>复杂度分析</strong></p><ul><li><p>时间复杂度: O(n+m)，其中 n 为课程数，m 为先修课程的要求数。这其实就是对图进行广度优先搜索的时间复杂度。</p></li><li><p>空间复杂度: O(n+m)。题目中是以列表形式给出的先修课程关系，为了对图进行广度优先搜索，我们需要存储成邻接表的形式，空间复杂度为 O(n+m)。在广度优先搜索的过程中，我们需要最多 O(n) 的队列空间（迭代）进行广度优先搜索，并且还需要若干个 O(n) 的空间存储节点入度、最终答案等。</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;课程表II&quot;&gt;&lt;a href=&quot;#课程表II&quot; class=&quot;headerlink&quot; title=&quot;课程表II&quot;&gt;&lt;/a&gt;课程表II&lt;/h1&gt;&lt;h2 id=&quot;1-题目内容&quot;&gt;&lt;a href=&quot;#1-题目内容&quot; class=&quot;headerlink&quot; title=&quot;1</summary>
      
    
    
    
    
    <category term="数据结构-图" scheme="https://icheng281.github.io/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E5%9B%BE/"/>
    
  </entry>
  
</feed>
