LeetCode刷题笔记第1260题

题目描述:

给你一个 m 行 n 列的二维网格 grid 和一个整数 k。你需要将 grid 迁移 k 次。每次「迁移」操作将会引发下述活动:

 

  • 位于 grid[i][j] 的元素将会移动到 grid[i][j + 1]
  • 位于 grid[i][n - 1] 的元素将会移动到 grid[i + 1][0]
  • 位于 grid[m - 1][n - 1] 的元素将会移动到 grid[0][0]

 

请你返回 k 次迁移操作后最终得到的 二维网格。示例:

  输入:grid = [[1,2,3],[4,5,6],[7,8,9]], k = 1
  输出:[[9,1,2],[3,4,5],[6,7,8]]
  输入:grid = [[3,8,1,9],[19,7,2,5],[4,6,11,10],[12,0,21,13]], k = 4
  输出:[[12,0,21,13],[3,8,1,9],[19,7,2,5],[4,6,11,10]]

约束条件:

  • 1 <= grid.length <= 50
  • 1 <= grid[i].length <= 50
  • -1000 <= grid[i][j] <= 1000
  • 0 <= k <= 100

解题思路:

  我的思路是将二维数组抽象成一个一维的数字序列,那么根据题目的转移要求可以知道k次迁移其实就是将这个一维的数字序列的后k个数字转移到数字序列的前方部分,整个过程可以想象成一个环形运动。所以我借助两个一维数组分别存储前n-k个元素和后k个元素,然后对这两个数组进行串行遍历,将元素存储到集合当中去。时间复杂度为O(m*n+(m+n))。代码如下所示:

    List<List<Integer>> res_list;

    public List<List<Integer>> shiftGrid(int[][] grid, int k) {
        if (k % (grid.length * grid[0].length) == 0){
            res_list = new LinkedList<>();
            for (int[] g : grid){
          
//Integer[] gg = IntStream.of(g).boxed().collect(Collectors.toList()).toArray(new Integer[0]); List<Integer> ls = IntStream.of(g).boxed().collect(Collectors.toList()); res_list.add(ls); } return res_list; } int temp_len = grid.length * grid[0].length; k = k % (temp_len); int[] temp = new int[temp_len-k]; int[] temp2 = new int[k]; int index = 0; for (int[] ints : grid) { for (int anInt : ints) { if (index < (temp_len-k)) temp[index] = anInt; else{ temp2[index-(temp_len-k)] = anInt; } index++; } } LinkedList<Integer> temp_list = new LinkedList<>(); res_list = new LinkedList<>(); for (int i =0; i <= temp_len; i++) { if (i % grid[0].length == 0 &&(i >= grid[0].length)){ res_list.add(temp_list); temp_list = new LinkedList<>(); } if (i < k) temp_list.add(temp2[i]); else if (i < temp_len) temp_list.add(temp[i-k]); } return res_list; }

注意:

  在写程序时犯了一些知识点的错误,就是不能往集合中存储基本数据类型,只能存储对象的引用。每个集合元素都是一个引用变量,实际内容都存放在堆内或方法区里面,但是基本数据类型是在栈内存上分配空间的,栈上的数据随时会被收回。所以可以通过包装类,把基本数据类型转化为对象类型,存放引用。更方便的,由于有了自动拆箱和装箱功能,基本数据类型和其对应对象之间的转换变得很方便,把基本数据类型存入集合中可以自动存,系统会自动将其装箱成封装类,然后将其加入到集合当中。

  但是,在本程序中的ls集合计算时,我通过增强for循环依次获取二维数组的每一行作为一维数组,当我将这个一维数组直接通过Arrays.asList转换成集合后再将其存入结果集合会发生编译错误,提示no instance(s) of type variable(s) exist so that int[] conforms to Integer错误,所以我将g数组中的元素转换为包装类类型,然后再存入结果集合,具体的代码如上面所示。

 

其他更好的解法:取模运算

这个解法是Leetcode给出的官方解法,所以学习一下,时间复杂度为O(m*n)。具体的思路如下:

  二维数组移动的问题上,除了模拟方法,直接计算元素迁移后的新位置更加高效。计算新位置分为两步:

  1. 什么是新列?
  2. 什么是新行?

  假设在一个三行五列的网格中,位于 i = 1 和 j = 3 处的值,迁移次数 k = 88

  第一步:计算新列值

  k步迁移之后,列值改变k次,每一步,列值都会改变一次,然而网格并不是无限大的,元素在横向上可以想象是在进行循环运动,因为列数是5所以每运动5次元素就会返回到原始的列位置(注意是列位置,并不是精确位置)。

  所以k步迁移后,元素的列位置可以通过(88+3)%5计算出,所以新的列位置为1;

  抽象为一般公式为:new_col = (j + k) % num_cols,j为元素起始列值,num_cols为网格总的列数。

  第二步:计算新行值

  行值变换并不频繁,只有当列值从最后一列变为第0列才会使得行值发生改变,同时元素在纵向上也是一个循环运动,所以要确定新的行值,需要确定从最后一列移动到第0列的次数,这里使用商计算行移动的次数。

  抽象为一般公式:new_row = (i + (j + k) /num_cols) % num_rows,i为元素起始行值,num_rows总行数

  代码如下:

public List<List<Integer>> shiftGrid(int[][] grid, int k) {

        int numCols = grid[0].length;
        int numRows = grid.length;

        // Setup the 2d list.
        List<List<Integer>> newGrid = new ArrayList<>();
        for (int row = 0; row < numRows; row++) {
            List<Integer> newRow = new ArrayList<>();
            newGrid.add(newRow);
            for (int col = 0; col < numCols; col++) {
                newRow.add(0);
            }
        }

        for (int row = 0; row < numRows; row++) {
            for (int col = 0; col < numCols; col++) {
                int newCol = (col + k) % numCols;
                int wrapAroundCount = (col + k) / numCols;
                int newRow = (row + wrapAroundCount) % numRows;
                newGrid.get(newRow).set(newCol, grid[row][col]);
            }
        }

        return newGrid;
    }

注意:List集合是有索引的,所以可以通过索引访问集合。

  

 

posted @ 2020-03-21 00:59  有心有梦  阅读(204)  评论(0编辑  收藏  举报