JS版数据结构第四篇(矩阵)

本篇博客参考银国徽老师的《Javascript版数据结构与算法》

背景

其实从严格意义上来说矩阵不能算一种典型的数据结构javascript

而之因此把矩阵放到这个系列里面是由于矩阵在面试和笔试中是很是高频出现的java

今天咱们将会用js实现两道LeetCode上两道比较经典的矩阵相关的算法题面试

矩阵的定义对于大学学习过《线性代数》这门课程的同窗们来说应该都不会很陌生,若是有同窗不了解能够自行查下百度百科。算法

很少废话,咱们直接看题。数组

螺旋矩阵

LeetCode第54题 原题地址bash

题目难度: 中等数据结构

题目描述

给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的全部元素。

示例1:函数

输入:
[
 [ 1, 2, 3 ],
 [ 4, 5, 6 ],
 [ 7, 8, 9 ]
]
输出: [1,2,3,6,9,8,7,4,5]复制代码

示例2:学习

输入:
[
  [1, 2, 3, 4],
  [5, 6, 7, 8],
  [9,10,11,12]
]
输出: [1,2,3,4,8,12,11,10,9,5,6,7]复制代码

题目理解

题目要求的是咱们按照顺时针的顺序从外向内遍历每个元素,并将他们按顺序返回出来。ui


根据这张图咱们应该很清除的理解题目的要求了,题目要求的元素的返回顺序就是图中的1到16

思路分析

咱们能够注意到:

不管是几×几的矩阵,都是须要咱们先将外圈遍历完成后才进入到它相邻内圈元素的遍历

如上图中咱们将1-12看做第一圈,将13-16看做第二圈,第一圈所有遍历完成后才会遍历第二圈

若是有阶数更高的矩阵(更多圈数),还会有第三圈,第四圈的遍历

因此咱们能够把每一圈的遍历视为一个步骤,不断地重复这个步骤就会获得咱们须要的结果

相信有一些同窗已经猜出咱们要用递归解决这道题目了

接下来咱们看一下代码具体是怎样实现的

代码实现

arr => {
    // map函数用来完成当前矩阵最外一圈的遍历
    // @param1{Array}二维数组 arr 表示当前矩阵
    // @param2{Array}一维数组 result 用来保存遍历结果 
    let map = (arr, result) => {
        // 矩阵的高度即行数
        let n = arr.length
        // 遍历矩阵的每一行
        for(let i = 0; i < n; i++){
            // 若第一行 按顺序插入
            if(i === 0){
                result = result.concat(arr[i])
            } else if (i === n-1){
                // 若最后一行 倒序插入
                result = result.concat(arr[i].reverse())
            } else {
                // 若中间行 插入该行最后一个元素 并将该元素从矩阵中删除
                result.push(arr[i].pop())
            }
        }
        // 将已经遍历的第一行和最后一行从矩阵中删除
        arr.pop()
        arr.shift()
        // 遍历插入最左侧一列 此时删除首位两行后矩阵高度已变为n-2
        for(let j = n - 3; j >= 0; j--){
            // 避免arr[j]长度为空时插入undefined
            if(arr[j].length){
                result.push(arr[j].shift())
            }
        }
        // 截止条件 矩阵有元素就继续递归
        if(arr.length){
            // 把已将遍历元素删除的矩阵进行递归
            return map(arr, result)
        }else{
            return result
        }
    }
    // 将初始矩阵传入, 保存结果的数组初始为空
    return map(arr, [])
}复制代码

思考

可能有些同窗不太清除咱们是怎样想到经过递归解决这样的问题。

这里根据我的的一些经验 我总结了一下知足递归的三个通用条件:

  • 能够将一个操做拆解为处理过程相同的小步骤
  • 每次步骤的输入数据格式都相同
  • 运行次数未知

在这个题目中

  • 首先咱们将顺时针螺旋遍历拆解为每一圈的遍历
  • 咱们最开始接收一个二维数组(矩阵),每次遍历完成一圈后传入删除最外圈元素的新矩阵,它一样是一个二维数组
  • 递归直到矩阵为空(传入的矩阵宽高并不肯定)

符合了递归的三个条件。

你们从此碰到相似的题目就能够参照这三点来判断是否可使用递归。

旋转图像

LeetCode第48题 原题地址

题目难度:中等

题目描述

给定一个 n × n 的二维矩阵表示一个图像。 将图像顺时针旋转 90 度。 
说明: 你必须在原地旋转图像,这意味着你须要直接修改输入的二维矩阵。
请不要使用另外一个矩阵来旋转图像。

示例1:

给定 matrix =
 [
     [1,2,3], 
     [4,5,6], 
     [7,8,9] 
], 
 原地旋转输入矩阵,使其变为: 
[
    [7,4,1], 
    [8,5,2], 
    [9,6,3] 
]

示例2:

给定 matrix = 
[
    [ 5, 1, 9,11],
    [ 2, 4, 8,10],
    [13, 3, 6, 7],
    [15,14,12,16]
]
 原地旋转输入矩阵,使其变为: 
[
    [15,13, 2, 5],
    [14, 3, 4, 1],
    [12, 6, 8, 9],
    [16, 7,10,11]
]

题目理解

题目很好理解,直接将矩阵顺时针旋转,有点相似于咱们平时使用的将图片顺时针旋转90度的功能

而这个题目之因此叫作'旋转图像'也正是由于这道题目的实现方法就是图片旋转功能的原理。

思路分析

表面上看这道题很简单,咱们只须要创建一个新的矩阵(二维数组),而后遍历原矩阵matrix的每一

列,按照从下到上的顺序添加至新矩阵的每一行中便可。

可是这道题的难度就在于题目中要求咱们“原地旋转图像”,这意味着咱们不能够新建一个二维数

组(矩阵),而后向里面添加数据,咱们只能处理原矩阵,那处理原矩阵的话咱们除了将数字交换位置

之外并无其余的方法了。

那具体怎样交换呢?

以示例1为例,首先咱们以中间行为轴,将矩阵进行轴对称


而后咱们再以对角线753做为轴进行轴对称


此时就获得了咱们想要的结果,接下来咱们用代码实现它

代码实现

arr => {
    // 获取矩阵阶数
    let n = arr.length
    let temp
    // 垂直翻转 考虑n的奇偶
    for(let i = 0; i < Math.floor(n / 2); i++){
        for(let j = 0; j< n; j++){
            temp = arr[i][j]
            arr[i][j] = arr[n -1 -i][j]
            arr[n -1 -i][j] = temp
        }
    }
    // 沿对角线反转
    for(let p = 0;p < n - 1; p++){
        for(let q = p + 1; q < n; q++){
            temp = arr[p][q]
            arr[p][q] = arr[q][p]
            arr[q][p] = temp
        }
    }
    return arr
}复制代码

总结

LeetCode上面也有不少有关矩阵的题目,你们均可以去尝试着作一下,若是对个人方法有疑问也欢迎你们来评论区探讨,多交流才会有所进步,但愿你早日成为大牛。

相关文章
相关标签/搜索