zoukankan      html  css  js  c++  java
  • 剑指offer

    题目描述

    输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下 4 X 4 矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字 1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

    解法 1: 模拟路径

    根据直觉,当遍历的过程中,遇到超出边界 / 元素已经被访问过的情况时,应该按照顺时针转变方向。

    假设给定的矩阵的形状是 m*n,那么一共要遍历 m*n 次。要准备一个长度为 m*n 的哈希表,来保存元素是否被遍历过。要准备一个记录方向的数组,里面方向的排列顺序是顺时针。

    时间复杂度为 O(M*N),空间复杂度为 O(M*N)。

    代码实现如下:

    // ac地址:https://www.nowcoder.com/practice/9b4c81a02cd34f76be2659fa0d54342a
    // 原文地址:https://xxoo521.com/2020-01-30-shun-shi-zhen-matrix/
    

    /**

    • @param {number} i
    • @param {number} j
      */
      function hash(i, j) {
      return <span class="hljs-subst" style="color: #333; font-weight: normal; line-height: 26px;">${i}</span>-<span class="hljs-subst" style="color: #333; font-weight: normal; line-height: 26px;">${j}</span>;
      }

    /**

    • @param {number[][]} matrix

    • @return {number[]}
      */
      function printMatrix(matrix) {
      const m = matrix.length;
      if (!m) {
      return [];
      }

      const n = matrix[0].length;
      if (!n) {
      return [];
      }

      const results = []; // 遍历结果
      const visited = {}; // 记录元素是否被访问过
      const directions = [
      [0, 1],
      [1, 0],
      [0, -1],
      [-1, 0]
      ]; // 顺时针方向数组

      for (let step = 0, row = 0, col = 0, dIdx = 0; step < m * n; ++step) {
      results.push(matrix[row][col]);
      visited[hash(row, col)] = true;
      // 最巧妙的地方:借助方向数组来进行row、col的更新
      const newR = row + directions[dIdx][0];
      const newC = col + directions[dIdx][1];

       <span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">if</span> (
           !visited[hash(newR, newC)] &amp;&amp;
           newR &gt;= <span class="hljs-number" style="color: #008080;user-select:none; line-height: 26px;">0</span> &amp;&amp;
           newR &lt; m &amp;&amp;
           newC &gt;= <span class="hljs-number" style="color: #008080;user-select:none; line-height: 26px;">0</span> &amp;&amp;
           newC &lt; n
       ) {
           row = newR;
           col = newC;
       } <span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">else</span> {
           <span class="hljs-comment" style="color: #998; font-style: italic; line-height: 26px;">// 转变方向</span>
           dIdx = (dIdx + <span class="hljs-number" style="color: #008080;user-select:none; line-height: 26px;">1</span>) % <span class="hljs-number" style="color: #008080;user-select:none; line-height: 26px;">4</span>;
           row += directions[dIdx][<span class="hljs-number" style="color: #008080;user-select:none; line-height: 26px;">0</span>];
           col += directions[dIdx][<span class="hljs-number" style="color: #008080;user-select:none; line-height: 26px;">1</span>];
       }
      

      }

      return results;
      }

解法 2: 按层遍历

这种方法的思路是从外到内,一层层打印。难点在于怎么找到标记点,以及防止重复遍历。

怎么找到标记点?对于每一层来说,设左上角的元素坐标为 (i, j),那么右上角的元素坐标为 (i, n - j - 1),右下角的元素坐标是 (m - i - 1 ,n - j - 1),左下角的元素坐标是 (m - i - 1, j)。找到标记点后,就是对行/列进行+/-的过程。

怎么防止重复遍历?找到四个坐标点后,每一层的遍历可以拆分成 4 个部分。我想到的是下图所示的两种拆分方法:

第一种拆分方法会有例外,比较难处理,无法避免重复遍历。因此使用第二种。

时间复杂度为 O(M*N),空间复杂度为 O(M*N)。代码实现如下:

// ac地址:https://www.nowcoder.com/practice/9b4c81a02cd34f76be2659fa0d54342a
// 原文地址:https://xxoo521.com/2020-01-30-shun-shi-zhen-matrix/

/**

  • @param {number[][]} matrix

  • @return {number[]}
    */
    function printMatrix(matrix) {
    const m = matrix.length;
    if (!m) {
    return [];
    }

    const n = matrix[0].length;
    if (!n) {
    return [];
    }

    const results = [];
    let i = 0,
    j = 0;
    // 这里的终止条件是: i <= (m - 1) / 2 与 j <= (n - j) / 2
    // 即最里面的那层左上角元素的坐标
    while (i <= m - i - 1 && j <= n - j - 1) {
    for (let col = j; col <= n - j - 1; ++col) {
    results.push(matrix[i][col]);
    }

     <span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">for</span> (<span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">let</span> row = i + <span class="hljs-number" style="color: #008080;user-select:none; line-height: 26px;">1</span>; row &lt;= m - i - <span class="hljs-number" style="color: #008080;user-select:none; line-height: 26px;">1</span>; ++row) {
         results.push(matrix[row][n - j - <span class="hljs-number" style="color: #008080;user-select:none; line-height: 26px;">1</span>]);
     }
    
     <span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">if</span> (i &lt; m - i - <span class="hljs-number" style="color: #008080;user-select:none; line-height: 26px;">1</span> &amp;&amp; j &lt; n - j - <span class="hljs-number" style="color: #008080;user-select:none; line-height: 26px;">1</span>) {
         <span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">for</span> (<span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">let</span> col = n - j - <span class="hljs-number" style="color: #008080;user-select:none; line-height: 26px;">2</span>; col &gt; j; --col) {
             results.push(matrix[m - i - <span class="hljs-number" style="color: #008080;user-select:none; line-height: 26px;">1</span>][col]);
         }
    
         <span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">for</span> (<span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">let</span> row = m - i - <span class="hljs-number" style="color: #008080;user-select:none; line-height: 26px;">1</span>; row &gt; i; --row) {
             results.push(matrix[row][j]);
         }
     }
    
     i++;
     j++;
    

    }

    return results;
    }

最后

  1. 觉得不错,帮忙点个推荐呗,您的支持是对我最大的激励
  2. 欢迎我的公众号:「心谭博客」,只专注于前端 + 算法的原创分享

由于个人精力有限,很多系列和历史文章没有即时同步,请前往「前端图谱」&「算法题解」,保证您有所收获。

查看全文
  • 相关阅读:
    [openjudge] jubeeeeeat
    [BJOI2006] 狼抓兔子
    [模板]网络最大流
    [HNOI2002]营业额统计
    【Java学习笔记之十一】Java中常用的8大排序算法详解总结
    【Java学习笔记之十】Java中循环语句foreach使用总结及foreach写法失效的问题
    【Java学习笔记之九】java二维数组及其多维数组的内存应用拓展延伸
    【Java学习笔记之八】JavaBean中布尔类型使用注意事项
    【Java学习笔记之七】java函数的语法规则总结
    二分图匹配--匈牙利算法模板
  • 原文地址:https://www.cnblogs.com/geyouneihan/p/12256154.html
  • Copyright © 2011-2022 走看看