leetcode-hot100-7链表
7.链表文章目录7.链表160.相交链表方法一:c(暴力解法)方法一:js方法二:c(哈希)方法二:js方法三:c(双指针)方法三:js206.反转链表方法一:c++方法二:js234.回文链表方法一:c方法一:js方法二:c方法二:js方法三:c方法三:js141.环形链表方法一:c方法一:js方法二:c方法二:js142.环形链表 II方法一:c方法一:js方法二:c方法二:js21.合并两个有序链表方法一:c方法一:js2.两数相加方法一:c++方法二:c++方法三:c++方法四:c++19.删除链表的倒数第 N 个结点方法一:c方法一:js方法二:c方法二:js24.两两交换链表中的节点方法一:c方法一:js25.K 个一组翻转链表方法一:c++方法二:js138.随机链表的复制方法一:c方法一:js148.排序链表方法一:c方法一:js方法二:c方法二:js23.合并 K 个升序链表方法一:c方法一:js146.LRU 缓存方法一:js方法二:c方法二:js160.相交链表给你两个单链表的头节点headA和headB,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回null图示两个链表在节点c1开始相交**:**题目数据保证整个链式结构中不存在环。注意,函数返回结果后,链表必须保持其原始结构。自定义评测:评测系统的输入如下(你设计的程序不适用此输入):intersectVal- 相交的起始节点的值。如果不存在相交节点,这一值为0listA- 第一个链表listB- 第二个链表skipA- 在listA中(从头节点开始)跳到交叉节点的节点数skipB- 在listB中(从头节点开始)跳到交叉节点的节点数评测系统将根据这些输入创建链式数据结构,并将两个头节点headA和headB传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被视作正确答案。示例 1:输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3 输出:Intersected at '8' 解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。 从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。 在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。 — 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点,B 中第四个节点) 在内存中指向相同的位置。示例 2:输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1 输出:Intersected at '2' 解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。 从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。 在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。示例 3:输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2 输出:No intersection 解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。 由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。 这两个链表不相交,因此返回 null方法一:c(暴力解法)简单的一个想法,对链表 A 中的每一个结点,我们都遍历一次链表 B 查找是否存在重复结点,第一个查找到的即第一个公共结点ListNode*getIntersectionNode(ListNode*headA,ListNode*headB){for(ListNode*p=headA;p!=nullptr;p=p-next){for(ListNode*q=headB;q!=nullptr;q=q-next){if(p==q)returnp;}}returnnullptr;}方法一:js// 定义链表节点类,模拟 C++ 的 ListNode 结构体classListNode{constructor(val){this.val=val;this.next=null;}}// 转换后的核心函数:查找两个链表的相交节点functiongetIntersectionNode(headA,headB){// 外层循环遍历链表 A 的每个节点for(letp=headA;p!==null;p=p.next){// 内层循环遍历链表 B 的每个节点for(letq=headB;q!==null;q=q.next){// 判断节点是否是同一个引用(相交的核心:地址/引用相同)if(p===q){returnp;}}}// 没有相交节点时返回 nullreturnnull;}方法二:c(哈希)判断两个链表是否相交,可以使用哈希集合存储链表节点。首先遍历链表 headA,并将链表 headA 中的每个节点加入哈希集合中。然后遍历链表 headB,对于遍历到的每个节点,判断该节点是否在哈希集合中:如果当前节点不在哈希集合中,则继续遍历下一个节点;如果当前节点在哈希集合中,则后面的节点都在哈希集合中,即从当前节点开始的所有节点都在两个链表的相交部分,因此在链表 headB 中遍历到的第一个在哈希集合中的节点就是两个链表相交的节点,返回该节点。如果链表 headB 中的所有节点都不在哈希集合中,则两个链表不相交,返回 null。structHashTable{structListNode*key;// 哈希表存储的键:链表节点的指针(地址)UT_hash_handle hh;// UT_hash 库必需的句柄,用于管理哈希表(无需手动操作)};// 核心函数:查找两个链表的相交节点,参数:headA-链表A头节点,headB-链表B头节点,返回值:相交节点指针(无则返回NULL)structListNode*getIntersectionNode(structListNode*headA,structListNode*headB){structHashTable*hashTable=NULL;// 初始化哈希表指针为NULL(UT_hash 要求哈希表初始化为空)structListNode*temp=headA;// 定义临时指针temp,初始指向链表A的头节点,用于遍历链表Awhile(temp!=NULL){// 遍历链表A的所有节点,将每个节点的指针存入哈希表structHashTable*tmp;// 定义临时哈希表节点指针tmp,用于接收查找结果// HASH_FIND:在哈希表hashTable中查找键为temp的节点,参数:hh(句柄)、哈希表指针、查找键地址、键大小、接收结果指针HASH_FIND(hh,hashTable,temp,sizeof(structHashTable*),tmp);if(tmp==NULL){// 如果查找结果为NULL,说明该节点还未存入哈希表tmp=malloc(sizeof(structHashTable));// 为新的哈希表节点分配内存(大小为HashTable结构体)tmp-key=temp;// 将当前链表节点的指针赋值给哈希表节点的key字段// HASH_ADD:将新节点添加到哈希表,参数:hh(句柄)、哈希表指针、键字段名、键大小、要添加的节点HASH_ADD(hh,hashTable,key,sizeof(structHashTable*),tmp);}temp=temp-next;// temp指针后移,遍历链表A的下一个节点}temp=headB;// 重置temp指针,指向链表B的头节点,用于遍历链表Bwhile(temp!=NULL){// 遍历链表B的所有节点,检查当前节点是否存在于哈希表中structHashTable*tmp;// 定义临时哈希表节点指针tmp,用于接收查找结果HASH_FIND(hh,hashTable,temp,sizeof(structHashTable*),tmp);// 在哈希表中查找当前链表B节点的指针if(tmp!=NULL){// 如果查找结果不为NULL,说明该节点在链表A中存在(即相交节点)returntemp;// 返回该相交节点的指针}temp=temp-next;// temp指针后移,遍历链表B的下一个节点}returnNULL;// 遍历完链表B后未找到相交节点,返回NULL}方法二:jsvargetIntersectionNode=function(headA,headB){// 定义函数,参数为两个链表的头节点,返回相交节点(无则返回null)constvisited=newSet();// 创建空Set集合,用于存储链表A的节点引用(地址)lettemp=headA;// 声明临时指针temp,初始指向链表A的头节点while(temp!==null){// 循环遍历链表A,直到temp指向null(链表末尾)visited.add(temp);// 将当前链表A的节点添加到Set集合中temp=temp.next;// temp指针后移,指向链表A的下一个节点}temp=headB;// 重置temp指针,指向链表B的头节点while(temp!==null){// 循环遍历链表B,直到temp指向null(链表末尾)if(visited.has(temp)){// 检查当前链表B的节点是否存在于Set集合中(即是否在链表A中出现过)returntemp;// 找到相交节点,直接返回该节点}temp=temp.next;// temp指针后移,指向链表B的下一个节点}returnnull;// 遍历完链表B未找到相交节点,返回null};方法三:c(双指针)思路分析:一上来没看懂这题要做什么,看了题解才知道这题是要找到两个链表相交的首个公共节点,一个巧妙的解法就是用双指针,一个指针遍历完之后,指向另一条链表的头节点,这样相当于做了一个长度对齐。边界判断:先处理任一链表为空的情况,直接返回空;双指针核心:指针走到末尾就切换到另一个链表的头,保证总路程相等;循环退出条件:指针相遇(要么是相交节点,要么都是空),直接返回该指针即可。ListNode*getIntersectionNode(ListNode*headA,ListNode*headB){if(headA==nullptr||headB==nullptr){// 边界条件:如果任一链表为空,直接返回空(无相交节点)returnnullptr;// 空链表不可能和其他链表相交,返回nullptr}ListNode*pA=headA,*pB=headB;// 初始化两个指针,pA指向链表A头节点,pB指向链表B头节点while(pA!=pB){// 循环条件:两个指针指向的节点不同时继续(相遇时退出)pA=pA==nullptr?headB:pA-next;// pA走到末尾(nullptr)则切换到链表B头节点,否则往后走一步pB=pB==nullptr?headA:pB-next;// pB走到末尾(nullptr)则切换到链表A头节点,否则往后走一步}returnpA;// 退出循环时pA==pB,要么是相交节点,要么都是nullptr(无相交)}方法三:jsvargetIntersectionNode=function(headA,headB){if(headA===null||headB===null){// 边界条件:如果任一链表为空,直接返回null(无相交节点)returnnull;// 空链表不可能和其他链表相交,返回null}letpA=headA,pB=headB;// 初始化两个指针,pA指向链表A头节点,pB指向链表B头节点while(pA!==pB){// 循环条件:两个指针指向的节点不同时继续(相遇时退出)pA=pA===null?headB:pA.next;// pA走到末尾(null)则切换到链表B头节点,否则往后走一步pB=pB===null?headA:pB.next;// pB走到末尾(null)则切换到链表A头节点,否则往后走一步}returnpA;// 退出循环时pA===pB,要么是相交节点,要么都是null(无相交)};206.反转链表给你单链表的头节点head,请你反转链表,并返回反转后的链表。示例 1:输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]示例 2:输入:head = [1,2] 输出:[2,1]示例 3:输入:head = [] 输出:[]方法一:c++#include iostream using namespace std; // 定义链表节点结构 struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; // 反转链表的函数 ListNode* reverseList(ListNode* head) { // cur 指向当前节点,初始化为链表头节点 // pre 指向当前节点的前一个节点,初始化为 nullptr ListNode *cur = head, *pre = nullptr; // 当 cur 不为空时,说明还没有遍历到链表的末尾 while (cur != nullptr) { // 保存 cur 的下一个节点,防止在修改 cur-next 后丢失后续链表的连接 ListNode* tmp = cur-next; // 将 cur 的 next 指针指向 pre,实现当前节点的指针反转 cur-next = pre; // 将 pre 移动到当前节点 cur 的位置,以便下一次迭代时 pre 指向当前节点的前一个节点 pre = cur; // 将 cur 移动到原来保存的下一个节点 tmp 的位置,继续处理下一个节点 cur = tmp; } // 当循环结束时,cur 为 nullptr,pre 指向的就是反转后的链表头节点,返回 pre return pre; } // 辅助函数,用于打印链表 void printList(ListNode* head) { while (head != nullptr) { cout head-val " "; head = head-next; } cout endl; } int main() { // 创建示例链表 [1,2,3,4,5] ListNode* head = new ListNode(1); ListNode* node2 = new ListNode(2); ListNode* node3 = new ListNode(3); ListNode* node4 = new ListNode(4); ListNode* node5 = new ListNode(5); head-next = node2; node2-next = node3; node3-next = node4; node4-next = node5; cout "输入链表: "; printList(head); // 调用反转链表函数 ListNode* reversedHead = reverseList(head); cout "输出链表: "; printList(reversedHead); // 释放链表节点内存 ListNode* current = reversedHead; while (current != nullptr) { ListNode* next = current-next; delete current; current = next; } return 0; }方法二:js// 定义链表节点类,每个节点包含一个值 val 和一个指向下一个节点的指针 nextclassListNode{constructor(val=0,next=null){this.val=val;this.next=next;}}// 反转链表的函数functionreverseList(head){// 初始化 prev 为 null,用于记录当前节点的前一个节点letprev=null;// 初始化 cur 为头节点,从链表头部开始处理letcur=head;// 只要 cur 不为 null,就继续处理链表节点while(cur!==null){// 保存当前节点的下一个节点,避免修改指针后丢失后续节点constnext=cur.next;// 将当前节点的 next 指针指向前一个节点,实现反转cur.next=prev;// 更新 prev 为当前节点,为下一次反转做准备prev=cur;// 更新 cur 为之前保存的下一个节点,继续处理后续节点cur=next;}// 当 cur 为 null 时,说明链表处理完毕,prev 即为反转后链表的头节点returnprev;}// 辅助函数,将数组转换为链表functionarrayToList(arr){if(arr.length===0)returnnull;// 创建头节点lethead=newListNode(arr[0]);letcurrent=head;// 遍历数组,依次创建节点并连接成链表for(leti=1;iarr.length;i++){current.next=newListNode(arr[i]);current=current.next;}returnhead;}// 辅助函数,将链表转换为数组,方便打印结果functionlistToArray(head){constresult=[];letcurrent=head;// 遍历链表,将节点值存入数组while(current){result.push(current.val);current=current.next;}returnresult;}constinputArray=[1,2,3,4,5];// 测试代码constinputList=arrayToList(inputArray);// 将输入数组转换为链表constreversedList=reverseList(inputList);// 调用反转链表函数constoutputArray=listToArray(reversedList);// 将反转后的链表转换为数组console.log("输入:",inputArray);console.log("输出:",outputArray);234.回文链表给你一个单链表的头节点head,请你判断该链表是否为回文链表。如果是,返回true;否则,返回false。示例 1:输入:head = [1,2,2,1] 输出:true示例 2:输入:head = [1,2] 输出:false方法一:c思路分析:回文链表是指向前和向后读都相同的链表,刚做完上题的反转链表,一个朴素的思想就是把链表先翻转一下,然后两个链表遍历对比,于是写了以下代码:class Solution{public:boolisPalindrome(ListNode*head){ListNode*curr=head;ListNode*prev=nullptr;ListNode*next;while(curr){next=curr-next;curr-next=prev;prev=curr;curr=next;}while(head){if(prev-val!=head-val)returnfalse;prev=prev-next;head=head-next;}returntrue;}};上面这个代码无法通过,因为犯了一个错误,*curr = head 并没有复制链表,而是使 curr 和 head 指向了同一个链表(地址相同)。因此,要复制链表,需要单独 new 一个 ListNode,再遍历复制。ListNode*cloneList(ListNode*head){if(!head)returnnullptr;ListNode*newHead=newListNode(head-val);ListNode*tail=newHead;head=head-next;while(head){tail-next=newListNode(head-val);tail=tail-next;head=head-next;}returnnewHead;}class Solution{public:boolisPalindrome(ListNode*head){ListNode*copy=cloneList(head);// 原地反转 copyListNode*curr=copy;ListNode*prev=nullptr;ListNode*next;while(curr){next=curr-next;curr-next=prev;prev=curr;curr=next;}// 和 head 比较while(head){if(prev-val!=head-val)returnfalse;prev=prev-next;head=head-next;}returntrue;}};方法一:js// 定义链表节点类(JS 无原生链表,需手动实现)classListNode{constructor(val){this.val=val;this.next=null;}}// 克隆链表的函数(对应 C++ 的 cloneList 函数)functioncloneList(head){if(!head)returnnull;// 边界条件:空链表直接返回nullconstnewHead=newListNode(head.val);// 创建新链表头节点,值和原链表头一致lettail=newHead;// 尾指针,用于构建新链表head=head.next;// 原链表指针后移,开始遍历剩余节点while(head){// 遍历原链表所有节点tail.next=newListNode(head.val);// 新建节点并挂载到新链表尾部tail=tail.next;// 尾指针后移head=head.next;// 原链表指针后移}returnnewHead;// 返回克隆后的新链表头节点}// 核心逻辑:判断链表是否为回文链表varisPalindrome=function(head){constcopy=cloneList(head);// 克隆原链表,避免修改原链表// 原地反转克隆后的链表(对应 C++ 的反转逻辑)letcurr=copy;// 当前遍历节点,初始指向克隆链表头letprev=null;// 前驱节点,初始为nullletnext;// 临时存储下一个节点while(curr){next=curr.next;// 先保存当前节点的下一个节点(防止断链)curr.next=prev;// 反转当前节点的指针,指向前驱节点prev=curr;// 前驱节点后移到当前节点curr=next;// 当前节点后移到之前保存的下一个节点}// 比较原链表和反转后的克隆链表是否完全一致while(head){if(prev.val!==head.val){// 只要有一个节点值不同,就不是回文returnfalse;}prev=prev.next;// 反转链表指针后移head=head.next;// 原链表指针后移}returntrue;// 所有节点值都匹配,是回文链表};方法二:c这样做拷贝显然过于复杂,一个更好的思路是将链表转换成数组,因为数组更方便查询。转换成数组后,就可以通过双指针的思路,往中间靠近查询。class Solution{public:boolisPalindrome(ListNode*head){vectorintvals;ListNode*curr=head;while(curr){vals.push_back(curr-val);// 拷贝值curr=curr-next;}intleft=0,right=vals.size()-1;while(leftright){if(vals[left++]!=vals[right--])returnfalse;}returntrue;}};方法二:js// 定义链表节点类(JS 无原生链表结构,需手动实现)classListNode{constructor(val){this.val=val;this.next=null;}}// 核心函数:判断链表是否为回文链表varisPalindrome=function(head){constvals=[];// 对应 C++ 的 vectorint vals,用于存储链表节点的值letcurr=head;// 遍历链表的指针,初始指向链表头节点// 遍历链表,将所有节点的值存入数组while(curr){// 等价于 C++ 的 while

相关新闻

超级SIM卡 vs. eSIM卡:谁才是未来通信的主角?

超级SIM卡 vs. eSIM卡:谁才是未来通信的主角?

当你的一体式ETC无法识别时,你是否会怀念古早的插卡式ETC?——可断开的物理连接总是更让人心安。(图片来源:网络,侵删)概念起源:超级SIM卡是紫光国微创新推出,融合通信与大容量存储功…

2026/7/4 5:29:32 阅读更多 →
从 PDE 到策略:PINN+RL 如何让仿真、控制与优化同时变快?

从 PDE 到策略:PINN+RL 如何让仿真、控制与优化同时变快?

过去几年,一个很清晰的信号在顶会顶刊里不断出现:PINN(Physics-Informed Neural Networks)不再只是“解方程的神经网络”,强化学习也不再只是“靠试错找策略”的黑盒。当两者结合,研究者开始把“物理规律、…

2026/7/2 22:32:06 阅读更多 →
常量中define和const不同

常量中define和const不同

预处理器宏与变量的区别 #define 是一个预处理器指令,用于在编译前将某些文本替换为指定的值。它没有类型信息,是简单的文本替换。#define 声明的常量不会占用内存,它只是在预处理阶段做替换。 const 是C语言中的一个类型修饰符,用…

2026/7/6 5:00:46 阅读更多 →

最新新闻

智能告警分析引擎:用AI区分真实告警与噪声信号

智能告警分析引擎:用AI区分真实告警与噪声信号

智能告警分析引擎:用AI区分真实告警与噪声信号 一、从"告警洪泛"到"精准诊断"的噪声治理 大型微服务系统的告警洪泛是运维效率的核心障碍:一个节点网络抖动可能触发上游依赖链的级联告警,产生数十条重复或关联告警&#…

2026/7/7 13:04:23 阅读更多 →
5分钟掌握SPT-AKI Profile Editor:高效管理你的离线塔科夫存档

5分钟掌握SPT-AKI Profile Editor:高效管理你的离线塔科夫存档

5分钟掌握SPT-AKI Profile Editor:高效管理你的离线塔科夫存档 【免费下载链接】SPT-AKI-Profile-Editor Программа для редактирования профиля игрока на сервере SPT-AKI 项目地址: https://gitcode.com/gh_m…

2026/7/7 13:04:23 阅读更多 →
新乡旧车间改造翻新|河南老旧食品厂房拆除改造施工要点

新乡旧车间改造翻新|河南老旧食品厂房拆除改造施工要点

新乡旧车间改造翻新|河南老旧食品厂房拆除改造施工要点 近几年食品行业验收标准持续升级,很多投产多年的无尘车间、洁净厂房陆续出现墙板老化、密封失效、板缝发霉、洁净度不达标的问题。大量新乡本地食品企业,面临年审整改、产能升级、车间换…

2026/7/7 13:04:23 阅读更多 →
LV3296与STM32F722ZE嵌入式信号处理系统设计

LV3296与STM32F722ZE嵌入式信号处理系统设计

1. LV3296与STM32F722ZE的硬件协同架构解析在嵌入式信息处理系统中,LV3296作为一款高性能信号调理芯片,与STM32F722ZE微控制器的组合堪称黄金搭档。这套组合之所以能实现高效的信息捕获与跟踪,关键在于两者的硬件特性形成了完美互补。LV3296的…

2026/7/7 12:56:01 阅读更多 →
FFmpeg 6.1 视频帧提取实战:5种场景命令详解与性能对比

FFmpeg 6.1 视频帧提取实战:5种场景命令详解与性能对比

FFmpeg 6.1 视频帧提取实战:5种场景命令详解与性能对比 视频帧提取是计算机视觉、媒体处理等领域的基础操作,而FFmpeg作为开源多媒体处理工具的标杆,其功能强大但参数复杂的特性常常让使用者感到困惑。本文将深入解析FFmpeg 6.1在五种典型视频…

2026/7/7 12:56:01 阅读更多 →
传统产品经理转型AI产品,你需要掌握的5大核心能力与学习路径

传统产品经理转型AI产品,你需要掌握的5大核心能力与学习路径

本文针对传统产品经理转型AI产品经理的常见误区,指出“学会用ChatGPT”并不等同于懂AI产品。文章强调AI产品经理需要重建认知框架,并提出AI产品经理最重要的能力是接受不确定性。核心内容围绕AI技术基础、AI产品设计、数据驱动能力、AI商业思维以及跨团队…

2026/7/7 12:56:01 阅读更多 →

日新闻

鸿蒙新特性:图片画廊与轮播导航——构建沉浸式图片浏览体验

鸿蒙新特性:图片画廊与轮播导航——构建沉浸式图片浏览体验

图片浏览是移动应用中最高频的场景之一。从社交应用的照片流到电商平台的商品图集,从旅游应用的景点相册到摄影作品展示——用户对图片浏览的体验要求不断提高:流畅的切换动画、直观的缩略图导航、便捷的收藏操作、自动播放模式。HarmonyOS NEXT ArkUI 虽…

2026/7/7 0:05:16 阅读更多 →
24V DC-DC降压芯片PW2312B/PW2815,SOT23-6到SOP8-EP方案对比

24V DC-DC降压芯片PW2312B/PW2815,SOT23-6到SOP8-EP方案对比

24V稳压芯片完整选型指南 PW8600 PW75XX PW2815 PW2312B LDODC/DC全方案 一、24V稳压方案概述 24V直流电源在工业自动化、门禁系统、电梯控制、汽车电子、LED驱动、监控设备等场景中应用极广,是最常见的中压直流母线电压。要将24V母线稳定降压至下游MCU、传感器…

2026/7/7 0:05:16 阅读更多 →
RAG+知识图谱混合检索与Graph RAG核心对比

RAG+知识图谱混合检索与Graph RAG核心对比

做企业RAG落地的团队,往往容易卡在一容易踩坑的选型难题: 当需求单纯靠向量RAG搞不定、单纯靠知识图谱也搞不定,必须同时依赖「文本语义理解 实体关系推理」时,到底是做「向量图谱混合检索」就够了,还是必须上「Grap…

2026/7/7 0:07:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/6 8:11:50 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/7 12:34:47 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/6 6:52:56 阅读更多 →

月新闻