LeetCode 566重塑矩阵 指针操作理解笔记

LeetCode 566重塑矩阵 指针操作理解笔记

题目来源: 力扣题库(LeetCode) 566. 重塑矩阵

前言:

本来刚看到题目的时候,我是很开心的,这题不就是把二维数组“排扁”成一维数组,然后再排列成一个新的二维数组嘛!关键就是把一维数组重新映射回二维数组的关系式,这个我熟!(OS:终于有道名副其实的简单题了)但是LeetCode给出的待补充函数里一堆一级二级指针、还要涉及动态内存分配,属实把我整沉默了TAT,再翻翻官方给出的答案,凡是跟指针有关的我貌似是一句都看不懂呐(╯‵□′)╯︵┻━┻

一览题目

  1. 重塑矩阵

在 MATLAB 中,有一个非常有用的函数 reshape ,它可以将一个 m x n 矩阵重塑为另一个大小不同(r x c)的新矩阵,但保留其原始数据。

给你一个由二维数组 mat 表示的 m x n 矩阵,以及两个正整数 r 和 c ,分别表示想要的重构的矩阵的行数和列数。

重构后的矩阵需要将原始矩阵的所有元素以相同的 行遍历顺序 填充。

如果具有给定参数的 reshape 操作是可行且合理的,则输出新的重塑矩阵;否则,输出原始矩阵。

img

示例 1:
输入:mat = [[1,2],[3,4]], r = 1, c = 4
输出:[[1,2,3,4]]

img

示例 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);
}

  二级指针指向一级指针,解引用得到一级指针指向的变量的地址。
  声明部分中,开辟出rint指针,这r个指针的内存是连续的。而且,malloc返回的是在连续内存上的这rint指针的首地址。将其赋给ans后,我们得到了一个一维指针数组如果将二维数组比作一栋楼的话,那么到这里我们已经把到达每一层(行)的电梯门(一级指针)搭好了!我们只需搭着电梯(二级指针)到每层楼的电梯门,就可以访问该层的房间啦!所以,接下来的任务是开辟出每层楼的房间!

问题来咯:这时候可以将整型赋值给数组了吗?其实只要将单个整型变量的地址赋给每个一级指针,那么我们就得到一个一维数组啦😅,那么如果是多个地址相邻的整型变量呢?

  循环部分中,ans[i]访问的是第i个一级指针(搭着电梯到了第i层的电梯门),而malloc返回的是在连续内存上的cint变量的首地址,也就是一个长度为c一维数组首地址相当于1号房间)。将其赋给一级指针ans[i]后,一级指针ans[i]便存放着一个一维数组的首地址,那么ans[i]此时就是一个一维数组啦。我们就可以从每个电梯门出来,访问各个房间啦。也就是说,我们可以通过ans[i][j]访问数组中的每个元素啦哈哈哈~(((φ(◎ロ◎;)φ)))
再贴个图帮助理解^____^
img

小小总结一下

抽象来说,整个过程就是二级指针指向一级指针数组的首元素,然后这个数组内的各个一级指针,又指向一个一维数组的首元素。通过开辟连续的内存,再将其首地址赋值给指针,就有了:
  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😅😅😅

讲讲题解

其实就是一个映射的公式的事~

(i,j)<=>i×m+j=x

其中ij分别表示矩阵的某个数所在的行和列(与二维数组下标对应),nm代表矩阵的行数和列数,x代表一维数组中的第x个元素,再将x映射到n×m的矩阵上就是:

{i=x/mj=x÷m

其中÷是取模运算

开辟一维数组的两种方法

以整型的数组为例

使用一级指针

//开辟长度为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;
}

运行结果

img

posted @   YusJade  阅读(120)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示