剑指offer - 顺时针打印矩阵 - JavaScript
题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下 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)] &&
newR >= <span class="hljs-number" style="color: #008080; line-height: 26px;">0</span> &&
newR < m &&
newC >= <span class="hljs-number" style="color: #008080; line-height: 26px;">0</span> &&
newC < 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 <= 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 < m - i - <span class="hljs-number" style="color: #008080; line-height: 26px;">1</span> && j < 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 > 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 > i; --row) {
results.push(matrix[row][j]);
}
}
i++;
j++;
}
return results;
}
最后
觉得不错,帮忙点个推荐呗,您的支持是对我最大的激励 欢迎我的公众号:「心谭博客」,只专注于前端 + 算法的原创分享
由于个人精力有限,很多系列和历史文章没有即时同步,请前往「前端图谱」&「算法题解」,保证您有所收获。

剑指offer - 顺时针打印矩阵 - JavaScript的更多相关文章
- 剑指Offer 顺时针打印矩阵
题目描述 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2, ...
- 剑指OFFER——顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8 ...
- 剑指Offer顺时针打印矩阵
题目描述 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数 ...
- 剑指Offer_Java_顺时针打印矩阵(二维数组)
顺(逆)时针打印矩阵 算法思想: 简单来说,就是不断地收缩矩阵的边界 定义四个变量代表范围,up(初始0).down(初始-行高).left(初始-0).right(初始-列宽), 向右走存入整行的值 ...
- 剑指Offer-19.顺时针打印矩阵(C++/Java)
题目: 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字 ...
- 剑指offer--26.顺时针打印矩阵
1,2,3,45,6,7,88,10,11,1213,14,15,16 每次输出第一行,然后删除第一行,逆时针旋转剩下的矩阵. ------------------------------------ ...
- python剑指offer 顺时针打印指针
题目描述 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数 ...
- 用js刷剑指offer(顺时针打印数组)
题目描述 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数 ...
- 剑指Offer18 顺时针打印矩阵
/************************************************************************* > File Name: 18_PrintM ...
随机推荐
- Python 之并发编程之进程下(事件(Event())、队列(Queue)、生产者与消费者模型、JoinableQueue)
八:事件(Event()) # 阻塞事件: e = Event() 生成事件对象e e.wait() 动态给程序加阻塞,程序当中是否加阻塞完全取决于该对象中的is_set() [默认返回值 ...
- 网络编程之TCP三次握手,四次断开
目录 TCP三次握手 1:上图的名词解释 2:TCP三次握手过程 3:为什么不能改成两次握手? TCP三次握手 1:上图的名词解释 SYN:同步序号.它表示建立连接.TCP规定SYN=1时不能携带数据 ...
- 吴裕雄--天生自然PYTHON爬虫:爬虫攻防战
我们在开发者模式下不仅可以找到URL.Form Data,还可以在Request headers 中构造浏览器的请求头,封装自己.服务器识别浏览器访问的方法就是判断keywor是否为Request h ...
- c++实现线性表中的顺序表(数据结构课程作业)
一.题目简介 实现顺序表的创建.初始化.赋值.插入.删除.按数据查找元素位置.按元素位置查找数据.清空.删除 的操作,以上操作用可用菜单选项完成 二.源程序代码 #include<iostrea ...
- dir815_FW_102.bin路由器固件解压碰到的坑
在跟随大神kczwa1进行路由器漏洞分析时,对dir815_FW_102.bin 固件文件用binwalk -e dir815_FW_102.bin命令进行解压时,在根目录squashfs-root下 ...
- Xshell 5的快捷键
Xshell 5的快捷键 1. 点击下图中的按钮查看快捷键: 2. 快捷键备忘录: 序号 功能 快捷键 备注 1 在窗口和撰写栏之间切换 Alt+I 2 全屏 Alt+Enter 3 ...
- 联想电脑硬盘保护系统EDU8.0.1iso安装
管理306机房4年了,15年我带领的第一批学生参加吉林省职业院校技能大赛的时候,领导把这个机房交给我负责.那个时候这个机房的机器是全校的顶配,30台DELL16G内存,2T硬盘,I7处理器,后面是6组 ...
- MyBatis学习之简单增删改查操作、MyBatis存储过程、MyBatis分页、MyBatis一对一、MyBatis一对多
一.用到的实体类如下: Student.java package com.company.entity; import java.io.Serializable; import java.util.D ...
- PyCharm无法找到已安装的Python类库的解决方法
一.问题描述 软件系统:Windows10.JetBrains PyCharm Edu 2018.1.1 x64 在命令行cmd中安装python类库包Numpy.Matplotlib.Pandas. ...
- Intel欲与AMD共同做大PC市场
自从2017年发布锐龙处理器以来,AMD在高性能处理器市场上正在恢复失地,CPU市场份额在今年Q1季度已经提升到了13.3%,要知道一年前不过8.6%而已.前面两代锐龙处理器相比Intel酷睿在单核性 ...