LeetCode 566重塑矩阵 指针操作理解笔记
LeetCode 566重塑矩阵 指针操作理解笔记
题目来源: 力扣题库(LeetCode) 566. 重塑矩阵
前言:
本来刚看到题目的时候,我是很开心的,这题不就是把二维数组“排扁”成一维数组,然后再排列成一个新的二维数组嘛!关键就是把一维数组重新映射回二维数组的关系式,这个我熟!(OS:终于有道名副其实的简单题了)但是LeetCode给出的待补充函数里一堆一级二级指针、还要涉及动态内存分配,属实把我整沉默了TAT,再翻翻官方给出的答案,凡是跟指针有关的我貌似是一句都看不懂呐(╯‵□′)╯︵┻━┻
一览题目
- 重塑矩阵
在 MATLAB 中,有一个非常有用的函数 reshape ,它可以将一个 m x n 矩阵重塑为另一个大小不同(r x c)的新矩阵,但保留其原始数据。
给你一个由二维数组 mat 表示的 m x n 矩阵,以及两个正整数 r 和 c ,分别表示想要的重构的矩阵的行数和列数。
重构后的矩阵需要将原始矩阵的所有元素以相同的 行遍历顺序 填充。
如果具有给定参数的 reshape 操作是可行且合理的,则输出新的重塑矩阵;否则,输出原始矩阵。
示例 1:
输入:mat = [[1,2],[3,4]], r = 1, c = 4
输出:[[1,2,3,4]]
示例 2:
输入:mat = [[1,2],[3,4]], r = 2, c = 4
输出:[[1,2],[3,4]]
提示:
m == mat.length
n == mat[i].length
1 <= m, n <= 100
-1000 <= mat[i][j] <= 1000
1 <= r, c <= 300
一览代码
/** * Return an array of arrays of size *returnSize. * The sizes of the arrays are returned as *returnColumnSizes array. * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). */ int** matrixReshape(int** mat, int matSize, int* matColSize, int r, int c, int* returnSize, int** returnColumnSizes){ int n = matColSize[0]; int m = matSize; if(r * c != m * n) { *returnColumnSizes = matColSize; *returnSize = matSize; return mat; } int **ans = malloc(sizeof(int *) * r); *returnColumnSizes = malloc(sizeof(int) * r); *returnSize = r; for(int i = 0; i < r; i ++) { (*returnColumnSizes)[i] = c; ans[i] = malloc(sizeof(int) * c); } for(int i = 0; i < m * n; i ++) { ans[i / c][i % c] = mat[i / n][i % n]; } return ans; }
对各个参数的理解
对于LeetCode给出的函数的参数
int** matrixReshape(int** mat, int matSize, int* matColSize, int r, int c, int* returnSize, int** returnColumnSizes)
其中:
mat
为整型的二级指针,是原始矩形(二维数组)
matSize
为整型变量,是矩阵的行数
matColSize
为整型变量,是矩形的列数
r
为整型变量,是重塑矩形的行数
c
为整型变量,是重塑矩形的列数
returnSize
为整型的一级指针,是重塑矩阵的行数(用于返回)
returnColumnSizes
为整型的二级指针,是重塑矩形各行的列数(一维数组)(LeetCode莫名奇妙的数据要求???)
对指针操作的理解
课本上有指针和指针变量之分,指针是地址的形象化的名称,是一个常量!而指针变量是存放地址的变量!为了方便,所以这里都叫做指针了😸
通过声明一个二级指针,开辟一个二维数组
//声明二级指针部分 int **ans = malloc(sizeof(int *) * r);
//循环开辟二维数组部分 for(int i = 0; i < r; i ++) { (*returnColumnSizes)[i] = c;//这条下面再讲(*^_^*)、 /* 开辟出容量为c个int的内存,将其首地址赋给ans[i] */ ans[i] = malloc(sizeof(int) * c); }
二级指针指向一级指针,解引用得到一级指针指向的变量的地址。
声明部分
中,开辟出r
个int
指针,这r
个指针的内存是连续的。而且,malloc
返回的是在连续内存上的这r
个int
指针的首地址。将其赋给ans
后,我们得到了一个一维指针数组。如果将二维数组比作一栋楼的话,那么到这里我们已经把到达每一层(行)的电梯门(一级指针)搭好了!我们只需搭着电梯(二级指针)到每层楼的电梯门,就可以访问该层的房间啦!所以,接下来的任务是开辟出每层楼的房间!
问题来咯:这时候可以将整型赋值给数组了吗?其实只要将单个整型变量的地址赋给每个一级指针,那么我们就得到一个一维数组啦😅,那么如果是多个地址相邻的整型变量呢?
循环部分
中,ans[i]
访问的是第i
个一级指针(搭着电梯到了第i层的电梯门),而malloc
返回的是在连续内存上的c
个int
变量的首地址,也就是一个长度为c
的一维数组的首地址(相当于1号房间)。将其赋给一级指针ans[i]
后,一级指针ans[i]
便存放着一个一维数组的首地址,那么ans[i]
此时就是一个一维数组啦。我们就可以从每个电梯门出来,访问各个房间啦。也就是说,我们可以通过ans[i][j]
访问数组中的每个元素啦哈哈哈~(((φ(◎ロ◎;)φ)))
再贴个图帮助理解^____^
小小总结一下
抽象来说,整个过程就是二级指针指向一级指针数组的首元素,然后这个数组内的各个一级指针,又指向一个一维数组的首元素。通过开辟连续的内存,再将其首地址赋值给指针,就有了:
ans
是一个指针数组,元素是一级指针;ans[i]
是一个一维数组,元素是整型变量。
我的形象理解是:通过一架电梯(二级指针),到达每层的电梯门(二级指针指向一级指针),再从电梯门出来访问该层的各个房间(一级指针指向整型变量)。
通过二级指针,开辟一维数组([ ]和*的优先级问题)
*returnColumnSizes = malloc(sizeof(int) * r);
理解了上文,这个就更好理解了:把多个在连续内存上的整型变量(相当于一个一维数组)的首地址赋给了一级指针*returnColumnSizes
,而二级指针returnColumnSizes
指向一级指针,对二级指针解引用得到的是一维数组的首地址。
我们看下面这段代码
for(int i = 0; i < r; i ++) { (*returnColumnSizes)[i] = c; ans[i] = malloc(sizeof(int) * c);//这条上面讲过了,忽略! }
(*returnColumnSizes)
是一个一维数组,为什么要包含于括号内呢?自然是优先级的问题:
优先级 | 运算符 |
---|---|
1 | 数组下标 [ ] |
2 | 取值运算符 * |
可见,如果是这样的*returnColumnSizes[i] = c
,那么运算的过程就是:对returnColumnSizes + i
解引用,得到某个不存在的一级指针数组的第i
个元素,再对这个元素解引用,获得该元素(一级指针)指向的整型变量。问题是我们并没有开辟出一级指针数组的内存(所以这元素不存在),指定是越界访问内存了,所以提交的时候就寄了O(∩_∩)O😅😅😅
讲讲题解
其实就是一个映射的公式的事~
其中
其中
开辟一维数组的两种方法
以整型的数组为例
使用一级指针
//开辟长度为n的一维数组,p指向数组的首元素 int *p = (int *) malloc(sizeof(int) * n); for(int i = 0; i < n; i ++) { p[i] = i; }
使用二级指针
//先开辟一个(一级)指针数组 int **p = (int **) malloc(sizeof(int *) * n); for(int i = 0; i < n; i ++) { //让每个(一级)指针指向一个整型变量 pp[i] =(int *) malloc(sizeof(int)); *pp[i] = i; }
实际上,*pp[i] = i
还可以写成pp[i][0] = i
,但是pp[i]
指向的是一个整型(在内存上是孤立的)。换句话说,pp[i]
指向的不是一个一维数组(各元素在内存上是连续的)的首元素。pp[i][0]= i
的写法容易产生误导,例如pp[i][1] = i
就会越界访问内存了!
测试代码
#include<stdio.h> #include<stdlib.h> #include<string.h> void Print_Array1(int **pp, int len) { int *array = *pp; printf("The array is: ["); for(int i = 0; i < len; i ++) { printf("%3d", array[i]); if(i < len - 1) printf(","); } printf("]\n"); } void Print_Array2(int **pp, int len) { printf("The array is: ["); for(int i = 0; i < len; i ++) { printf("%3d", *pp[i]); if(i < len - 1) printf(","); } printf("]\n"); } int **Pointer_to_Array(int n) { int **pp = (int **) malloc(sizeof(int *)); *pp = (int *) malloc(sizeof(int) * n); for(int i = 0; i < n; i ++) { (*pp)[i] = i; } return pp; } int **PtoP_to_Array(int n) { int **pp = (int **) malloc(sizeof(int *) * n); for(int i = 0; i < n; i ++) { pp[i] =(int *) malloc(sizeof(int)); *pp[i] = i; } return pp; } int main() { int n = 10; printf("使用一级指针开辟一维数组:\n"); Print_Array1(Pointer_to_Array(n), n); printf("使用二级指针开辟一维数组:\n"); Print_Array2(PtoP_to_Array(n), n); return 0; }