学习记录:js算法(六):有效的数独、 最长连续序列

有效的数独

请你判断一个 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
};

讲解

  1. 使用 new Set() 创建row,col,box的集合
  2. 循环 board 参数,再循环board[i]i 是行的下标, k 是列的下标,每行每列的那个值就是 board[i][j] ,再计算 box 的下标,因为 box3*3 的,所以行和列都要除3,加起来就是 box 的下标,所以 boxIndex = Math.floor(i / 3) * 3 + Math.floor(j / 3)
  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() ,而是通过标记 truefalse 来判断。简单来说,就是将 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。
网上思路

  1. 双重循环
  2. 使用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;
};

讲解

  1. 使用 Set() 对数组去重
  2. 使用 !numSet.has(num - 1) 来判断当前这个 num 是否是序列的开始数字
  3. 如果是序列的开始数字,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;
};

讲解
刚开始没看懂,想了好久才明白是啥意思。。。

  1. 首先得明确 连续长度 的概念,打个比方:
    数组 [6,7,8] ,数组中没有小于6的连续数,所以 6前面长度是0(前一个数字的连续长度)数组中存在6后面的7和8两个连续数(后面数字的连续长度,数组是[6,7,9]的话,因为7和9不是连续的,所以后面数字的连续长度是1),那么 6后面长度就是2,所以当前数组中的连续长度就是 前面长度+自身长度(永远是1)+后面长度,也就是0+1+2=3
  2. 上面的概念清楚了,之前的代码也就好理解了。
    **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) :更新后一个数字的起始点。

总结

还是循环好使。

  • 29
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值