有效的数独
请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
注意:
一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
空白格用 ‘.’ 表示。
示例 1:
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6256ce4b08b9401196061c0f683c921b.jpeg#pic_center)
输入:board =
[["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
输出:true
示例 2:
输入:board =
[["8","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
输出:false
解释:除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。 但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
我的思路
第一眼看到题目,头晕,字太多了!仔细看看,好像也没啥,就是要我将这个数独,计算行、列、9*9 的盒子中不出现相同的数就行了,那还想啥,双重循环 + Set()
网上思路
略有差别,使用双重循环+数组标记
我的思路:
var isValidSudoku = function (board) {
const row = Array.from({ length: 9 }, () => new Set())
const col = Array.from({ length: 9 }, () => new Set())
const box = Array.from({ length: 9 }, () => new Set())
for (let i = 0; i < board.length; i++) {
for (let j = 0; j < board[i].length; j++) {
let num = board[i][j]
let boxIndex = Math.floor(i / 3) * 3 + Math.floor(j / 3)
if (num === ".") continue;
if (row[i].has(num) || col[j].has(num) || box[boxIndex].has(num)) {
return false
}
row[i].add(num)
col[j].add(num)
box[boxIndex].add(num)
}
}
return true
};
讲解
- 使用 new Set() 创建row,col,box的集合
- 循环 board 参数,再循环board[i] , i 是行的下标, k 是列的下标,每行每列的那个值就是 board[i][j] ,再计算 box 的下标,因为 box 是 3*3 的,所以行和列都要除3,加起来就是 box 的下标,所以 boxIndex = Math.floor(i / 3) * 3 + Math.floor(j / 3)
- 有了下标就好操作了,根据下标找出对应值,然后将这个值,通过 Set.has() 方法比较,如果存在就返回false,不存在就将这个值填充到对应的 Set 集合中。
网上思路
var isValidSudoku = function (board) {
const rows = Array(9).fill(0).map(() => Array(9).fill(false));
const cols = Array(9).fill(0).map(() => Array(9).fill(false));
const boxes = Array(9).fill(0).map(() => Array(9).fill(false));
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
const num = board[i][j];
if (num === '.') continue;
const index = num - 1;
const boxIndex = Math.floor(i / 3) * 3 + Math.floor(j / 3);
if (rows[i][index] || cols[j][index] || boxes[boxIndex][index]) {
return false;
}
rows[i][index] = true;
cols[j][index] = true;
boxes[boxIndex][index] = true;
}
}
return true;
};
讲解
代码差不多,只不过不是用 Set() ,而是通过标记 true 和 false 来判断。简单来说,就是将 row、col、box 创建成 二维数组,第二层数组中的元素全是 false ,然后同样是双重循环,只不过这里是找下标了,因为 num 的值永远是 1~9 ,所以 index = num-1 ,然后将出现的元素标记为 true 即可。
最长连续序列
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
示例 1:
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
示例 2:
输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9
我的想法
做了这么多天的算法了,感觉这个比较简答,先排序,然后循环,找出 item+1=nums[index+1],然后根据这个下标,找出它后面 元素元素中 item+1 != nums[index+1] 下标,然后相减,又因为是下标,所以长度要+1。
网上思路
- 双重循环
- 使用Map()
我的思路
var longestConsecutive = function (nums) {
if (nums.length === 0) return 0;
const sortNums = [...new Set(nums)].sort((a, b) => a - b);
let a = 1;
let b = 1;
for (let i = 1; i < sortNums.length; i++) {
if (sortNums[i] === sortNums[i - 1] + 1) {
a++;
} else {
b = Math.max(b, a);
a = 1;
}
}
b = Math.max(b, a);
return b;
};
讲解
刚开始上面的想法有问题,因为只考虑了第一次出现 item+1=== nums[index+1] 的情况,没考虑到后面如果也出现了 item+1 === nums[index+1] 的话,没去比较他们的长度。
仔细想了想,可以这么理解:先去重排序,然后用 变量a 和 变量b,变量a 去接收当前的长度,然后在循环中,判断出 item+1 !== nums[index+1] 时,将变量a 重新赋值为0,变量b 用来接收 变量a 和 变量b 的最大值。最后return 变量b 即可。
网上思路
- 双重循环
var longestConsecutive = function (nums) {
if (nums.length === 0) return 0;
const numSet = new Set(nums);
let maxLength = 0;
for (const num of numSet) {
if (!numSet.has(num - 1)) {
let currentNum = num;
let currentLength = 1;
for (const item of numSet) {
if (numSet.has(currentNum + 1)) {
currentNum += 1;
currentLength += 1;
} else {
maxLength = Math.max(maxLength, currentLength);
}
}
}
}
return maxLength;
};
讲解
- 使用 Set() 对数组去重
- 使用 !numSet.has(num - 1) 来判断当前这个 num 是否是序列的开始数字
- 如果是序列的开始数字,currentNum = num ,再判断 numSet.has(currentNum + 1) ,也就是判断 Set()集合 中是否存在 currentNum + 1 ,如果存在,currentNum+=1,currentLength += 1 ,如果不存在,比较 maxLength 和 currentLength
- Map()方法
var longestConsecutive = function (nums) {
let map = new Map();
let maxCount = 0;
for (let i = 0; i < nums.length; i++) {
// 避免重复计算相同的元素
if (!map.get(nums[i])) {
preLen = map.get(nums[i] - 1) || 0;
lastLen = map.get(nums[i] + 1) || 0;
curLen = preLen + 1 + lastLen;
maxCount = Math.max(maxCount, curLen);
map.set(nums[i], curLen);
map.set(nums[i] - preLen, curLen);
map.set(nums[i] + lastLen, curLen);
}
}
return maxCount;
};
讲解
刚开始没看懂,想了好久才明白是啥意思。。。
- 首先得明确 连续长度 的概念,打个比方:
数组 [6,7,8] ,数组中没有小于6的连续数,所以 6前面长度是0(前一个数字的连续长度) ,数组中存在6后面的7和8两个连续数(后面数字的连续长度,数组是[6,7,9]的话,因为7和9不是连续的,所以后面数字的连续长度是1),那么 6后面长度就是2,所以当前数组中的连续长度就是 前面长度+自身长度(永远是1)+后面长度,也就是0+1+2=3- 上面的概念清楚了,之前的代码也就好理解了。
**preLen、lastLen、curLen ** :前一个数字的连续长度、后一个数字的连续长度、当前数字的连续长度
maxCount = Math.max(maxCount, curLen) :更新最大长度,
map.set(nums[i], curLen) :将当前数字及其长度存起来,
map.set(nums[i] - preLen, curLen) :更新前一个数字的起始点,
map.set(nums[i] + lastLen, curLen) :更新后一个数字的起始点。
总结
还是循环好使。