题目描述

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下 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; line-height: 26px;">0</span> &amp;&amp;
    newR &lt; m &amp;&amp;
    newC &gt;= <span class="hljs-number" style="color: #008080; 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; line-height: 26px;">1</span>) % <span class="hljs-number" style="color: #008080; line-height: 26px;">4</span>;
    row += directions[dIdx][<span class="hljs-number" style="color: #008080; line-height: 26px;">0</span>];
    col += directions[dIdx][<span class="hljs-number" style="color: #008080; 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; line-height: 26px;">1</span>; row &lt;= m - i - <span class="hljs-number" style="color: #008080; line-height: 26px;">1</span>; ++row) {
    results.push(matrix[row][n - j - <span class="hljs-number" style="color: #008080; 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; line-height: 26px;">1</span> &amp;&amp; j &lt; n - j - <span class="hljs-number" style="color: #008080; 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; line-height: 26px;">2</span>; col &gt; j; --col) {
    results.push(matrix[m - i - <span class="hljs-number" style="color: #008080; 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; line-height: 26px;">1</span>; row &gt; i; --row) {
    results.push(matrix[row][j]);
    }
    } i++;
    j++;

    }

    return results;

    }

最后

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

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

剑指offer - 顺时针打印矩阵 - JavaScript的更多相关文章

  1. 剑指Offer 顺时针打印矩阵

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

  2. 剑指OFFER——顺时针打印矩阵

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

  3. 剑指Offer顺时针打印矩阵

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

  4. 剑指Offer_Java_顺时针打印矩阵(二维数组)

    顺(逆)时针打印矩阵 算法思想: 简单来说,就是不断地收缩矩阵的边界 定义四个变量代表范围,up(初始0).down(初始-行高).left(初始-0).right(初始-列宽), 向右走存入整行的值 ...

  5. 剑指Offer-19.顺时针打印矩阵(C++/Java)

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

  6. 剑指offer--26.顺时针打印矩阵

    1,2,3,45,6,7,88,10,11,1213,14,15,16 每次输出第一行,然后删除第一行,逆时针旋转剩下的矩阵. ------------------------------------ ...

  7. python剑指offer 顺时针打印指针

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

  8. 用js刷剑指offer(顺时针打印数组)

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

  9. 剑指Offer18 顺时针打印矩阵

    /************************************************************************* > File Name: 18_PrintM ...

随机推荐

  1. Input输入框日期控件

    案例 https://pan.baidu.com/s/1i6BNLcT   密码:p77m

  2. 「NOWCODER」CSP-S模拟赛第3场

    「NOWCODER」CSP模拟赛第3场 T1 货物收集 题目 考场思路即正解 T2 货物分组 题目 考场思路 题解 60pts 算法:一维 DP 100pts 算法:一维 DP ?线段树 + 单调栈 ...

  3. 三 HTML框架标签

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  4. Kubernetes——YAML文件

    kubernetes——yaml文件的编写yaml文件的结尾后缀名.yaml或者.yml都能够识别.yaml文件就像脚本一样,可以放在任意的位置.编写yaml文件需要用到的帮助手册的查看: kubec ...

  5. Ajax--概述

    1.Ajax(Asynchronous JavaScript and XML),允许浏览器与服务器通信而无需刷新当前页面的技术都被叫做Ajax; 2.XMLHttpRequest:该对象是对JavaS ...

  6. jQuery父级以及同级元素查找的实例

    父级以及同级元素的查找在使用过程中还是蛮频繁的,下面为大家介绍下jQuery是如何实现的,感兴趣的朋友可以参考下 jQuery.parent(expr) 找父亲节点,可以传入expr进行过滤,比如$( ...

  7. 使用input:file控件在微信内置浏览器上传文件返回未显示选择的文件

    使用input:file控件在微信内置浏览器上传文件返回未显示选择的文件 原来的写法: <input type="file" accept="image/x-png ...

  8. Nginx配置详解 http://www.cnblogs.com/knowledgesea/p/5175711.html

    Nginx配置详解 序言 Nginx是lgor Sysoev为俄罗斯访问量第二的rambler.ru站点设计开发的.从2004年发布至今,凭借开源的力量,已经接近成熟与完善. Nginx功能丰富,可作 ...

  9. LINQ -- 匿名类型

    匿名类型注意事项: 匿名类型只能和局部变量配合使用,不能用于成员. 由于匿名类型没有名字,我们必须使用var关键字作为变量类型. 不能设置匿名类型对象的属性.编译器为匿名类型穿件的属性是只读的. 除了 ...

  10. 在CentOS 7环境下安装 Spark

    1.下载Spark安装包:http://mirror.bit.edu.cn/apache/spark/ 2.解压Spark的安装包并更改名称: (1)tar -zxvf spark-2.4.3-bin ...