原地翻转矩阵必须分两步:先沿主对角线转置(遍历上三角交换 matrixi 与 matrixj),再按垂直中线左右镜像(交换 matrixi 与 matrixi),二者均 O(1) 空间。

原地翻转必须先转置再镜像
直接按旋转后坐标一一赋值会覆盖未处理的元素,原地操作唯一可靠路径是分两步:先沿主对角线转置矩阵,再按垂直中线左右镜像。这两步都可严格在原数组上完成,空间复杂度 O(1)。
常见错误是试图用单层循环推导 i、j 到旋转后位置的映射(如 (j, n-1-i)),然后直接交换——这会导致部分元素被多次写入或漏写。必须把“写入目标”和“读取源”解耦,而转置+镜像天然满足该条件。
- 转置:只遍历上三角,交换
matrix[i][j]和matrix[j][i](i ) - 镜像:对每行,交换
matrix[i][j]和matrix[i][n-1-j](j ) - 两步顺序不可逆:先镜像再转置得到的是逆时针 90°
C++ 中 vector> 的索引边界易错点
vector 不是连续内存,但行列索引逻辑和二维数组一致。关键陷阱在于:转置时若用 matrix.size() 作外层循环上限,内层用 matrix[i].size(),当矩阵非正方形时会出错——顺时针 90° 翻转要求输入为方阵,否则维度不匹配。实际编码前应断言 matrix.size() == matrix[0].size()。
另一个坑是镜像循环的右边界:用 j ,而非 j ,否则奇数长度时中间列会被自己交换一次(无害但冗余);更严重的是,若误写成 j 就彻底乱序。
- 必须检查是否为空:空矩阵或首行为空时,
matrix[0].size()会越界 - 转置循环中,内层上限应为
matrix.size()(不是matrix[i].size()),因为转置后行列互换,但循环结构仍基于原尺寸 - 使用
size_t时注意与负数比较(如j >= 0)会触发隐式转换警告,建议统一用int索引
坐标变换公式在非原地场景下才直接可用
如果允许额外空间(如新建 vector),那么旋转后坐标映射 (i, j) → (j, n-1-i) 可直接用于赋值,代码简洁且不易错。此时时间复杂度仍是 O(n²),但空间涨到 O(n²)。
这个公式本身没问题,但它的适用前提是“读旧写新”,一旦混用新旧内存,就会因覆盖导致结果错误。有人尝试用临时变量缓存四个角来轮换,理论上可行,但四点轮换需分组(共 n²/4 组),且每组内索引推导极易出错,调试成本远高于转置+镜像。
- 新建结果矩阵时,初始化大小别写反:
vector,不是>(n, vector (n)) (n, n) - 映射式写法中,
n必须是运行时确定的边长,不能硬写4或matrix.size()在循环内反复调用(性能无影响,但可读性差) - 若输入是 C 风格二维数组(
int**),需额外传入n,且注意malloc后内存未初始化,要 memset 或逐个赋值
LeetCode 48 题的测试用例暗藏非方阵干扰项
LeetCode 第 48 题明确要求“n x n matrix”,但部分用户提交时用测试平台其他题目的非方阵样例去验证,发现输出异常——这不是算法问题,而是前提不满足。C++ 实现里若没加尺寸校验,遇到 [[1,2,3],[4,5]] 这类输入会直接崩溃或行为未定义。
另外,该题判题器检查的是原 matrix 对象是否被修改,所以即使你用了额外空间,也必须把结果拷回原容器(如用 swap 或逐行赋值),否则判为失败。用 matrix = result; 是安全的,但若 result 是局部变量,析构不影响原对象。
- 提交前务必用
[[1,2],[3,4]]和[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]验证 - 不要依赖
cout调试——LeetCode 禁止输出,会判为格式错误 - 最简健壮写法:开头加
if (matrix.empty() || matrix[0].empty()) return;,再跟assert(matrix.size() == matrix[0].size());
原地翻转的“原地”二字,不是指代码短,而是指内存访问模式必须避免读写冲突——转置和镜像各自独立、无重叠区域,这才是它真正不可替代的原因。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











