原地顺时针旋转90°的正确做法是按环四点轮换:对第k层环(k从0到n/2-1),列索引j从k到n-2-k,依次将位置(k,j)、(j,n-1-k)、(n-1-k,n-1-j)、(n-1-j,k)的元素循环搬运,使用一个临时变量,避免覆盖。

直接用坐标变换,别先转置再翻转
原地顺时针旋转 90° 的常见误区是「先转置 + 再水平翻转」,这需要两次遍历且逻辑割裂。实际只需一个公式:new_row = col,new_col = n - 1 - row。对每个 (row, col),它最终会落到 (col, n-1-row)。这个映射是单射且满射,只要按特定顺序赋值(比如从外层向内层、每层按顺时针四点轮换),就能原地完成,避免额外空间。
关键点在于轮换顺序:取左上角为起点,依次把「上→右→下→左」四个位置的值循环搬运。否则会出现值被提前覆盖。
- 必须按环处理,不能逐行扫:对
n x n矩阵,共n/2层环,第k层的行列范围是[k, n-1-k] - 每层内,列索引从
k扫到n-2-k(闭区间),避免重复或越界 - 四点轮换时,用临时变量存
matrix[k][j],再依次赋值:右←上、下←右、左←下、上←左
std::vector> 的 in-place 旋转要小心内存布局
std::vector<vector>></vector> 不是连续内存,每行是独立分配的指针。所以即使你做原地轮换,也不能指望缓存友好——相邻逻辑行在物理内存中可能相距很远。如果性能敏感(比如大矩阵 > 1000×1000),应改用一维 std::vector<int></int> 模拟二维,用 index = row * n + col 访问。
否则你会看到明明算法 O(1) 空间,但实测比 memcpy 版本还慢——主因是 TLB miss 和 cache line 跳跃。
立即学习“C++免费学习笔记(深入)”;
- 若必须用嵌套 vector,至少预留容量:
matrix.reserve(n); for (auto& row : matrix) row.reserve(n); - 避免在循环中调用
matrix.size()或row.size(),提前提取为 const int 变量 - 编译时加
-O2且禁用调试符号,否则 vector 的边界检查会拖慢明显
memcpy 版本适合只读场景或小矩阵
当矩阵只旋转一次、后续全读取,或者 n ,直接申请新矩阵 + 一次 memcpy 风格拷贝反而更清晰、更不容易出错。此时重点不是省空间,而是避免轮换逻辑写错导致静默错误(比如某层少迭代一次,末尾几行不动)。
示例核心循环:
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
rotated[j][n-1-i] = matrix[i][j];
}
}
- 注意目标索引是
[j][n-1-i],不是[n-1-j][i](那是逆时针) - 源矩阵用
[i][j]行优先遍历,目标用[j][...]列优先写入,CPU 缓存不友好,但小矩阵影响不大 - 如果编译器支持,可加
#pragma omp parallel for并行外层循环,提速约 2–3×(仅限无依赖场景)
边界检查和 n == 0 或 n == 1 必须显式处理
所有实现都容易漏掉空矩阵或单元素情况。C++ 中 vector<vector>></vector> 可能为空,或某行为空;n == 0 时,n/2 == 0,环数为 0,但若没判空就访问 matrix[0] 会崩溃;n == 1 时轮换循环不执行,但若忘了 return 也可能隐含未定义行为。
- 开头加:
if (matrix.empty() || matrix[0].empty()) return; - 确认
n = matrix.size()后,再断言matrix[i].size() == n(非方阵需另处理,本文不覆盖) - 轮换循环的上界用
n / 2,整数除法天然向下取整,n=1时循环体不进入,安全
实际最难的不是公式,是轮换时四个坐标的索引写错一位——比如把 n-1-j 写成 n-j,或者内层循环上限写成 n-k 而不是 n-1-k,这种 bug 不报错,但结果偏移一格,极难调试。


















