ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

codeforces-go 题解详解:LeetCode 双周赛 102 第一题「网格列宽」findColumnWidth 的两种解法与复杂度优化

codeforces-go 题解详解:LeetCode 双周赛 102 第一题「网格列宽」findColumnWidth 的两种解法与复杂度优化 科学计算【免费下载链接】codeforces-go算法竞赛模板库 by 灵茶山艾府 项目地址https://gitcode.com/GitHub_Trending/co/codeforces-go点击查看免费下载本篇技术指南以算法竞赛模板库 codeforces-go灵茶山艾府维护中 leetcode/biweekly/102/a/README.md 题解文档为主体完整拆解 LeetCode 双周赛 102 第一题「Find the Width of Columns of a Grid网格列宽」的两种解法逐列枚举求字符串长度的朴素做法以及仅考察列最值的 O(n(mlog U)) 优化做法。文中结合仓库内 Go 实现、测试用例 与 测试数据文件给出可复制、可验证的 Python / Java / C / Go / JavaScript / Rust 多语言代码并解释“负号占一位”这一易错点背后的长度计算原理。题目背景与仓库对应关系该题是 LeetCode 双周赛第 102 场的第一题Problem 1题目编号为 2639官方题名为 “Find the Width of Columns of a Grid”。在 codeforces-go 仓库中该题解位于 leetcode/biweekly/102/a/README.md配套的 Go 实现位于 a.go测试文件为 a_test.go输入输出数据存放在 a.txt。仓库的目录组织方式是按照比赛场次biweekly/102与题号a、b、c、d分层存放a目录下固定包含README.md、a.go、a.txt、a_test.go四个文件便于读者在同一目录内完成“读题解 → 看实现 → 跑用例”的完整闭环。题目描述给定一个由整数构成的二维网格gridgrid[i][j]为int类型对于每一列需要求出该列中所有整数以十进制字符串形式表示时的最大字符长度返回一个长度为grid[0].length的整数数组。注意负号-也计入字符长度例如-15的长度为 3。方法一逐列枚举求数字转字符串后的最大长度最直观的思路逐列遍历将每一列的每个数字转换为十进制字符串取其长度的最大值。由于负号属于字符串的一部分str(-15)的长度自然是 3因此转字符串的做法天然正确。多语言实现转字符串版class Solution: def findColumnWidth(self, grid: List[List[int]]) - List[int]: return [max(len(str(x)) for x in col) for col in zip(*grid)]class Solution { public int[] findColumnWidth(int[][] grid) { int n grid[0].length; int[] ans new int[n]; for (int j 0; j n; j) { for (int[] row : grid) { ans[j] Math.max(ans[j], Integer.toString(row[j]).length()); } } return ans; } }class Solution { public: vectorint findColumnWidth(vectorvectorint grid) { int n grid[0].size(); vectorint ans(n); for (int j 0; j n; j) { for (auto row : grid) { ans[j] max(ans[j], (int) to_string(row[j]).length()); } } return ans; } };func findColumnWidth(grid [][]int) []int { ans : make([]int, len(grid[0])) for j : range grid[0] { for _, row : range grid { ans[j] max(ans[j], len(strconv.Itoa(row[j]))) } } return ans }var findColumnWidth function(grid) { const n grid[0].length; const ans Array(n).fill(0); for (let j 0; j n; j) { for (const row of grid) { ans[j] Math.max(ans[j], row[j].toString().length); } } return ans; };impl Solution { pub fn find_column_width(grid: VecVeci32) - Veci32 { (0..grid[0].len()).map(|j| { grid.iter().map(|row| row[j].to_string().len()).max().unwrap() as i32 }).collect() } }手动计算长度不依赖字符串转换若希望避免字符串转换的开销也可以手动统计位数。核心思路是x 0时包括0与负数先令长度len 10本身占一位负数的负号占一位再对x反复除以 10 累加位数。注意0的处理x ! 0的循环条件对0不会执行因此必须由初始的len 1兜底。class Solution: def findColumnWidth(self, grid: List[List[int]]) - List[int]: ans [0] * len(grid[0]) for j, col in enumerate(zip(*grid)): for x in col: x_len int(x 0) x abs(x) while x: x_len 1 x // 10 ans[j] max(ans[j], x_len) return ansclass Solution { public int[] findColumnWidth(int[][] grid) { int n grid[0].length; int[] ans new int[n]; for (int j 0; j n; j) { for (int[] row : grid) { int len row[j] 0 ? 1 : 0; for (int x row[j]; x ! 0; x / 10) { len; } ans[j] Math.max(ans[j], len); } } return ans; } }class Solution { public: vectorint findColumnWidth(vectorvectorint grid) { int n grid[0].size(); vectorint ans(n); for (int j 0; j n; j) { for (auto row : grid) { int len row[j] 0; for (int x row[j]; x; x / 10) { len; } ans[j] max(ans[j], len); } } return ans; } };func findColumnWidth(grid [][]int) []int { ans : make([]int, len(grid[0])) for j : range grid[0] { for _, row : range grid { xLen : 0 if row[j] 0 { xLen 1 } for x : row[j]; x ! 0; x / 10 { xLen } ans[j] max(ans[j], xLen) } } return ans }var findColumnWidth function(grid) { const n grid[0].length; const ans Array(n).fill(0); for (let j 0; j n; j) { for (const row of grid) { let len row[j] 0 ? 1 : 0; for (let x Math.abs(row[j]); x; x Math.floor(x / 10)) { len; } ans[j] Math.max(ans[j], len); } } return ans; };impl Solution { pub fn find_column_width(grid: VecVeci32) - Veci32 { let n grid[0].len(); let mut ans vec![0; n]; for j in 0..n { for row in grid { let mut len if row[j] 0 { 1 } else { 0 }; let mut x row[j]; while x ! 0 { len 1; x / 10; } ans[j] ans[j].max(len); } } ans } }该方法在仓库中有对应的直接实现findColumnWidth2见 a.go 第 21-36 行它正是上述“手动统计位数”的 Go 版本用于与优化版findColumnWidth进行对照验证。方法一复杂度分析时间复杂度O(mn log U)其中 m 和 n 分别为grid的行数和列数U 为grid[i][j]的绝对值的最大值。空间复杂度O(1)返回值不计入Python 中zip(*grid)产生的临时列视图空间同样忽略不计。方法二优化——只对每一列的最小值和最大值求长度方法一需要为每一个数字计算长度。但观察到数字的绝对值越大其十进制长度越长即位数单调递增。因此一列的最大宽度必然由该列的最小值或最大值决定只需取二者之一计算长度即可无需遍历整列所有元素。设某列的最小值为mn最大值为mx。由于负数中的负号也占一个长度可以直接取max(mx, -10 * mn)的长度作为答案Python 一行写法即基于此公式。或者为避免10 * mn乘法溢出可以改写为取max(mx // 10, -mn)的长度再加一作为答案此时要把0的长度视作 0。注意上述公式在一整列全为负数或全为正数时同样成立。为什么可以这样变换核心在于位数与数值规模的关系长度为 k 的十进制数的取值范围约为[10^(k-1), 10^k)。max(mx, -10*mn)的意义是把最小的负数“放大十倍”后与最大正数比较从而让负数的位数能体现出来而max(mx//10, -mn)再 1 则是利用整除降位的等价变形避免了乘法溢出。多语言实现优化版class Solution: def findColumnWidth(self, grid: List[List[int]]) - List[int]: return [len(str(max(max(col), -10 * min(col)))) for col in zip(*grid)]class Solution: def findColumnWidth(self, grid: List[List[int]]) - List[int]: ans [] for col in zip(*grid): x_len 1 x max(max(col) // 10, -min(col)) while x: x_len 1 x // 10 ans.append(x_len) return ansclass Solution { public int[] findColumnWidth(int[][] grid) { int n grid[0].length; int[] ans new int[n]; for (int j 0; j n; j) { int mn 0; int mx 0; for (int[] row : grid) { mn Math.min(mn, row[j]); mx Math.max(mx, row[j]); } int len 1; for (int x Math.max(mx / 10, -mn); x 0; x / 10) { len; } ans[j] len; } return ans; } }class Solution { public: vectorint findColumnWidth(vectorvectorint grid) { int n grid[0].size(); vectorint ans(n); for (int j 0; j n; j) { int mn 0, mx 0; for (auto row : grid) { mn min(mn, row[j]); mx max(mx, row[j]); } int len 1; for (int x max(mx / 10, -mn); x; x / 10) { len; } ans[j] len; } return ans; } };func findColumnWidth(grid [][]int) []int { ans : make([]int, len(grid[0])) for j : range grid[0] { mn, mx : 0, 0 for _, row : range grid { mn min(mn, row[j]) mx max(mx, row[j]) } xLen : 1 for x : max(mx/10, -mn); x 0; x / 10 { xLen } ans[j] xLen } return ans }var findColumnWidth function(grid) { const n grid[0].length; const ans Array(n); for (let j 0; j n; j) { let mn 0, mx 0; for (const row of grid) { mn Math.min(mn, row[j]); mx Math.max(mx, row[j]); } let len 1; for (let x Math.max(Math.floor(mx / 10), -mn); x; x Math.floor(x / 10)) { len; } ans[j] len; } return ans; };impl Solution { pub fn find_column_width(grid: VecVeci32) - Veci32 { let n grid[0].len(); let mut ans vec![0; n]; for j in 0..n { let mut mn 0; let mut mx 0; for row in grid { mn mn.min(row[j]); mx mx.max(row[j]); } let mut len 1; let mut x (mx / 10).max(-mn); while x 0 { len 1; x / 10; } ans[j] len; } ans } }仓库 Go 实现与测试验证仓库中的正式提交实现位于 a.go 第 4-19 行与 README 中的优化版 Go 代码一致先初始化mn, mx 0, 0对每一列扫描求得列最小值与最大值再以xLen : 1起步对max(mx/10, -mn)反复整除 10 累加位数。测试侧由 a_test.go 驱动它通过反射调用RunLeetCodeFuncWithFile(t, findColumnWidth, a.txt, targetCaseNum)读取 a.txt 中的用例该机制在 leetcode/testutil/leetcode.go 第 340-370 行实现将文件内容按“函数入参个数 返回个数”分组解析并使用RunFuncWithRandomInput进行随机输入对拍。当前测试数据共两组输入 grid期望输出[[1],[22],[333]][3][[-15,1,3],[15,7,12],[5,6,-2]][3,1,2]第二组用例很好地覆盖了负号占位的情况第一列-15与15的字符串长度为 3故答案为 3第二列各数为1,7,6长度均为 1第三列3,12,-2中12与-2长度为 2。方法二复杂度分析时间复杂度O(n(m log U))其中 m 和 n 分别为grid的行数和列数U 为grid[i][j]的绝对值的最大值。相比方法一的 O(mn log U)将“对所有元素求长度”降为“仅对列最值求长度”log 部分只承担一次位数统计。空间复杂度O(1)返回值不计入Python 中zip(*grid)的空间同样忽略。总结与延伸本题的关键考点有两个一是处理0与负数负号占一位时的长度计算细节二是借助“位数随绝对值单调递增”这一性质把逐元素统计优化为只考察列最小值与最大值。方法一实现直观、不易出错适合作为“保底”写法方法二在 m 很大时能显著减少字符串转换或除法统计的次数是竞赛场景下的推荐写法。该仓库还提供了完整的题解索引 leetcode/SOLUTIONS.md其中收录了作者灵茶山艾府按类别整理的力扣题解精选便于读者继续学习同类型的网格、位运算、动态规划等专题仓库根目录下的 go.mod 与 go.sum 则管理着上述测试依赖如 testify确保go test ./leetcode/biweekly/102/a/可以直接运行验证。赞分享科学计算【免费下载链接】codeforces-go算法竞赛模板库 by 灵茶山艾府 项目地址https://gitcode.com/GitHub_Trending/co/codeforces-go点击查看免费下载相关推荐codeforces-go 题解LeetCode 双周赛 102「数组所有前缀的得分」—— 前缀最大值与得分累计的单遍扫描codeforces go 题解LeetCode 双周赛 102「数组所有前缀的得分」—— 前缀最大值与得分累计的单遍扫描 导读 本文基于 leetcode/科学计算Sails 框架下通过自定义 HTTP 中间件配置 P3P 隐私策略头P3P 兼容旧版 IE 应用实战指南Sails 框架下通过自定义 HTTP 中间件配置 P3P 隐私策略头P3P 兼容旧版 IE 应用实战指南 导读 本文讲解如何在 SailsNode.js科学计算掌握Go优先队列算法竞赛中的高效性能优化指南掌握Go优先队列算法竞赛中的高效性能优化指南 在算法竞赛中时间复杂度往往是决定解题成败的关键因素。Go语言的优先队列Priority Queue作为一种科学计算上一篇Sanity Functions Agent Actions媒体库多语言 Alt Text 自动生成实战指南下一篇Feast Python SDK 包结构深度解读基于 feast.rst 的模块地图与源码级 API 指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表