力扣刷题之路------数组的旋转、遍历
参考刷题顺序:力扣刷题顺序
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;
}
刷题感想:这两个题目都是中等题,但是其实能把题读明白的话这两个题也不难。感觉想题目的解法的时候不能局限于题目给的解释,比如第一个翻转的题解就很妙。要把眼界打开思考问题。其实还是有点难的。题目太复杂反而有点不想看官方题解了。按照这个刷题顺序来刷确实挺快的!