力扣每日一题37:解数独

目录

题目

大致思路

方法一:回溯剪枝(正常人能想出来的)

方法二:位运算优化剪枝

需要使用的位运算技巧

代码

位运算怎么就优化了呢?

方法三:枚举优化。

官解代码

方法四:舞蹈链算法


题目

困难

编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

数独部分空格内已填入了数字,空白格用 '.' 表示。

示例 1:

输入: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"]]
输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:


提示:

  • board.length == 9
  • board[i].length == 9
  • board[i][j] 是一位数字或者 '.'
  • 题目数据 保证 输入数独仅有一个解

面试中遇到过这道题?

1/5

通过次数

248.8K

提交次数

367.3K

通过率

67.7%

大致思路

使用一个数据记录每个每一行,每一列,每一个3*3宫格中数组1-9出现的情况。

对所有空格(也就是需要填数的地方)进行遍历填数,判断这个位置能填1-9中的哪个数字,如果数字num在空格所在的行、列、3*3宫格都没出现过,那就可以把num放进去,然后填写下一个空格。当填写完最后一个空格的时候,就代表解完了。

方法一:回溯剪枝(正常人能想出来的)

对于每一行、每一列、每个3*3宫格中1-9出现的情况,我们都用一个哈希表表示。

这样在对某个空格进行填数num时,我们只需要判断这个空格所在行、所在列、所在3*3宫格有没有出现过num即可。

class Solution {
public:
    //判断九行、九列、九个格中数字1-9有无出现的哈希表
    int rows[9][9];
    int columns[9][9];
    int boxes[3][3][9];

    vector<pair<int,int>> blanks;//需要填写的空格

    bool flag;//用来剪枝
    void solveSudoku(vector<vector<char>>& board) {
        //初始化哈希表
        memset(rows,0,sizeof(rows));
        memset(columns,0,sizeof(columns));
        memset(boxes,0,sizeof(boxes));
        flag=false;
        
        //遍历一次9*9宫,寻找空格并更新哈希表
        int i,j;
        for(i=0;i<9;i++)
        {
            for(j=0;j<9;j++)
            {
                char c=board[i][j];
                if(c=='.'){
                    blanks.emplace_back(i,j);//blanks.emplace_back({i,j});会有语法错误
                }else{
                    int digit=c-'0';
                    rows[i][digit-1]=columns[j][digit-1]=boxes[i/3][j/3][digit-1]=1;
                }
            }
        }
        backtrack(board,0);
    }
    void backtrack(vector<vector<char>>& board,int index){
        //填完了最后一个空格,
        if(index==blanks.size()){
            flag=true;
            return;
        }
        //1-9遍历
        int x=blanks[index].first;
        int y=blanks[index].second;
        for(int num=1;num<=9&&flag==false;num++){
            if(rows[x][num-1]==0&&columns[y][num-1]==0&&boxes[x/3][y/3][num-1]==0){
                rows[x][num-1]=columns[y][num-1]=boxes[x/3][y/3][num-1]=1;
                board[x][y]=num+'0';
                backtrack(board,index+1);
                rows[x][num-1]=columns[y][num-1]=boxes[x/3][y/3][num-1]=0;
            }
        }
    }
};

方法二:位运算优化剪枝

看了题解我才知道这也能优化…………

在方法一中,我们使用了长度为 9 的数组表示每个数字是否出现过(1为出现过,0为没有出现)。我们同样也可以借助位运算,仅使用一个整数表示每个数字是否出现过。

具体的做法是:一个二进制数的第i位数(最低为是第0位)是1,就表示 i+1 已经出现过。例如:

二进制数 (011010100) 代表数字 3、5、7、8已经出现过。

也就是说我们用一个二进制数来表示一个哈希表。

在方法一中,每在一个空格出现一个数字num,或者是填写一个数字num,都要将这个空格对于行、列、3*3宫格的哈希表的num值由0变成1,也就是 简单的[num-1]=1,填写完num发现数字不对后,又要将哈希表num的值由1变成0。也就是说,在用数组当作哈希表时,哈希表的更新就是将数组对应的值简单的0,1互换。

在此方法中,哈希表的更新就变成了二进制数对应某一位上的0,1互换。要使用一些位运算的技巧,

需要使用的位运算技巧

(1)、将二进制数的第i位由i变成0,或由0变成1.

1<<i 的第i+1位二进制数是1,其余的二进制数是0。x和1<<i按位异或后,x=x^(1<<i)后,x的第i+1位就实现了0,1的互换。

(2)、遍历一个二进制数上的所有0。

方法一中,空格所在的行、列、3*3宫格的哈希表[num-1]==0就代表这个空格可以填数字num,而在二进制的哈希表中,我们要找二进制数对应的0。二进制数的0不太好取,我们就把0,1互换,互换后的1就是先前的0,而二进制的1是好取的(随后会讲)。

一个二进制数按位取反得到的1就是先前的0,但是我们只需要前9位的1,所以取反后的数还要和 (111111111)进行按位与操作,这样就能只留下前九位数。

(3)、取得二进制数最低位的1(包括后面的0)

x=~x&(111111111) 后,x中第i(从最低为第0位开始)位的1就代表数字 i+1可以填在空格里。

low_one=x&(-x)。因为-x在计算器用的是补码,所以-x和x的二进制数的最低为1和右边的0都相等,其余为都不想等,按位与刚好可以留下这一部分。留下这一部分后,low_one的位数就是可以填入空格的数字。

(4)、将二进制数的最低为1变成0。

在某个空格blanks中填入某个数后不一定对,这时候要填下一个数。我们只需要将最低位的1变成0,然后再取最低位的1,这样就取到了下一个数。

x&=(x-1);

代码

class Solution {
private:
    int rows[9];
    int cols[9];
    int boxes[3][3];
    vector<pair<int, int>> blanks;
    bool valid;
public:
    //将指定数位1变0,0变1
    void func(int i, int j, int digit) {
        rows[i] ^= (1 << digit);
        cols[j] ^= (1 << digit);
        boxes[i / 3][j / 3] ^= (1 << digit);
    }
    void backtrack(vector<vector<char>>& board, int index) {
        if (index == blanks.size()) {
            valid = true;
            return;
        }
        int x = blanks[index].first;
        int y = blanks[index].second;
        //创建掩码来遍历
        int mask = ~(rows[x] | cols[y] | boxes[x / 3][y / 3]);
        mask = mask & 0x1ff;            //和(111111111)按位取和,去除第九位之前的1
        while (mask && !valid) {
            //取最低位的1包括后面的0,然后转换成对应的数字
            int lowdigit = mask & (-mask);
            int digit = 0;
            while (lowdigit) {
                lowdigit >>= 1;
                digit++;
            }
            func(x, y, digit - 1);
            board[x][y] = '0' + digit;
            backtrack(board, index + 1);
            func(x, y, digit - 1);

            //去除掉最低位的1
            mask &= (mask - 1);
        }
    }
    void solveSudoku(vector<vector<char>>& board) {
        memset(rows, 0, sizeof(rows));
        memset(cols, 0, sizeof(cols));
        memset(boxes, 0, sizeof(boxes));
        valid = false;
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] == '.') {
                    blanks.emplace_back(i, j);
                }
                else {
                    int digit = board[i][j] - '0' - 1;
                    func(i, j, digit);
                }
            }
        }

        backtrack(board, 0);
    }
};

位运算怎么就优化了呢?

对比一下方法一和方法二的代码。区别就是哈希表的更新和在遍历空格时,对于填充数字的遍历。


先说哈希表的更新,方法一的代码是

rows[i][digit-1]=columns[j][digit-1]=boxes[i/3][j/3][digit-1]=1;
或
rows[i][digit-1]=columns[j][digit-1]=boxes[i/3][j/3][digit-1]=0;

而方法二是对下面函数的一次调用。

void func(int i, int j, int digit) {
        rows[i] ^= (1 << digit);
        cols[j] ^= (1 << digit);
        boxes[i / 3][j / 3] ^= (1 << digit);
    }

光看这一部分来说,方法二反而要更复杂。

但是考虑到对空格填充数字的遍历时,那就不一样了。

方法一是对数字1-9都有一次判断,一共有九次。

for(int num=1;num<=9&&flag==false;num++){
    if(rows[x][num-1]==0&&columns[y][num-1]==0&&boxes[x/3][y/3][num-1]==0){
        //填充数字
    }
}

而方法二是在创建掩码后,每次只取对应二进制数位上有1的数。

//创建掩码来遍历
        int mask = ~(rows[x] | cols[y] | boxes[x / 3][y / 3]);
        mask = mask & 0x1ff;            //和(111111111)按位取和,去除第九位之前的1
        while (mask && !valid) {
            //填充数字
            mask &= (mask - 1);
        }

假设某个空格所在行、列、3*3宫格都只有一个数9可以填的时候,方法一需要循环9次,而方法二在创建掩码后,只需要循环1次。也就是说,方法二自动过滤掉了空格所在行、列、3*3宫格中已经出现的数。这大大缩短了代码运行时间。

方法三:枚举优化。

看了题解还知道,位运算的方法还能优化………………

想一下,我们在手作数独游戏的时候,都是先将能确定数字的位置先填上。

就比如在刚开始给出的矩阵中,有一个空格所在行出现过1、2、3,所在列出现过4、5、6,所在九宫格出现过7,8。那么毫无疑问,这个位置就是填9,不用等其他位置填完也能确认。

这样一来,我们可以不断地对整个数独进行遍历,将可以唯一确定的空白格全部填入对应的数。随后我们再使用与方法二相同的方法对剩余无法唯一确定的空白格进行递归 + 回溯。

官解代码

class Solution {
private:
    int line[9];
    int column[9];
    int block[3][3];
    bool valid;
    vector<pair<int, int>> spaces;

public:
    void flip(int i, int j, int digit) {
        line[i] ^= (1 << digit);
        column[j] ^= (1 << digit);
        block[i / 3][j / 3] ^= (1 << digit);
    }

    void dfs(vector<vector<char>>& board, int pos) {
        if (pos == spaces.size()) {
            valid = true;
            return;
        }

        auto [i, j] = spaces[pos];
        int mask = ~(line[i] | column[j] | block[i / 3][j / 3]) & 0x1ff;
        for (; mask && !valid; mask &= (mask - 1)) {
            int digitMask = mask & (-mask);
            int digit = __builtin_ctz(digitMask);
            flip(i, j, digit);
            board[i][j] = digit + '0' + 1;
            dfs(board, pos + 1);
            flip(i, j, digit);
        }
    }

    void solveSudoku(vector<vector<char>>& board) {
        memset(line, 0, sizeof(line));
        memset(column, 0, sizeof(column));
        memset(block, 0, sizeof(block));
        valid = false;

        for (int i = 0; i < 9; ++i) {
            for (int j = 0; j < 9; ++j) {
                if (board[i][j] != '.') {
                    int digit = board[i][j] - '0' - 1;
                    flip(i, j, digit);
                }
            }
        }

        while (true) {
            int modified = false;
            for (int i = 0; i < 9; ++i) {
                for (int j = 0; j < 9; ++j) {
                    if (board[i][j] == '.') {
                        int mask = ~(line[i] | column[j] | block[i / 3][j / 3]) & 0x1ff;
                        if (!(mask & (mask - 1))) {
                            int digit = __builtin_ctz(mask);
                            flip(i, j, digit);
                            board[i][j] = digit + '0' + 1;
                            modified = true;
                        }
                    }
                }
            }
            if (!modified) {
                break;
            }
        }

        for (int i = 0; i < 9; ++i) {
            for (int j = 0; j < 9; ++j) {
                if (board[i][j] == '.') {
                    spaces.emplace_back(i, j);
                }
            }
        }

        dfs(board, 0);
    }
};

方法四:舞蹈链算法

我以为吕布已经天下无敌了,没想到有人比他还要勇猛…………

我翻看评论的时候,看到有个大佬发的一种算法,叫做舞蹈链算法,自称是解数独最快的算法,没有之一。这算法我也不会,直接把代码贴上来吧。

/*
最快的是舞蹈链算法
然后是贪心加回溯算法
最慢的是正常的回溯算法
*/
#include <bits/stdc++.h>

using namespace std;

const int N = 9 * 9 * 9 * 4;

class node {
public:
    int u{}, d{}, l{}, r{}; //上下左右
    int row{}, col{};   //具体坐标
    int s{};          //col节点数量
    int h{};          //row头节点
    int ans{};        //选了那些row

    node() = default;
};

class DLX {
public:
    vector<vector<int>> a;
    const int n = 9 * 9 * 9, m = 9 * 9 * 4;
    int num{};
    vector<node> nodes;

    explicit DLX(vector<vector<int>> &B) {
        a = B;
        dance();
        B = a;
    }

    void dance() {
        init();
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                for (int k = 1; k <= 9; k++) {
                    if (a[i][j] != k && a[i][j] != 0) {
                        continue;
                    }
                    int id = i * 9 * 9 + j * 9 + k;
                    Link(id, i * 9 + j + 1);
                    Link(id, i * 9 + k + 81);
                    Link(id, j * 9 + k + 162);
                    Link(id, (i / 3 * 3 + j / 3) * 9 + k + 243);
                }
            }
        }
        Dance(0);
    }

    void init() {
        num = m + 1;
        nodes.assign(N, node());
        for (int i = 0; i <= m; i++) {
            node &it = nodes[i];
            it.l = i - 1;
            it.r = i + 1;
            it.u = it.d = i;
        }
        nodes[0].l = m;
        nodes[m].r = 0;
    }

    void Link(int r, int c) {
        num++;
        node &row = nodes[r];
        node &col = nodes[c];
        node &nums = nodes[num];

        nums.row = r;
        nums.col = c;
        col.s++;

        // node[col.u] <-> nums <-> col
        nums.u = col.u;
        nodes[col.u].d = num;
        col.u = num;
        nums.d = c;

        if (row.h == 0) {
            row.h = nums.l = nums.r = num;
        } else {
            node &head = nodes[row.h];
            //node[head.l] <-> nums <-> head
            nums.l = head.l;
            nodes[head.l].r = num;
            head.l = num;
            nums.r = row.h;
        }
    }

    void Remove(int c) {
        node &col = nodes[c];
        // node[col.l] <-> node[col.r]
        nodes[col.l].r = col.r;
        nodes[col.r].l = col.l;

        //下右
        for (int i = col.d; i != c; i = nodes[i].d) {
            for (int j = nodes[i].r; j != i; j = nodes[j].r) {
                node &it = nodes[j];
                //node[it.u] <-> node[it.d]
                nodes[it.d].u = it.u;
                nodes[it.u].d = it.d;
                nodes[it.col].s--;
            }
        }
    }

    void Resume(int c) {
        // node[col.l] -> node[c] <- node[col.r]
        node &col = nodes[c];
        nodes[col.r].l = c;
        nodes[col.l].r = c;
        //上左
        for (int i = nodes[c].u; i != c; i = nodes[i].u) {
            for (int j = nodes[i].l; j != i; j = nodes[j].l) {
                node &it = nodes[j];
                //node[it.u] -> node[j] <- node[it.d]
                nodes[it.d].u = j;
                nodes[it.u].d = j;
                nodes[it.col].s++;
            }
        }
    }

    int Dance(int dep) { // NOLINT(*-no-recursion)
        if (nodes[0].r == 0) {
            for (int i = 0; i < dep; i++) {
                node &it = nodes[i];
                int x = (it.ans - 1) / 9 / 9;
                int y = (it.ans - 1) / 9 % 9;
                int val = (it.ans - 1) % 9 + 1;
                a[x][y] = val;
            }
            return 1;
        }
        int c = nodes[0].r;
        for (int i = nodes[0].r; i != 0; i = nodes[i].r) {
            if (nodes[i].s == 0 || nodes[c].s == 0) {
                return 0;
            }
            if (nodes[i].s < nodes[c].s) {
                c = i;
            }
        }
        Remove(c);
        for (int i = nodes[c].d; i != c; i = nodes[i].d) {
            node &it = nodes[i];
            nodes[dep].ans = it.row;
            for (int j = nodes[i].r; j != i; j = nodes[j].r) {
                Remove(nodes[j].col);
            }
            if (Dance(dep + 1) == 1) {
                return 1;
            }
            for (int j = nodes[i].l; j != i; j = nodes[j].l) {
                Resume(nodes[j].col);
            }
        }
        Resume(c);
        return 0;
    }
};

class Solution {
public:
    static void solveSudoku(vector<vector<char>> &b) {
        vector<vector<int>> a(9, vector<int>(9));
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                int val = b[i][j] == '.' ? 0 : int(b[i][j] - '0');
                a[i][j] = val;
            }
        }
        DLX o(a);
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                b[i][j] = char(a[i][j] + '0');
            }
        }
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/612337.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

机器学习-Padans

机器学习-Padans 面对人生的烦恼与挫折&#xff0c;最重要的是摆正自己的心态&#xff0c;积极面对一切。再苦再累&#xff0c;也要保持微笑。笑一笑&#xff0c;你的人生会更美好&#xff01; 目录 机器学习-Padans 1.DataFrame 2.画图 3. 扩展&#xff1a; 1.DataFrame #…

一键局域网共享工具

一键局域网共享工具&#xff1a;实现文件快速共享的新选择 在数字化时代&#xff0c;文件共享已成为我们日常工作和生活中的重要需求。无论是在家庭还是在办公环境中&#xff0c;我们经常需要在不同的设备之间传输文件。为了满足这一需求&#xff0c;一键局域网共享工具应运而…

python爬虫(三)之虎嗅网汽车文章爬虫

python爬虫&#xff08;三&#xff09;之虎嗅网汽车文章爬虫 闲来没事&#xff0c;闲鱼上有个好兄弟要我从虎嗅网上抓一些汽车文章的爬虫&#xff0c;于是大力出奇迹&#xff0c;我写了一个python程序&#xff0c;将这个网站上所有的汽车文章全部抓取下来了&#xff0c;存储到…

(三十九)第 6 章 树和二叉树(二叉树的三叉链表存储)

1. 背景说明 2. 示例代码 1) errorRecord.h // 记录错误宏定义头文件#ifndef ERROR_RECORD_H #define ERROR_RECORD_H#include <stdio.h> #include <string.h> #include <stdint.h>// 从文件路径中提取文件名 #define FILE_NAME(X) strrchr(X, \\) ? strrc…

RF Plasma gernerator-系列(RF-5KW Adtec)说明书TX06-9001-00

RF Plasma gernerator-系列(RF-5KW Adtec)说明书TX06-9001-00

C++随手写一个打字练习软件TL(TypeLetters)附原码

C随手写一个打字练习软件TL&#xff08;TypeLetters&#xff09;附原码 说明 软件名称&#xff1a;TL&#xff08;TypeLetters&#xff09; 开发语言&#xff1a;C 适合人群&#xff1a;零基础小白或C学习者 软件功能&#xff1a;打字练习软件TL&#xff08;TypeLetters&#…

【树莓派4B】如何用树莓派的串口发送数据给单片机

文章目录 查看路由器中的树莓派IProot连接打开vnc远程桌面服务打开win的远程桌面软件输入IP和端口串口发送数据硬件连接树莓派发送 查看路由器中的树莓派IP root连接 打开vnc远程桌面服务 vncserver :1打开win的远程桌面软件 输入IP和端口 192.168.3.33:1输入密码qwer1234后点…

2019年计算机真题

2019年计算机真题 离散数学 一、用逻辑符号表达下列语句(论域为包含一切事物的集合) 1&#xff09;过平面上的两个点&#xff0c;有且仅有一条直线通过。 解: (1) P ( x , y ) : x , y \mathrm{P}_{(\mathrm{x}, \mathrm{y})}: \mathrm{x}, \mathrm{y} P(x,y)​:x,y 是平面上的…

Vitis HLS 学习笔记--理解串流Stream(3)

目录 1. 简介 2. 综合报告的差别 2.1 包含 do-while 2.2 不包含 do-while 2.3 报告差异分析 3. 总结 1. 简介 针对《Vitis HLS 学习笔记--理解串流Stream(2)-CSDN博客》博文的内容&#xff0c;做进一步说明。 2. 综合报告的差别 2.1 包含 do-while Performance & …

QML及VTK配合构建类MVVM模式DEMO

1 创建QT QUICK项目 这次我们不在主程中加载VTK的几何&#xff1b; 在qml建立的控件&#xff0c;创建MyVtkObject类的单例&#xff0c;main中将指针和单例挂钩&#xff1b; 在MyVtkObject实例中操作 QQuickVTKRenderItem 类即可&#xff1b; 由于VTK的opengl显示是状态机&a…

脆皮之“指针和数组的关系”

文章目录 1. 数组名的理解2. 使用指针访问数组3. 一维数组传参的本质4. 冒泡排序5. 二级指针6. 指针数组7. 指针数组模拟二维数组 hello&#xff0c;大家好呀&#xff0c;窝是脆皮炸鸡。这一期是关于数组和指针的&#xff0c;我觉得并不是很难&#xff0c;但是我觉着下一期可能…

最新版Ceph( Reef版本)块存储简单对接k8s

当前ceph 你的ceph集群上执行 1.创建名为k8s-rbd 的存储池 ceph osd pool create k8s-rbd 64 642.初始化 rbd pool init k8s-rbd3 创建k8s访问块设备的认证用户 ceph auth get-or-create client.kubernetes mon profile rbd osd profile rbd poolk8s-rbd部署 ceph-rbd-csi c…

如何用 OceanBase做业务开发——【DBA从入门到实践】第六期

当应用一款新的数据库时&#xff0c;除了基础的安装部署步骤&#xff0c;掌握其应用开发方法才是实现数据库价值的关键。为此&#xff0c;我们特别安排了5月15日&#xff08;周三&#xff09;的《DBA 从入门到实践》第六期课程——本次课程将带大家了解OceanBase数据库的开发流…

学习Java的日子 Day45 HTML常用的标签

Day45 HTML 1.掌握常用的标签 1.1 标题标签 h1-h6 <h1>一级标签</h1> <h2>二级标签</h2> <h3>三级标签</h3> <h4>四级标签</h4> <h5>五级标签</h5> <h6>六级标签</h6> 显示特点&#xff1a; * 文字…

论文解读--------FedMut: Generalized Federated Learning via Stochastic Mutation

动机 Many previous works observed that the well-generalized solutions are located in flat areas rather than sharp areas of the loss landscapes. 通常&#xff0c;由于每个本地模型的任务是相同的&#xff0c;因此每个客户端的损失情况仍然相似。直观上&#xff0c;…

鸿蒙内核源码分析(文件句柄篇) | 你为什么叫句柄

句柄 | handle int open(const char* pathname,int flags); ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); int close(int fd);只要写过应用程序代码操作过文件不会陌生这几个函数,文件操作的几个关键步骤嘛,跟把大…

react18【实战】tab切换,纯前端列表排序(含 lodash 和 classnames 的安装和使用)

技术要点 动态样式 className{tabItem ${currentType item.value && "active"}}安装 lodash npm i --save lodash使用 lodash 对对象数组排序&#xff08;不会改变源数组&#xff09; _.orderBy(dataList, "readNum", "desc")src\De…

WireShark对tcp通信数据的抓包

一、抓包准备工作 安装wireshark sudo apt update sudo apt install wireshark 运行 二、WireShark工具面板分析 上图中所显示的信息从上到下分布在 3 个面板中&#xff0c;每个面板包含的信息含义如下&#xff1a; Packet List 面板&#xff1a;显示 Wireshark 捕获到的所…

【项目实战】使用Github pages、Hexo如何10分钟内快速生成个人博客网站

文章目录 一.准备工作1.安装git2.安装node安装 cnpm 3.使用 GitHub 创建仓库&#xff0c;并配置 GitHub Pages0.Github Pages是什么1. 在 GitHub 上创建一个新仓库2. 创建您的静态网站3. 启用 GitHub Pages4. 等待构建完成5. 访问您的网站 二. Hexo1.什么是Hexo2.安装Hexo1. 安…

【核武器】2024 年美国核武器-20240507

2024年5月7日,《原子科学家公报》发布了最新版的2024美国核武器手册 Hans M. Kristensen, Matt Korda, Eliana Johns, and Mackenzie Knight, United States nuclear weapons, 2024, Bulletin of the Atomic Scientists, 80:3, 182-208, DOI: https://doi.org/10.1080/00963…