zoukankan      html  css  js  c++  java
  • 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集合是有索引的,所以可以通过索引访问集合。

      

  • 相关阅读:
    类加载器加载class文件
    多线程用到的概念知识点
    3年工作经验你的程序员应该具备的技能
    面试题
    正则表达式(一)
    Servlet(五)----ServletContext对象
    Servlet(四)----HTTP、Response、servletContent
    JDBC(三)----Spring JDBC(JDBCTemplate)
    JDBC(四)----数据库连接池
    JDBC(三)----JDBC控制事务
  • 原文地址:https://www.cnblogs.com/yxym2016/p/12536034.html
Copyright © 2011-2022 走看看