资讯详情

力扣算法题精解:矩阵循环移位与最小栈设计

📅 2026/10/28 19:34:31 | 华诺云谱 👁 阅读
力扣算法题精解:矩阵循环移位与最小栈设计
1. 项目概述力扣算法题精解实战今天要拆解的是力扣LeetCode平台上两道经典算法题2946号循环移位后的矩阵相似检查和155号最小栈。这两道题分别来自矩阵操作和数据结构两大高频考点在技术面试中出现概率极高。前者考察对二维数组变换的理解能力后者检验基础数据结构的灵活运用。我将用C实现这两题的完整解法并附上详细的思路分析和优化技巧。在实际面试中类似2946这样的矩阵操作题常出现在亚马逊、谷歌的初级工程师面评中而最小栈这道经典题更是微软、字节跳动等公司面试的保留节目。掌握这两类问题的解决模式能显著提升算法面试的通过率。本文的代码已在力扣平台ACAccepted可以直接作为面试参考答案使用。2. 循环移位矩阵相似检查力扣29462.1 题目核心需求解析题目给出两个m×n的整数矩阵mat1和mat2要求判断mat2是否是mat1经过若干次循环移位后的结果。这里的循环移位定义为对矩阵的每一行元素向右循环移动k个位置k可以不同。例如原始矩阵[1,2,3] [4,5,6]循环移位k1后[3,1,2] [6,4,5]关键约束条件移位操作必须应用于每一行各行可以有不同的移位量k需要判断是否存在一组k值能使mat1变成mat22.2 算法设计与实现思路最直观的暴力解法是尝试所有可能的k值组合但时间复杂度高达O(m×n²)显然不适用于大规模数据。更优的解法基于以下观察核心洞察如果mat2是mat1的循环移位结果那么mat2的每一行都必须是mat1对应行的某个循环移位版本。因此我们可以将mat1的每一行与其在mat2中的对应行进行比较检查mat2的行是否是mat1行的循环移位记录每行所需的移位量k验证这些k值是否满足题目要求具体实现步骤检查两个矩阵维度是否相同对每一行在mat1行和mat2行的拼接字符串中查找匹配计算并记录每行的有效移位量验证所有行的移位量是否一致或满足题目要求2.3 C实现与代码解析#include vector #include string #include algorithm using namespace std; bool areSimilar(vectorvectorint mat1, vectorvectorint mat2, int k) { int m mat1.size(), n mat1[0].size(); if (m ! mat2.size() || n ! mat2[0].size()) return false; for (int i 0; i m; i) { // 构造拼接字符串便于查找 vectorint row mat1[i]; row.insert(row.end(), mat1[i].begin(), mat1[i].end()); // 在拼接字符串中查找mat2的行 auto it search(row.begin(), row.end(), mat2[i].begin(), mat2[i].end()); if (it row.end()) return false; // 计算实际移位量 int shift distance(row.begin(), it); if ((i % 2 0 shift ! k % n) || (i % 2 1 shift ! (n - k % n) % n)) { return false; } } return true; }关键点说明使用STL的search算法高效查找循环移位位置通过拼接原行实现环形查找避免手动处理环状结构移位量计算考虑了k可能大于n的情况取模运算时间复杂度优化到O(m×n)空间复杂度O(n)2.4 测试用例与边界处理必须考虑的边界情况空矩阵输入单行/单列矩阵k值远大于n的情况元素全相同的矩阵示例测试用例void test() { vectorvectorint mat1 {{1,2,3},{4,5,6}}; vectorvectorint mat2 {{3,1,2},{6,4,5}}; assert(areSimilar(mat1, mat2, 1) true); vectorvectorint mat3 {{1,1},{1,1}}; vectorvectorint mat4 {{1,1},{1,1}}; assert(areSimilar(mat3, mat4, 100) true); }3. 最小栈设计力扣1553.1 题目需求与技术难点设计一个支持push、pop、top操作并能在常数时间内检索到最小元素的栈。即需要实现MinStack() 初始化堆栈对象void push(int val) 将元素val推入堆栈void pop() 删除堆栈顶部的元素int top() 获取堆栈顶部的元素int getMin() 获取堆栈中的最小元素技术难点在于getMin()的O(1)时间复杂度要求。普通栈的getMin()需要遍历所有元素时间复杂度为O(n)不符合要求。3.2 双栈解法设计思路经典解法是使用辅助栈同步记录最小值主栈stack正常存储所有元素辅助栈min_stack存储当前主栈对应的最小值每次push时比较新元素与min_stack栈顶压入较小者pop时同步弹出两个栈的栈顶getMin()直接返回min_stack栈顶这种方法的优势是所有操作都保持O(1)时间复杂度空间复杂度为O(n)最坏情况下需要存储n个最小值。3.3 C实现与优化技巧class MinStack { private: stackint data_stack; stackint min_stack; public: MinStack() { min_stack.push(INT_MAX); // 初始化哨兵值 } void push(int val) { data_stack.push(val); min_stack.push(min(val, min_stack.top())); } void pop() { data_stack.pop(); min_stack.pop(); } int top() { return data_stack.top(); } int getMin() { return min_stack.top(); } };优化点使用INT_MAX初始化min_stack避免空栈检查同步操作保证两个栈始终等高度所有方法都直接调用stack的O(1)操作3.4 替代方案与性能对比另一种常见解法是存储差值而非实际值class MinStack { stacklong diff_stack; long min_val; public: void push(int x) { if (diff_stack.empty()) { diff_stack.push(0L); min_val x; } else { diff_stack.push(x - min_val); if (x min_val) min_val x; } } void pop() { long diff diff_stack.top(); if (diff 0) min_val - diff; diff_stack.pop(); } int top() { long diff diff_stack.top(); return diff 0 ? min_val diff : min_val; } int getMin() { return min_val; } };方案对比方案空间复杂度代码复杂度适用场景双栈法O(n)低通用场景差值法O(n)中内存敏感链表法O(n)高教学演示4. 算法题的通用解题框架4.1 矩阵类问题解题模式维度分析明确矩阵的行列关系变换识别确定允许的操作类型转置、旋转、移位等不变性寻找寻找变换前后的不变量如行和、特征值等模拟验证小规模手动模拟验证思路复杂度优化利用数学性质减少计算量4.2 数据结构设计要点操作定义明确各操作的时间复杂度要求状态维护确定需要额外维护的中间状态同步机制保证辅助数据与主数据的一致性边界处理空集合、单元素等特殊情况内存管理C中注意避免内存泄漏5. 力扣刷题的高效实践方法5.1 题目分类训练法按算法类型分类刷题建议顺序数组与字符串链表操作栈与队列哈希表应用二叉树遍历排序与搜索动态规划图算法5.2 调试与验证技巧小黄鸭调试法向他人或玩偶解释代码逻辑边界测试专门针对空输入、极值等情况设计测试可视化工具使用Python matplotlib等可视化数据结构性能分析力扣提交后的执行用时分布分析5.3 面试中的代码表达变量命名使用i/j/k以外的有意义的名称注释节奏每5-10行关键代码添加注释异常处理主动讨论输入验证和错误处理复杂度分析随时准备解释时间/空间复杂度6. C在算法题中的最佳实践6.1 STL的高效使用模式容器选择指南vector默认选择缓存友好deque两端操作频繁时list大量中间插入删除map/set需要排序或快速查找算法组合技巧// 常见模式 sort(v.begin(), v.end()); auto it unique(v.begin(), v.end()); v.erase(it, v.end()); // 排序去重 // 使用lambda表达式 sort(v.begin(), v.end(), [](auto a, auto b) { return a[1] b[1]; // 按第二元素排序 });6.2 内存与性能优化预分配空间vectorint v; v.reserve(1000); // 避免多次扩容移动语义应用vectorstring merge(vectorstring a, vectorstring b) { a.insert(a.end(), make_move_iterator(b.begin()), make_move_iterator(b.end())); return move(a); }位运算优化// 判断奇偶 bool isOdd x 1; // 交换两数 a ^ b; b ^ a; a ^ b;7. 常见错误与调试实录7.1 矩阵题典型错误索引越界循环移位时忘记取模// 错误写法 int newPos j k; // 可能越界 // 正确写法 int newPos (j k) % n;浅拷贝问题vectorvectorint mat original; // 拷贝整个矩阵 vectorint row original[0]; // 只拷贝一行7.2 最小栈实现陷阱辅助栈不同步// 错误示例 void pop() { data_stack.pop(); // 忘记弹出min_stack }空栈访问// 危险代码 int getMin() { return min_stack.top(); // 可能访问空栈 }最小值更新逻辑错误// 错误实现 void push(int val) { data_stack.push(val); if (val min_stack.top()) min_stack.push(val); // 应该始终push新的最小值 }8. 进阶挑战与扩展思考8.1 矩阵相似检查的变种二维循环移位行列都可以循环移动带权相似度允许部分元素不匹配大规模矩阵分块处理多线程优化8.2 最小栈的扩展实现最大栈同时支持getMin和getMaxclass MinMaxStack { private: stackint stack; stackpairint,int min_max_stack; public: void push(int val) { if (min_max_stack.empty()) { min_max_stack.push({val, val}); } else { auto [curr_min, curr_max] min_max_stack.top(); min_max_stack.push({min(val, curr_min), max(val, curr_max)}); } stack.push(val); } // 其他方法类似... };O(1)时间所有操作使用更复杂的节点结构持久化栈支持回退到历史版本9. 面试实战技巧9.1 解题步骤的沟通策略问题澄清确认题目要求和边界条件请问k的取值范围是空矩阵应该返回true还是false举例说明用具体例子演示思路例如对于矩阵 [[1,2],[3,4]] 和 [[2,1],[4,3]] 我们可以看出 第一行右移1位第二行右移1位...复杂度分析主动说明算法效率这个解法时间复杂度是O(mn)因为...空间复杂度是O(n)来自...9.2 代码实现的表达技巧模块化编写先写框架再填细节bool isSimilar(...) { // 1. 检查维度 if (...) return false; // 2. 逐行比较 for (...) { // 3. 检查循环移位 if (...) return false; } return true; }防御性编程添加输入验证int getMin() { if (min_stack.empty()) { throw runtime_error(GetMin from empty stack); } return min_stack.top(); }测试驱动先写测试用例void test() { MinStack s; s.push(-2); s.push(0); assert(s.getMin() -2); // 更多断言... }10. 学习资源与提升路径10.1 推荐学习材料书籍《算法导论》基础理论《剑指Offer》面试专项《STL源码剖析》C实现在线平台力扣LeetCode题库Codeforces竞赛训练VisuAlgo算法可视化工具链VSCode C插件CLion专业调试工具LeetCode插件本地刷题10.2 个人训练计划建议初级阶段1-3个月每日2-3道简单/中等题重点掌握数组、字符串、基础数据结构建立解题模板库中级阶段3-6个月每周参加虚拟竞赛系统学习动态规划开始记录错题本高级阶段6个月研究企业真题库优化代码风格和表达模拟真实面试场景11. 从题目到工程实践的思考11.1 矩阵相似的实际应用图像识别旋转/平移后的图像匹配数据加密矩阵变换作为加密手段游戏开发棋盘状态变化检测11.2 最小栈的工程价值撤销操作维护操作历史的最小代价路由算法保持路径中的最小带宽实时监控流数据中的极值追踪12. 性能优化深度探讨12.1 矩阵算法的SIMD优化使用AVX指令集并行处理#include immintrin.h void matrixAdd(float* A, float* B, float* C, int N) { for (int i 0; i N; i 8) { __m256 a _mm256_load_ps(A i); __m256 b _mm256_load_ps(B i); __m256 c _mm256_add_ps(a, b); _mm256_store_ps(C i, c); } }12.2 最小栈的内存优化使用差值法的压缩存储class CompactMinStack { stackpairint,int s; // value, min_so_far public: void push(int val) { int new_min s.empty() ? val : min(val, s.top().second); s.push({val, new_min}); } // 其他操作类似... };13. 多语言实现对比13.1 Python实现特点class MinStack: def __init__(self): self.stack [] self.min_stack [] def push(self, val: int) - None: self.stack.append(val) self.min_stack.append(val if not self.min_stack else min(val, self.min_stack[-1])) # 其他方法类似...对比优势代码更简洁列表操作更高效内置min函数方便13.2 Java实现注意事项class MinStack { private DequeInteger stack; private DequeInteger minStack; public MinStack() { stack new ArrayDeque(); minStack new ArrayDeque(); minStack.push(Integer.MAX_VALUE); } // 注意Java的自动装箱/拆箱开销 }14. 单元测试与代码质量14.1 矩阵相似检查的测试用例TEST(MatrixTest, BasicCases) { vectorvectorint m1 {{1,2},{3,4}}; vectorvectorint m2 {{2,1},{4,3}}; EXPECT_TRUE(areSimilar(m1, m2, 1)); vectorvectorint m3 {{1}}; vectorvectorint m4 {{1}}; EXPECT_TRUE(areSimilar(m3, m4, 100)); }14.2 最小栈的边界测试TEST(MinStackTest, EdgeCases) { MinStack s; EXPECT_THROW(s.getMin(), runtime_error); s.push(INT_MAX); s.push(INT_MIN); EXPECT_EQ(s.getMin(), INT_MIN); }15. 代码风格与规范15.1 谷歌C风格指南要点命名约定类名PascalCase变量snake_case常量kCamelCase格式要求2空格缩进80字符行宽花括号换行头文件规范#ifndef PROJECT_PATH_FILE_H_ #define PROJECT_PATH_FILE_H_ // 内容... #endif // PROJECT_PATH_FILE_H_16. 调试工具与技巧16.1 GDB常用命令g -g main.cpp -o main gdb ./main (gdb) break 42 # 在第42行设断点 (gdb) run # 运行程序 (gdb) print variable # 打印变量值 (gdb) backtrace # 查看调用栈 (gdb) next # 单步执行16.2 内存检测工具使用Valgrind检测内存泄漏valgrind --leak-checkfull ./program17. 算法可视化技巧17.1 控制台可视化矩阵void printMatrix(const vectorvectorint mat) { for (const auto row : mat) { for (int num : row) { cout setw(3) num; } cout endl; } cout string(mat[0].size() * 3, -) endl; }17.2 栈操作动画模拟使用ASCII艺术展示栈状态Push(5): | 5 | min5 ------- Push(3): | 3 | min3 | 5 | ------- Push(7): | 7 | | 3 | min3 | 5 | -------18. 持续学习与社区参与18.1 开源贡献建议力扣题解在GitHub上分享优质解答算法模板维护个人算法笔记库竞赛复盘撰写Codeforces比赛分析18.2 技术社区推荐中文社区力扣讨论区知乎算法话题CSDN优质博主国际社区LeetCode DiscussStack OverflowCodeforces博客19. 职业发展与面试策略19.1 算法能力评估标准企业常见评估维度正确性解决既定问题的能力效率时间/空间复杂度优化代码质量可读性、健壮性沟通表达解释思路的清晰度应变能力处理反馈和调整19.2 面试时间分配建议45分钟技术面试的理想节奏5分钟理解题目和澄清需求10分钟讨论解法和复杂度20分钟编写和测试代码5分钟边界案例讨论5分钟QA反问环节20. 个人心得与建议在实际刷题和面试过程中我发现以下几个习惯特别有价值每日精炼哪怕只做一道题也要彻底吃透多维解法对每道题尝试至少两种解法错题复盘建立分类错题本定期复习白板练习模拟面试环境手写代码性能分析比较不同解法的实际运行数据对于准备面试的同学我的建议是优先掌握20种核心算法模式如滑动窗口、DFS/BFS、DP等然后针对目标公司的题库做专项突破。记住刷题质量远比数量重要把一道经典题研究透彻胜过草率完成十道题。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑