力扣刷题之路------数组的旋转、遍历

参考刷题顺序:力扣刷题顺序

189 轮转数组

在这里插入图片描述
自己的想法: 类似于题目中的解释,一次性的将数字放到最后它应该在的位置,想法和官方的第二个思路差不多,如图。
在这里插入图片描述
可以看出来,一类的循环(绿色是一类),需要在循环到出现闭合的时候就结束,并且要往后移动一格,再次循环。整个大的循环结束条件很容易判断,就是循环了nums.length次时就可以结束了。此处用count来做算法结束的标志。一个小循环的结束需要判断是否这个循环闭合了,即这个循环是不是又到了循环刚开始的位置,这里的想法是把循环开始的位置的值变成一个标志数,每次一个数值完成了它的移动之后,要判断下一个移动是不是到标志位了。如果到了,那么这个小循环就结束了,如果大循环还没有结束,那么需要新开一个小循环,即需要把标志位往后移动。说起来有点绕。代码也有点乱。感觉看图片其实就很容易理解了。这里把标志设置成-1是偷了个懒。

    public void rotate(int[] nums, int k) {
        int temp=nums[0],temp1;
        int count = 0;
        if(k%nums.length != 0){
            nums[0] = -1;
            for(int i=0;i< nums.length;){
                temp1 = nums[(i+k)%nums.length];
                nums[(i+k)%nums.length] = temp;
                temp = temp1;
                count++;
                if(count >= nums.length) break;
                if(nums[(i+k+k)%nums.length]==-1){
                    nums[(i+k+k)%nums.length] = temp;
                    i = (i+k+k+1)%nums.length;
                    temp = nums[i];
                    count++;
                    if(count==nums.length) break;
                    nums[i] = -1;
                }
                else i = (i+k)%nums.length;
            }
        }
    }

官方的想法: 没看明白第二个环状替换的想法的后边具体是怎么搞的。不过第三个想法感觉很妙。以下图的情况为例,向右移动3个,那么最后三个就会变成最前面的三个,前面的四个就会变成最后的四个。翻转第一次,前后整体变化,翻转第二次,前3个顺序变正,翻转第三次,后四个顺序变正。
在这里插入图片描述

    public void rotate(int[] nums, int k) {
        reverse(nums,0,nums.length-1);
        reverse(nums,0,k% nums.length-1);
        reverse(nums,k% nums.length, nums.length-1);
    }
    public void reverse(int[] nums,int start,int end){
        while (start<end){
            int temp;
            temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start++;
            end--;
        }
    }

396 旋转函数

在这里插入图片描述
自己的想法: 本来的想法是发现不论数组里有几个负数,最大的值都不存在于Bk[0]>Bk[1]的情况。但是一直超时。看到别人的方法是在F0,F1,F2,F3之间找规律,发现F(N)和F(N-1)之间存在一个关系。即F(N)=F(N-1)+A-len(A[len-n])。写的可能不清楚,从F1-F0和F2-F1算一算就可以发现这个规律了。这个题目没有官方题解。这个也算是别人的想法吧

    public int maxRotateFunction(int[] nums) {
        int max=-2147483648;int sum=0,SUMALL=0;
        for(int i = 0;i<nums.length;i++) {
            SUMALL += nums[i];
            sum += i*nums[i];
        }
        max = Math.max(max,sum);
        for(int i=1;i<= nums.length;i++){
            sum += SUMALL-nums.length*(nums[nums.length-i]);
            max = Math.max(max,sum);
        }
        return max;
    }

54. 螺旋矩阵

在这里插入图片描述
自己的想法: 这个题目很容易理解,绕着圈旋转而已。难的是如何界定什么时候往哪里转。被这道题卡了好久。基本的想法就是,每个格子会有四个旋转的方向,判断上下左右的旋转条件就可以。然后用一个标志来判断次数,次数够的时候就停止。

    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> list = new ArrayList<Integer>();
        int u = 0,i=0,j=0;
        while (u<matrix.length*matrix[0].length){
            list.add(matrix[i][j]);
            if(i>=matrix[0].length-1-j && j>=(matrix[0].length)/2 && i< matrix.length-(matrix[0].length-j)) i++;//向下
            else if(j>=matrix.length-i && i>=(matrix.length+1)/2 && j<=matrix[0].length-(matrix.length-i)) j--;//向左 
            else if(j<matrix[0].length-1-i && i<=(matrix.length+1)/2 && j>=i-1) j++;//向右
            else if(j<(matrix[0].length+1)/2)i--;//向上
            u++;
        }
        return list;
    }

官方的想法: 看了看官方的想法好像确实是简单一些。第一个是需要增加一个数组来判断是否被遍历过,这个想法的空间复杂度太高,所以只认真看了第二个按层模拟的想法。按层模拟就是每次的输出是首先输出最外层,然后依次输出里面的一层。设置四个标志位,即左上、左下、右上、右下四个角,按照顺序遍历最外层,遍历完一层之后需要把标志位更新。懒得自己打了,这个题自己做的时候有点太耗心力了,这里粘上官网的代码。

class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> order = new ArrayList<Integer>();
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return order;
        }
        int rows = matrix.length, columns = matrix[0].length;
        int left = 0, right = columns - 1, top = 0, bottom = rows - 1;
        while (left <= right && top <= bottom) {
            for (int column = left; column <= right; column++) {
                order.add(matrix[top][column]);
            }
            for (int row = top + 1; row <= bottom; row++) {
                order.add(matrix[row][right]);
            }
            if (left < right && top < bottom) {
                for (int column = right - 1; column > left; column--) {
                    order.add(matrix[bottom][column]);
                }
                for (int row = bottom; row > top; row--) {
                    order.add(matrix[row][left]);
                }
            }
            left++;
            right--;
            top++;
            bottom--;
        }
        return order;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/spiral-matrix/solution/luo-xuan-ju-zhen-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

59. 螺旋矩阵 II

在这里插入图片描述
自己的想法: 这个题目和上边的题目可以说是一模一样。利用上一道题的代码简单修改一下就可以。这里输入的是n,就是说上一道题目中的matrix[0].length和matrix.length是一样的,都是n,把这两个都换成n。然后matrix数组中的元素是从1开始的,所以结束标志u改为从1开始,结束标志改为≤n*n。这样就可以提交了,哈哈哈。做出来一道题等于做出来两道题,感觉非常好。

    public int[][] generateMatrix(int n) {
        int [][] matrix = new int[n][n];
        int u = 1,i=0,j=0;
        while (u<=n*n){
            matrix[i][j] = u;
            if(i>=n-1-j && j>=(n)/2 && i< j) i++;//向下
            else if(j>=n-i && i>=(n+1)/2 && j<=i) j--;//向左 
            else if(j<n-1-i && i<=(n+1)/2 && j>=i-1) j++;//向右
            else if(j<(n+1)/2)i--;//向上
            u++;
        }
        return matrix;
    }

498. 对角线遍历

在这里插入图片描述
自己的想法: 要被这些个循环折磨疯了。参考前两道题中官方的一个思想,设置left,top,right,bottom作为一次小循环(即一个箭头穿过的值)的开始和结束的标志。这四个标志在一次小循环结束后进行更新,小循环当中的顺序有从下往上或者从上往下,这里也设置一个标志flag作为小循环顺序的标志,因为是从[1][1]开始的,所以刚开始的时候要往左下方进行遍历,这时设置flag=0;一次小循环结束后flag=1,表示下次的循环应该是往右上走。至于四个开始结束标志的更新,可以画几个不同行列数的数组来思考。如下图。
在这里插入图片描述
画的比较粗糙。总之就是没有到最后一行和最后一列的时候,箭头是往右往下移动,到最后一行的时候是往右移动,到最后一列的时候是往下移动。第一个版本写了个if else来判断数组的行数或者列数是1的情况,这种情况直接遍历输出也可以。后来删除了if else 竟然运行时间还慢了一些。

    public int[] findDiagonalOrder(int[][] mat) {
        int row = mat[0].length,len=mat.length;
        int[] result = new int[row*len];
        int u=0,flag=0;//从0行1列开始 flag=0表示往左下走,flag=1表示往右上走
        int left=0,right=0,top=0,bottom=0;
        result[0] = mat[0][0];
        while (left<=right && top<=bottom){
            if(bottom<len-1 && right<row-1){bottom++; right++; }//
            else if(right==row-1 && bottom<len-1){top++;bottom++;}
            else if(bottom==len-1 && right<row-1){left++;right++;}
            else {left++;top++;}
            if(flag == 0){//向左下走
                int i=top,j=right;
                while (i<=bottom && j>=left){
                    result[u+1] = mat[i][j];
                    i++;
                    j--;
                    u++;
                }
                flag=1;
            }
            else{//向右上走
                int i=bottom,j=left;//i=2 j=0
                while (i>=top && j<=right){
                    result[u+1] = mat[i][j];
                    i--;
                    j++;
                    u++;
                }
                flag=0;
            }
        }
        return result;
    }

刷题感想:这两个题目都是中等题,但是其实能把题读明白的话这两个题也不难。感觉想题目的解法的时候不能局限于题目给的解释,比如第一个翻转的题解就很妙。要把眼界打开思考问题。其实还是有点难的。题目太复杂反而有点不想看官方题解了。按照这个刷题顺序来刷确实挺快的!

posted @ 2022-05-03 21:28  啵啵ray  阅读(23)  评论(0编辑  收藏  举报