小哆啦解题记:数独有效性判断的奇妙之旅
小哆啦开始刷力扣的第二十七天
一、神秘任务降临
在那充满奇幻色彩的数字魔法世界里,阳光暖暖地洒在魔法广场上,小哆啦正欢快地和数字精灵们玩耍。突然,一道闪耀着神秘光芒的卷轴从天而降,稳稳地落在小哆啦面前。小哆啦好奇地凑上前,轻轻展开卷轴,只见上面写着一个艰巨的任务:判断一个 9 x 9 的数独是否有效。
小哆啦的眼睛瞬间亮了起来,它拍拍胸脯,自信满满地说:“这有什么难的,看我大展身手!” 一旁的小智微笑着提醒道:“可别小看这个任务哦,里面藏着不少学问呢。”
二、暴力解法的初次尝试
(一)灵感闪现
小哆啦找了个安静的角落,盘腿坐下,眼睛紧紧盯着数独题目,大脑飞速运转。不一会儿,它的脸上露出了得意的笑容,想到了一个最直接的办法 —— 暴力检查。它自言自语道:“我只要一行一行、一列一列、一个 3x3 宫格一个 3x3 宫格地去检查数字是否重复,肯定能搞定!”
(二)魔法代码书写
小哆啦兴奋地挥舞着魔法棒,魔法棒顶端闪烁着五彩光芒,在魔法屏幕上敲下了如下 TypeScript 代码:
function isValidSudoku(board: string[][]): boolean {
// 检查每一行
for (let i = 0; i < 9; i++) {
const rowSet = new Set<string>();
for (let j = 0; j < 9; j++) {
const num = board[i][j];
if (num!== '.') {
if (rowSet.has(num)) {
return false;
}
rowSet.add(num);
}
}
}
// 检查每一列
for (let j = 0; j < 9; j++) {
const colSet = new Set<string>();
for (let i = 0; i < 9; i++) {
const num = board[i][j];
if (num!== '.') {
if (colSet.has(num)) {
return false;
}
colSet.add(num);
}
}
}
// 检查每一个 3x3 宫格
for (let boxRow = 0; boxRow < 9; boxRow += 3) {
for (let boxCol = 0; boxCol < 9; boxCol += 3) {
const boxSet = new Set<string>();
for (let i = boxRow; i < boxRow + 3; i++) {
for (let j = boxCol; j < boxCol + 3; j++) {
const num = board[i][j];
if (num!== '.') {
if (boxSet.has(num)) {
return false;
}
boxSet.add(num);
}
}
}
}
}
return true;
}
(三)遭遇瓶颈
小哆啦满心期待地运行代码,代码运行时,魔法屏幕上数字如流星般闪烁。可当小哆啦看到运行结果虽然正确,但速度却很慢时,它的眉头皱成了一个 “川” 字。它焦急地跺着脚,嘟囔着:“怎么这么慢呀,这要是遇到更大的数独,不得等到花儿都谢了!”
三、小智的智慧引导
(一)耐心剖析
就在小哆啦急得像热锅上的蚂蚁时,小智慢悠悠地走了过来。他轻轻拍了拍小哆啦的肩膀,说:“小哆啦,别着急。你看你这个方法虽然能解决问题,但是要对整个数独进行三次遍历,就好像你要跑三趟才能完成一件事,自然就慢啦。我们能不能想办法在一次遍历中就完成所有的检查呢?”
小哆啦听了,眼睛里满是疑惑,它挠挠头,问道:“一次遍历?这怎么可能呢?”
(二)巧妙点拨
小智笑着拿出一根魔法粉笔,在地上画起了数独的结构。他指着图说:“我们可以同时记录每一行、每一列和每一个 3x3 宫格中数字的出现情况。用三个数组分别存储行、列和宫格的信息,每个数组元素是一个 Map
,用来记录对应位置上数字的出现次数。这样在一次遍历过程中就能完成所有的检查。”
小哆啦仔细地听着,眼睛里渐渐有了光亮,它兴奋地说:“我好像明白了,就像我们可以同时记住很多事情一样,让程序也同时记住行、列和宫格的数字情况!”
四、算法优化的成功逆袭
(一)优化代码诞生
小哆啦迫不及待地拿起魔法棒,根据小智的提示,修改起了代码:
function isValidSudokuOptimized(board: string[][]): boolean {
const rows: Map<string, number>[] = Array.from({ length: 9 }, () => new Map());
const cols: Map<string, number>[] = Array.from({ length: 9 }, () => new Map());
const boxes: Map<string, number>[] = Array.from({ length: 9 }, () => new Map());
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
const num = board[i][j];
if (num!== '.') {
const boxIndex = Math.floor(i / 3) * 3 + Math.floor(j / 3);
rows[i].set(num, (rows[i].get(num) || 0) + 1);
cols[j].set(num, (cols[j].get(num) || 0) + 1);
boxes[boxIndex].set(num, (boxes[boxIndex].get(num) || 0) + 1);
if (
rows[i].get(num)! > 1 ||
cols[j].get(num)! > 1 ||
boxes[boxIndex].get(num)! > 1
) {
return false;
}
}
}
}
return true;
}
(二)惊喜效果呈现
小哆啦紧张地按下运行按钮,只见魔法屏幕上数字闪烁的速度明显加快,不一会儿就得出了结果。小哆啦兴奋得跳了起来,它抱住小智,大喊道:“成功啦!速度变快好多,太神奇了!”
小智笑着解释道:“现在我们只需要对整个数独进行一次遍历,时间复杂度从 降低到了 ,效率提升了不少呢。在处理大规模数据时,这种优化就会更加明显。”
五、收获与成长
小哆啦看着优化后的代码,心中感慨万千。它明白了,在解决问题时,不能只满足于能得到答案,还要思考如何让方法更加高效。这次的经历就像一把神奇的钥匙,为小哆啦打开了算法优化的大门。
从此,小哆啦在算法探索的道路上更加自信满满,它期待着迎接更多的挑战,用智慧和魔法书写更多精彩的解题故事。