【数据结构入门】数组和链表的OJ题(2)

目录

1.回文链表

分析:

代码:

2.相交链表

分析:

代码:

3.环形链表

分析:

代码:

面试提问:

4.环形链表II

分析1:

分析2:

代码:

5.随机链表的复制

分析:

代码:

6.对链表进行插入排序

分析:

代码:

7.删除排序链表中的重复元素 II

分析:

代码:


1.回文链表

        限制时间复杂度是O(n),空间复杂度为O(1);

给定一个链表的 头节点 head ,请判断其是否为回文链表。

如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。

示例 1:

输入: head = [1,2,3,3,2,1]
输出: true

示例 2:

输入: head = [1,2]
输出: false

提示:

  • 链表 L 的长度范围为 [1, 105]
  • 0 <= node.val <= 9

分析:

        先找到中间位置,从中间位置到末尾的链表进行逆置,将逆置完毕的链表和开始位置到中间位置的链表进行比对,若相同那么就是回文链表。

1.找到链表的中间位置

        这里使用快慢指针,对于快指针一次走两步,慢指针一次走一步;

        如果链表元素个数是偶数那么快指针走到NULL停止,如果链表元素个数是奇数,快指针走到链表最后一个元素停止,停止的条件是:fast->next == NULL || fast == NULL,那么继续的条件就是取反:fast->next != NULL && fast != NULL;

        如此以来循环结束之后,slow指针指向的就是链表的中间位置。

2.逆置链表

                使用三个指针,分别指向NULL,head,head->next,这里需要在程序之前判断链表元素的个数为0或者1直接返回头结点。       

        n2指向n1,n1更新为n2,n2更新为n3,n3更新为n3->next,当n3为空的时候,终止循环,此时链表的最后两个节点没有完成逆置,所以需要手动进行逆置。 

逆置完毕之后,n2就是逆置之后的链表的头结点。

3.衔接的问题

当链表是偶数的时候,逆置中间的元素,那么此时需要将中间元素的前一个元素的指针进行断开,奇数也是如此。

        想要断开与中间节点的连接,这里还需要知道中间节点的前一个节点,在求中间节点的时候可以设置变量记录下来,然后使其指向NULL,这样一来就断开与中间节点的连接了;

        最后就是比较回文了,我们发现回文链表有可能是奇数也有可能是偶数,如果是偶数就不用担心比较的方法,如果是奇数,则这里需要按照前半段的链表(较短)来比较。

代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/typedef struct ListNode ListNode;
ListNode* reverseList(ListNode* head) {// 如果0、1个节点,返回自身if (head == NULL || head->next == NULL) {return head;}// 定义三个指针ListNode* p1 = NULL;ListNode* p2 = head;ListNode* p3 = head->next;while (p3) {p2->next = p1;p1 = p2;p2 = p3;p3 = p3->next;}// n3为空的时候,最后一次反转还没开始p2->next = p1;return p2;
}
bool isPalindrome(struct ListNode* head) {ListNode* fast = head;ListNode* slow = head;ListNode* prev = head; // 中间节点的前一个节点// 链表个数为偶数,fast会在NULL,链表个数为奇数,fast会在最后一个元素while (fast != NULL && fast->next != NULL) {prev = slow; // 记录中间节点的前一个节点fast = fast->next->next;slow = slow->next;}prev->next = NULL;                    // 断开后半部分ListNode* scHead = reverseList(slow); // 翻转之后的头结点// 进行比较,如果是奇数那么两边的链表长度不一致,所以循环按照前半部分链表while (head) {if (head->val == scHead->val) {head = head->next;scHead = scHead->next;}else{return false;break;}}return true;}

2.相交链表

给定两个单链表的头节点 headA 和 headB ,请找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [0,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
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。

提示:

  • listA 中节点数目为 m
  • listB 中节点数目为 n
  • 0 <= m, n <= 3 * 104
  • 1 <= Node.val <= 105
  • 0 <= skipA <= m
  • 0 <= skipB <= n
  • 如果 listA 和 listB 没有交点,intersectVal 为 0
  • 如果 listA 和 listB 有交点,intersectVal == listA[skipA + 1] == listB[skipB + 1]

进阶:能否设计一个时间复杂度 O(n) 、仅用 O(1) 内存的解决方案?

分析:

        首先需要计算出每一个链表的长度,再计算长度的差值,需要让长一点的指针走差值那么多步,使得两个指针处于同一起跑线上,此时同时再进行遍历,直到两个指针相同(不是值相同),需要在循环中判断如果任意一方(因为同步走)的指针是NULL,说明没有交点,返回空,否则就继续循环,退出循环的条件是两个指针不同,如果没有提前return,说明循环正常结束,那么就直接返回任意一个指针即可。

代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {ListNode* p1 = headA;ListNode* p2 = headB;// 先计算出各自链表的长度,链表长的可以先走几步,保证指针在同一个位置int la = 0;int lb = 0;int between = 0;while(p1){p1 = p1->next;la++;}while(p2){p2 = p2->next;lb++;}// 保持同步if(la > lb){between = la - lb;while(between--){headA = headA->next;}}else{between = lb - la;while(between--){headB = headB->next;}}// 同时走while(headA != headB){headA = headA->next;headB = headB->next;if(headA ==NULL){return NULL;}}return headA;
}

3.环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 104]
  • -105 <= Node.val <= 105
  • pos 为 -1 或者链表中的一个 有效索引 。

进阶:你能用 O(1)(即,常量)内存解决此问题吗?

分析:

        带环链表最恶心的地方是在于不能遍历,这里可以用快慢指针来实现。

快指针先进环,慢指针后进环,如果有环,那么快指针一定能追上慢指针,一旦追上就判定有环。

代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) {ListNode* fast = head;ListNode* slow = head;while(fast != NULL && fast->next != NULL){fast = fast->next->next;slow = slow->next;if(fast == slow){return true;}}return false;
}

面试提问:

请正面快指针如何追上慢指针?为什么快指针的步长是2步,3步行不行,n步行不行?

        假设两个指针进环以后他们之间的距离是x,那么slow每走一步,fast就走两步,那么它们之间的距离就会是x、x-1、x-2.......0

        那么如果fast指针一次走3步,那么他们之间的距离是x-2、x-4、x-6,此时x如果是奇数那么快慢指针就会永不相遇。

4.环形链表II

给定一个链表,返回链表开始入环的第一个节点。 从链表的头节点开始沿着 next 指针进入环的第一个节点为环的入口节点。如果链表无环,则返回 null

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

说明:不允许修改给定的链表。

    示例 1:

    输入:head = [3,2,0,-4], pos = 1
    输出:返回索引为 1 的链表节点
    解释:链表中有一个环,其尾部连接到第二个节点。
    

    示例 2:

    输入:head = [1,2], pos = 0
    输出:返回索引为 0 的链表节点
    解释:链表中有一个环,其尾部连接到第一个节点。
    

    示例 3:

    输入:head = [1], pos = -1
    输出:返回 null
    解释:链表中没有环。
    

    提示:

    • 链表中节点的数目范围在范围 [0, 104] 内
    • -105 <= Node.val <= 105
    • pos 的值为 -1 或者链表中的一个有效索引

    进阶:是否可以使用 O(1) 空间解决此题?

    分析1:

            快指针走两步,慢指针走一步,总会在环中相遇,在相遇的节点中断开,作为头结点;

    此时非环的链表和断开的链表分别作为头结点,查找单链表的交点,那么这个交点就是入环的第一个节点。

    分析2:

    快慢指针进圈之后:假如圈前的长度是L,圈的周长是C,圈开始的地方距离快慢指针相遇点的距离是X。

    慢指针走的距离:L+X

    快指针走的距离:L + C + X

    由于快指针走的距离是慢指针的两倍就有:

    L + C + X = 2 * (L + X)

    稍微化简一下:

    C = L+X

    即:

    L = C-X

    那么我们可以这样做:一个指针从起点出发,一个指针从快慢指针的相遇处出发,两个指针会走相同的步数L 或者C -X,循环结束后两个指针会相遇,最后返回任意一个节点即可。

            有没有例外的情况,这种情况是快指针刚好只走了1圈,那么当圈为2的时候,慢指针走一步,快指针走一圈,这样的情况,上面的推论就不成立,我们可以再进行推算。

    当慢指针入圈之前,快指针可能走了N圈。

    慢指针走的距离:L+X

    快指针走的距离:L + N*C + X

    由于快指针走的距离是慢指针的两倍就有:

    L + N*C + X = 2 * (L + X)

    稍微化简一下:

    N*C = L+X

    即:

    L = N*C-X

    也就是说上面的情况,只是一种特殊的情况,即N=1。

    那么我们可以从快慢相遇点开始、从链表的头开始,当链表头指针走了L的时候,相遇点指针可能走了N圈,具体走了几圈,我们不关心,我们只需要知道这两个指针走的路程是一样的,当这两个指针。

    代码:

    /*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
    typedef struct ListNode ListNode;
    struct ListNode* detectCycle(struct ListNode* head) {ListNode* fast = head;ListNode* slow = head;// 1.由于是环,所以快指针慢指针必定重合while(fast != NULL && fast->next != NULL){fast = fast->next->next;slow = slow->next;if(fast == slow)// 后判断,不然循环开始就跳出了{break;}}// 循环退出有两种可能// 如果是指针相等退出,这是我们想要的,如果不是,那么说明fast本身为空,或者fast->next为空if(fast == NULL || fast->next == NULL){   // 根本不是环return NULL;}// 2.head和交接点同时出发while(head != fast){head = head->next;fast = fast->next;}//3.此时fast走了若干圈,head走了L的距离相遇,这个节点就是入圈点return head;
    }

    5.随机链表的复制

    给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

    构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 

    例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

    返回复制链表的头节点。

    用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

    • val:一个表示 Node.val 的整数。
    • random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为  null 。

    你的代码  接受原链表的头节点 head 作为传入参数。

    示例 1:

    输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
    输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
    

    示例 2:

    输入:head = [[1,1],[2,1]]
    输出:[[1,1],[2,1]]
    

    示例 3:

    输入:head = [[3,null],[3,0],[3,null]]
    输出:[[3,null],[3,0],[3,null]]
    

    提示:

    • 0 <= n <= 1000
    • -104 <= Node.val <= 104
    • Node.random 为 null 或指向链表中的节点。

    分析:

            每一个结点除了next指针之外还有一个随机指针,这个随机指针有可能指向任意节点(自己、空、其他),这道题的难点是复制随机指针,由于新链表的空间是全新空间,我们从老链表得到了第一个节点的随机指针,但是怎么和新链表的节点进行对应呢?

    1.拷贝节点,链接到原节点的后面

    2.处理拷贝节点的随机指针

            通过上图我们可以发现,源节点的随机指针指向的元素的next就是新复制的随机指针!

    3. 将原链表的next指针进行恢复

    首先将原链表的next指向复制链表的next,然后再进行判断:如果拷贝链表next有值,那么就将next赋值为next->next,如果next为空,直接next赋值为空;拷贝链表和原链表指针后移。

    4.返回拷贝链表即可

    代码:

    /*** Definition for a Node.* struct Node {*     int val;*     struct Node *next;*     struct Node *random;* };*/
    typedef struct Node Node;Node* createNode(Node* sourceNode) {Node* newNode = (Node*)malloc(sizeof(Node));newNode->next = NULL;newNode->random = NULL;newNode->val = sourceNode->val;return newNode;
    }
    struct Node* copyRandomList(struct Node* head) {// 如果没有节点直接返回空if(!head){return NULL;}Node* curr = head;// 1.复制节点并且串联在原链表中while (curr) {Node* newNode = createNode(curr);Node* tmp = curr->next;curr->next = newNode;newNode->next = tmp;curr = tmp;}// 2.给随机链表赋值,curr的随机链表的next就是复制节点的随机链表curr = head;while (curr !=NULL && curr->next != NULL ) {if(curr->random != NULL){curr->next->random = curr->random->next;}else{curr->next->random = NULL;}curr = curr->next->next;}// 3.断开连接Node* copyHead = head->next;Node* copyCurr = copyHead;curr = head;while (curr) {curr->next = copyCurr->next;if(copyCurr->next !=NULL){copyCurr->next = copyCurr->next->next;}else{copyCurr->next = NULL;}// 移动指针curr = curr->next;copyCurr = copyCurr->next;}return copyHead;
    }

    6.对链表进行插入排序

    给定单个链表的头 head ,使用 插入排序 对链表进行排序,并返回 排序后链表的头 。

    插入排序 算法的步骤:

    1. 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
    2. 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
    3. 重复直到所有输入数据插入完为止。

    下面是插入排序算法的一个图形示例。部分排序的列表(黑色)最初只包含列表中的第一个元素。每次迭代时,从输入数据中删除一个元素(红色),并就地插入已排序的列表中。

    对链表进行插入排序。

    示例 1:

    输入: head = [4,2,1,3]
    输出: [1,2,3,4]

    示例 2:

    输入: head = [-1,5,3,4,0]
    输出: [-1,0,3,4,5]

    提示:

    • 列表中的节点数在 [1, 5000]范围内
    • -5000 <= Node.val <= 5000

    分析:

            插入排序的原理就是,先把第一个数当做是有序的,后面的数字跟第一个数字进行比较,如果比第一个数字大那么就放在第一个数字后面,如果比第一个数字小那么就放在第一个数字前面,此时链表中有两个数,这两个数是有序的,那么第三个数只需要从头开始遍历即可,插入到合适的位置。

    以下是代码的梳理:

    ①判断链表中若有1个或0个节点,那么不用排序,直接返回本身;

    ②将链表划分为两个链表,一个是待排序的链表,一个是已经有序的链表,需要从待排序的链表中取节点放到未排序的链表中;

    ③将未排序的链表的头结点初始化为已排序的链表的头结点,那么未排序的链表的头结点更新为之前头结点的下一个节点,然后把已排序的链表的头结点的next置为NULL;

    ④遍历未排序的链表,当当前节点的值比已经排序的链表的头结点的值还要小,说明当前值需要作为已经排序链表的头结点(头插);

    ⑤另一种情况(中间插入):遍历已排序的链表,如果有节点比当前节点的值还要大(或者相等),说明此时应该插入已排序的当前节点之前,所以需要定义一个prev来保存已排序链表的节点的前一个节点,prev从已排序节点的头结点开始即可,因为头插的情况已经处理了,最近的一个节点是头结点的下一个节点。

    ⑥如果没有找到比未排序的当前节点大的节点,已排序的两个指针prev和curr就一直往后走,当curr为空退出循环,若找到了就在curr之前进行插入,使用break主动退出循环。

    ⑦此时退出循环的可能有两种,要么自己主动插入后退出循环,要么没有找到比未排序的当前节点更大的节点,导致sortedCurr为空,我们只需要对后者进行处理,后者说明需要将curr插入到已排序节点的最后面,即尾插。

    ⑧插入完毕之后需要在while大循环的最后将curr指针向后移动,这里就需要在循环开始之前将curr的next节点进行保存,以便移动。

    ⑨直接返回已排序链表的头结点即可。

    代码:

    /*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/typedef struct ListNode ListNode;
    struct ListNode* insertionSortList(struct ListNode* head) {// 如果只有一个节点或空链表,直接返回本身if(head == NULL || head->next == NULL){return head;}// 分成两个区域,排序区和未排序区// 取出未排序区的第一个节点作为排序区的第一个节点ListNode* sortedHead = head; head = head->next;sortedHead->next = NULL;ListNode* curr = head; // 未排序区的头指针// 遍历未排序区的每一个节点进行插入while(curr){// 保存curr的下一个节点方便迭代ListNode* next = curr->next;// 当前元素比排序区域头结点还要小,头插if(curr->val < sortedHead->val){curr->next = sortedHead;sortedHead = curr;}else{// 中间插入,遍历已排序的链表// 找到一个节点比当前节点要大,当前节点就插在这个节点的前面/*因为头插的情况已经判断过了,所以最早的插入是头节点的后面,所以prev直接是头而curr是头的下一个节点*/ListNode* sortedPrev = sortedHead;ListNode* sortedCurr = sortedHead->next;while(sortedCurr){// 插在prev和sortedcurr之间if(sortedCurr->val >= curr->val){sortedPrev->next = curr;curr->next = sortedCurr;break;}else{// 如果没有找到,那就指针后移sortedPrev = sortedCurr;sortedCurr = sortedCurr->next;}}// 出循环有两种可能,要么是没插入,要么是插入了提前退出循环// 当sortedCurr为空的时候,说明此时没有插入,只能尾插if(sortedCurr == NULL){sortedPrev->next = curr;curr->next = NULL;}}// 当前curr插入完毕之后,迭代curr = next;}return sortedHead;
    }

    7.删除排序链表中的重复元素 II

    给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。

    示例 1:

    输入:head = [1,2,3,3,4,4,5]
    输出:[1,2,5]
    

    示例 2:

    输入:head = [1,1,1,2,3]
    输出:[2,3]
    

    提示:

    • 链表中节点数目在范围 [0, 300] 内
    • -100 <= Node.val <= 100
    • 题目数据保证链表已经按升序 排列

    分析:

    ①一般情况:定义三个指针,判断curr和next是否相同,如果相同那么就移动next,如果不同那就free掉curr到next之间的所有结点,然后将prev指向next,最后再三个指针一起动;

    ②特殊情况:当链表的头全部相等,那么按照上面的逻辑就是当next和curr不相同的时候,prev的next指向next,如此以来就会空指针的解引用。

    所以我们不能直接将prev指向next,如果不为空才能指向,若为空那么需要重新更换头结点。

     // 防止开始的几个元素是相同的,此时prev是空,就会报错if(prev != NULL){// prev需要指向nextprev->next;}else{// 如果prev是空,又要删掉前面相同的内容,直接换头head = next;}

    ③特殊情况:当链表的尾全部相等,此时如果单纯的比较cur和next,全部相同,那么next就会一直走到NULL,最后依然会对NULL进行解引用。

    这里需要增加一个条件:

    是因为,当末尾元素相同的时候,next和curr都是NULL,此时再进行while判断就会对空指针解引用。

    当next指针后移的时候,也需要进行判断,若指针为NULL,那么就会对空指针解引用,加上条件,在最外层的循环会直接退出。

    代码:

            

     typedef struct ListNode ListNode;
    struct ListNode* deleteDuplicates(struct ListNode* head) {if(head == NULL || head->next == NULL){return head; // 0或1个节点不需要删除,直接返回}ListNode* prev = NULL;ListNode* curr = head;ListNode* next = head->next;while(next){// 如果不相等,三个指针同时后移if(curr->val != next->val){prev = curr;curr = next;next = next->next;}else{// 如果curr和next相等,next要移动到不相等为止,若尾元素相同,那么next会走到NULL,判断的时候会出现空指针解引用// 这里加一个条件next不能为空while(next != NULL && curr->val == next->val){next = next->next;}// 循环结束后,curr和next的值不相等或者next走到null了// 防止开始的几个元素是相同的,此时prev是空,就会报错if(prev != NULL){// prev需要指向nextprev->next = next;}else{// 如果prev是空,又要删掉前面相同的内容,直接换头head = next;}// curr需要移动到next,并且在移动的过程中释放内存while(curr != next){ListNode* trash = curr;curr = curr->next;free(trash);}if(next != NULL){next = next->next; }}}return head;
    }

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

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    文件包含篇

    web78 第一题filter伪协议直接读源码即可 ?filephp://filter/convert.base64-encode/resourceflag.php web79 flag.php的php无法用大小写绕过&#xff0c;所以用Php://input只读流 import requests url "http://fadb524a-f22d-4747-a35c-82f71e84bba7.challenge.ctf.sho…

    互作蛋白组学技术对比:邻近标记与传统IP-MS、Pull down-MS优势对比

    在生命科学领域&#xff0c;蛋白质间的相互作用构成了生命活动的核心网络&#xff0c;驱动着信号传导、基因调控、代谢途径等关键过程。为了绘制这幅复杂的“分子互作地图”&#xff0c;科学家们开发了多种技术&#xff0c;其中免疫共沉淀结合质谱&#xff08;IP-MS&#xff09…

    (ZipList入门笔记一)ZipList的节点介绍

    ZipList是 Redis 中一种非常紧凑、节省内存的数据结构 Ziplist&#xff08;压缩列表&#xff09; 的内部内存布局。它被用于存储元素较少的 List、Hash 和 Zset。 下面我们来详细介绍每一个节点的含义&#xff1a; 1. zlbytes (ziplist bytes) 含义&#xff1a; 整个压缩列…

    Unix 发展史概览

    这里是一个简明清晰的 Unix 发展史概览&#xff0c;涵盖从起源到现代的重要节点和演变过程。Unix 发展史概览 1. Unix 起源&#xff08;1969年&#xff09; 贝尔实验室&#xff1a;Ken Thompson 和 Dennis Ritchie 开发出 Unix 操作系统。最初设计目标&#xff1a;简洁、可移植…

    基于coze studio开源框架二次定制开发教程

    目录 一、 项目介绍 1.1 什么是Coze Studio 1.2 功能清单 1.3对比商业版本 二、 功能定开说明 2.1 技术栈简介 2.2 项目架构

    RHCE认证题解

    考前说明请勿更改 IP 地址。DNS 解析完整主机名&#xff0c;同时也解析短名称。• 所有系统的 root 密码都是 redhat• Ansible 控制节点上已创建用户账户 devops。可以使用 ssh 访问• 所需的所有镜像保存在镜像仓库 utility.lab.example.compodman 可使用下述账号登录使用 用…

    调用com对象的坑

    1、谏言 最近我在弄64位调用32位dll的问题&#xff0c;在几种IPC之间&#xff0c;最后考虑了调用COM 毕竟我们只在windows平台 2、第一坑–修改编译后都需要重新注册&#xff0c;注册表 一直以为只需要编译就好了&#xff0c;结果调用没反应、报错什么的&#xff0c;需要先撤销…

    【Python】PyQt 实现 TreeWidget 多级联动选择逻辑,打造素材搜索自定义树形控件!

    在开发自己的写作素材管理工具时,我遇到了一个非常典型但又略显棘手的 UI 问题: 💡 如何实现一个“可自由勾选分类标签”的树形结构界面,支持父子节点自动联动勾选,提升用户体验? 虽然 PyQt 的 QTreeWidget 是构建多层分类结构的好帮手,但默认却不具备父子节点的自动级…

    27-数据仓库与Apache Hive-2

    1.数仓开发语言概述 理论上来说&#xff0c;任何一款编程语言只要具备读写数据、处理数据的能力&#xff0c;都可以用于数仓的开发。比如大家耳熟能详的C、java、Python等&#xff1b; 关键在于编程语言是否易学、好用、功能是否强大。遗憾的是上面所列出的C、Python等编程语言…

    软件测试——接口自动化

    测试中的自动化分为两类&#xff1a; 1.ui自动化&#xff08;web、移动端&#xff09;2.接口自动化 前面的博客中&#xff0c;我们已经讲解了web端的ui自动化&#xff0c;感兴趣的同学可以去看看&#xff1a;软件测试——自动化测试常见函数_自动化测试代码编写-CSDN博客 今…

    Flask一个用户同时只能在一处登录实现

    场景&#xff1a;web页面如果多人用同一账号同时登录操作&#xff0c;可能会导致数据等的混乱甚至出现故障。并且可能损害开发者的利益。为此&#xff0c;本篇文章就讲下如何实现同一账户同时仅能一个地方登录操作。 思路&#xff1a;1. 用户登陆时生成token&#xff08;uuid.u…

    联发科芯片组曝高危漏洞:越界写入缺陷危及智能手机与物联网设备安全

    漏洞概况全球领先的芯片组制造商联发科&#xff08;MediaTek&#xff09;近日发布最新产品安全公告&#xff0c;披露了影响其智能手机、物联网设备及其他嵌入式系统芯片的多项安全漏洞。高危漏洞分析CVE-2025-20696 作为公告披露的首个且最严重的漏洞&#xff0c;该高危缺陷源于…

    Android与Flutter混合开发:页面跳转与通信完整指南

    Android与Flutter混合开发&#xff1a;页面跳转与通信完整指南 一、Android跳转Flutter页面的实现方式 1. 基础跳转方法 &#xff08;1&#xff09;使用全新引擎跳转&#xff08;每次新建&#xff09; startActivity(FlutterActivity.withNewEngine().initialRoute("/home…

    Web存储技术详解:sessionStorage、localStorage与Cookie

    一、核心特性对比特性CookielocalStoragesessionStorage存储大小4KB左右5-10MB5-10MB生命周期可设置过期时间永久存储&#xff08;除非手动清除&#xff09;会话期间有效&#xff08;标签页关闭即清除&#xff09;作用域同源的所有窗口同源的所有窗口仅当前标签页自动发送每次H…

    3. 为什么 0.1 + 0.2 != 0.3

    总结 底层是二进制实现概述 在 JavaScript 中&#xff0c;0.1 0.2 的结果并不是精确的 0.3&#xff0c;而是 0.30000000000000004。这个现象并不是 JavaScript 的“bug”&#xff0c;而是由于浮点数在计算机底层的二进制表示方式导致的精度丢失问题。一、计算机如何表示小数&a…

    股票数据接口哪家好?专业评测各主流接口的优势与不足

    Python股票接口实现查询账户&#xff0c;提交订单&#xff0c;自动交易&#xff08;1&#xff09; Python股票程序交易接口查账&#xff0c;提交订单&#xff0c;自动交易&#xff08;2&#xff09; 股票量化&#xff0c;Python炒股&#xff0c;CSDN交流社区 >>> 股票…

    如何用分布式架构视角理解宇宙稳定性?从精细调参到微服务的类比思考

    在调试一段多线程分布式代码时&#xff0c;我忽然意识到一个不合理的事实&#xff1a;为什么现实世界这么稳定&#xff1f;为什么没有“宇宙蓝屏”或“感知崩溃”&#xff1f;为什么每天醒来&#xff0c;我们还能看到同样的物理规律、感知同一个自我&#xff1f;站在程序员的角…

    游戏画面总是卡顿怎么办 告别延迟畅玩游戏

    玩游戏最让人头疼的问题之一就是画面卡顿&#xff0c;影响操作流畅度与游戏体验。卡顿可能由硬件性能、系统设置、网络延迟等多种因素导致。本文将从不同角度出发&#xff0c;为你提供五个有效解决方法&#xff0c;帮助你快速提升游戏流畅度。 一、降低游戏画质设置 高画质虽然…

    VUE+SPRINGBOOT从0-1打造前后端-前后台系统-邮箱重置密码

    在现代Web应用中&#xff0c;密码重置功能是用户账户安全体系中不可或缺的一部分。本文将详细介绍如何使用Vue.js前端框架和SpringBoot后端框架实现一个基于邮箱验证的密码重置功能。功能概述本密码重置功能包含以下核心流程&#xff1a;用户输入注册邮箱系统发送验证码到该邮箱…

    华为云云产品的发展趋势:技术创新驱动数字化未来

    近年来&#xff0c;随着5G、人工智能&#xff08;AI&#xff09;、大数据、物联网&#xff08;IoT&#xff09;和边缘计算等新兴技术的快速发展&#xff0c;全球云计算产业正迎来新一轮变革。作为中国领先的云服务提供商&#xff0c;华为云依托华为集团在ICT&#xff08;信息与…