链表算法题目

1.两数相加

两个非空链表,分别表示两个整数,只不过是反着存储的,即先存储低位在存储高位。要求计算这两个链表所表示数的和,然后再以相同的表示方式将结果表示出来。如示例一:两个数分别是342和465,和为807,但是我们要逆序存储,所以结果链表就是708.

解法:模拟

我们只需要用两个指针同时遍历两个链表,取出相加,相加的值刚好作为新链表的头。再遍历下一个节点的时候,只需要将结果尾插到新链表后即可。

但因为每一个节点只能存储个位数,所以我们在储存时要对相加的结果进行取模,而且两数相加还有可能遇到进位的情况,所以我们还要保存进位,因为进位是在下一位相加时才有用,所以要注意取模和求进位的先后顺序。

我们会遇到那些位数不同的数字相加,要注意循环的结束条件,只有一个为空时,我们仍需要继续遍历,空的那个只需要让其默认是0即可,这样也不会引起问题。当然,当计算到最后一位时,仍有可能进位,但是两个链表都空了,此时直接返回的话,就会出错。所以我们也要保证进位不为0

时间复杂度:O(max(n1,n2)),只需要遍历一遍链表,n1,n2分别是两个链表的长度

空间复杂度:O(1),我们申请了一个哨兵位的头节点,但是再最后释放掉了

// C++

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution 
{
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) 
    {
        ListNode* newhead = new ListNode;// 哨兵头节点
        ListNode* tail = newhead; // 尾节点

        // 遍历链表指针
        ListNode* cur1 = l1;
        ListNode* cur2 = l2;
        int carry = 0;

        // 如果两个链表还有一个没有走完还得继续走
        // 如果链表都走完了,但是要记得检查进位,判断是否还有剩余进位
        while(cur1 || cur2 || carry)
        {
            // 获取节点的值,如果节点为空则默认该节点的值为0,不影响加的结果
            carry += cur1 ? cur1->val : 0;
            carry += cur2 ? cur2->val : 0;

            // carry的个位就是该节点要存储的,不要忘了保存进位
            ListNode* newnode = new ListNode(carry % 10);
            carry /= 10;
            
            // 尾插
            tail->next = newnode;
            tail = newnode;

            // 移动cur1/2到下一个节点
            cur1 = cur1 ? cur1->next : cur1 = nullptr;
            cur2 = cur2 ? cur2->next : cur2 = nullptr;
        }
        // newhead是new出来的,避免内存泄露,在返回之前将其delete掉
        tail = newhead->next;
        delete newhead;

        return tail;
    }
};

2.链表相加2

这道题与上面很像,这不过这里的链表是按照高位到地位,返回的结果也是按照高位到地位。 

解法

先将两个链表逆置,然后再利用上一题的方法进行相加并得出逆序的结果,最后再将和链表逆置就可以得出答案。

时间复杂度:O(n)

空间复杂度:O(1)

class Solution 
{
public:
    ListNode* addInList(ListNode* head1, ListNode* head2) 
    {
        if((head1 == nullptr && head2 == nullptr) || (head1 == nullptr && head2)) return head2;
        if(head1 && head2 == nullptr) return head1;

        // 1.先将原链表进行逆置 
        // 头插法
        ListNode* newhead1 = new ListNode(0);
        ListNode* newhead2 = new ListNode(0);
        while(head1 || head2)
        {
            if(head1)
            {
                ListNode* next = head1->next;
                head1->next = newhead1->next;
                newhead1->next = head1;
                head1 = next;
            }

            if(head2)
            {
                ListNode* next = head2->next;
                head2->next = newhead2->next;
                newhead2->next = head2;
                head2 = next;
            }
        }

        // 2.同时遍历两个逆置链表,进行加法运算
        ListNode* ret = new ListNode(0);
        ListNode* cur1 = newhead1->next;
        ListNode* cur2 = newhead2->next;
        int carry = 0;
        while(cur1 || cur2 || carry)
        {
            carry += cur1?cur1->val:0;
            carry += cur2?cur2->val:0;
            ListNode* newNode = new ListNode(carry%10);
            carry /= 10;

            // 头插法,将和链表逆置回来
            newNode->next = ret->next;
            ret->next = newNode;

            cur1 = cur1?cur1->next:nullptr;
            cur2 = cur2?cur2->next:nullptr;
        }

        cur1 = ret->next;
        delete ret;
        delete newhead1;
        delete newhead2;

        return cur1;
    }
};

3.反转链表

 解法1:头插法

我们定义一个哨兵头节点,然后遍历原链表,将原链表的节点都头插到哨兵头节点的后面。

时间复杂度:O(n)

空间复杂度:O(1) 

ListNode* _ReverseList(ListNode* head)
{
    // 头插法反转链表
    if (!head || !head->next) return head;

    ListNode* ret = new ListNode(0);
    while (head)
    {
        ListNode* next = head->next;
        head->next = ret->next;
        ret->next = head;
        head = next;
    }
    head = ret->next;
    delete ret;
    return head;
}

 解法2:双指针:

借助两个指针cur1和cur2,在原链表上修改指针的指向来实现逆置。cur2指向头节点,cur1指向空,然后用cur2遍历原链表,遍历的同时,让cur2的next指向cur1,实现逆置,然后让cur1走到cur2的位置,然后cur2走到下一个位置。在这个过程中需要用一个next指针提前记录cur2的next位置。

时间复杂度:O(n)

空间复杂度:O(1) 

ListNode* __ReverseList(ListNode* head)
{
    // 双指针反转链表
    if (!head || !head->next) return head;

    ListNode* cur1 = nullptr;
    ListNode* cur2 = head;
    while (cur2)
    {
        ListNode* next = cur2->next;
        cur2->next = cur1;
        cur1 = cur2;
        cur2 = next;
    }
    return cur1;
}

 解法3:递归

首先借助递归找到尾节点,以该节点为头节点开始返回。返回的过程中,将该层的head的next的next指针指向自己即head的前一个节点的next指针指向自己,此时就达到了后一个指向前一个,接着将head的next指针指向空,避免成环。接着继续递归返回。

下面是链表递归到尾节点第一次返回的时候:

等到下一层时,head就到了2节点,此时再让前一个节点的next指向自己,在将自己的next置为空,就将2号节点连接到了ans链表中。 

 时间复杂度:O(n)

空间复杂度:O(n),递归会消耗栈空间,递归所需的栈空间就是链表的长度

ListNode* ReverseList(ListNode* head)
{
    // 递归反转链表
    if (!head || !head->next) return head;

    ListNode* ans = ReverseList(head->next);
    head->next->next = head;
    head->next = nullptr;
    return ans;
}

4.两两交换链表中的节点

 解法1:哨兵头节点+快慢指针

利用快慢双指针遍历原链表,让fast指针先尾插到哨兵头节点后,接着尾插slow,重复该过程。在遍历的过程中,如果原链表是奇数个,最后一次slow不为空,fast为空,退出循环,直接将slow尾插即可;如果原链表是偶数个,最后一个slow和fast都不为空,尾插结束后,slow走到空,循环结束。但记得最后让待返回的链表的尾节点指向空,否则答案可能会错误!

时间复杂度:O(n),只遍历了一遍链表

空间复杂度:O(1)

 // 借助哨兵头节点+快慢指针
 ListNode* swapPairs(ListNode* head) 
 {
     // 链表为空/链表只有一个节点,直接返回
     if(head == nullptr || head->next == nullptr) return head;

     // 创建虚拟头节点
     ListNode* virtualHead = new ListNode;
     ListNode* tail = virtualHead;

     // 定义快慢指针
     ListNode* slow = head;
     ListNode* fast = slow->next;

     while(fast && slow)
     {
         ListNode* _fast = fast;
         ListNode* _slow = slow;
         slow = fast->next;
         if(slow)
             fast = slow->next;

         tail->next = _fast;
         tail = _fast;
         tail->next = _slow;
         tail = _slow;
     } 
     if(slow)
     {
         tail->next = slow;
         tail = slow;
     }
     tail->next = nullptr;

     tail = virtualHead->next;
     delete virtualHead;

     return tail;
 }

 解法2:哨兵头节点+原地修改链表指向

首先将哨兵头节点连接到原链表头部,接着定义4个指针prev,cur,next,nnext,从哨兵开始依次指向一个节点。接下来只需要根据图示修改指针指向即可:

prev->next = next; next->next = cur;cur->next = nnext;

修改一次之后,cur直接指向第3个节点——nnext,其他的三个指针分别修改好,继续修改指向。 在修改next和nnext指针时要注意空指针解引用的问题。

结束条件:当cur遍历到空时,说明已经没有节点需要交换了;当next指向空时,说明只剩cur一个节点了,此时也不需要交换。

时间复杂度:O(n),每个节点都被访问一次

空间复杂度:O(1)

 ListNode* swapPairs(ListNode* head) 
 {
     // 链表为空/链表只有一个节点,直接返回
     if(head == nullptr || head->next == nullptr) return head;

     // 创建虚拟头节点
     ListNode* virtualHead = new ListNode;
     virtualHead->next = head;

     ListNode* prev = virtualHead;
     ListNode* cur = head;
     ListNode* next = cur->next;
     ListNode* nnext = next->next;
     while(cur && next)
     {
         prev->next = next;
         next->next = cur;
         cur->next = nnext;

         prev = cur;
         cur = nnext;
         if(cur) next = cur->next;
         if(next) nnext = next->next;
     }
     cur = virtualHead->next;
     delete virtualHead;

     return cur;
 }

 解法3:递归

我们将节点两个两个分组,并且我们确保在交换前两个节点时,后面的链表已经交换完成了,并且返回了一个新的头节点tmp。我们用新头节点newhead指向旧头节点oldhead,然后用oldhead指向后面已经完成交换的链表,将两个部分连接起来,其实newhead就是我们完成交换之后的头节点。

简单来说,对于原链表来说,每两个一组的节点,第一个节点是新链表的第二个节点,第二个节点是新链表的头节点。 

时间复杂度:O(n),每一个节点都被访问了

空间复杂度:O(n),递归所需要的栈空间,每两个节点需要一个函数栈帧

// 递归
ListNode* swapPairs(ListNode* head)
{
    if (head == nullptr || head->next == nullptr) return head;

    ListNode* newhead = head->next;
    ListNode* tmp = swapPairs(newhead->next); // 下层传上来的交换后的头节点
    newhead->next = head;
    head->next = tmp;

    return newhead;
}

5.重排链表

该题的意思其实是按照第一个倒数第一个,第二个倒数第二个...的方式将链表重新排列后返回。 

解法:

我们观察规律可以得出,重排后的链表其实是可以由两个链表合并得来的。而且这两个链表就是从原链表中间分开,前半部分和后半部分的逆置。

所以我们的解决策略就是先找出原链表的中心节点,接着将后半部分逆置,然后借助哨兵头节点,将这样个链表进行合并,合并时先尾插前半部分,再尾插后半部分。

1.对于寻找链表的中间节点,我们可以采取快慢双指针的方式,fast一次走两步,slow一次走一步,当fast走到空/尾时,slow的位置就是中间节点的位置。但是这种方法对于奇数链表来说,slow会停在中间位置,对于偶数链表,slow会停在中间靠右的节点。 

2.接下来的逆置我们既可以直接逆置slow及以后的部分,也可以逆置slow后面的部分。两者都是可以的。下面只给出偶数节点的例子,对于奇数节点个数也是成立的。

虽然两种方式都可以,但是这里建议将slow之后的链表进行逆置。因为这样可以将原链表断开,形成两个独立的链表,合并时更好处理。如果逆置slow及之后的话,无法将前后链表断开。 

3. 采取逆置slow之后,我们会得到两个独立的链表,接下来只需要合并即可。注意合并顺序:先前再后。

时间复杂度:O(n),虽然三次遍历链表,但整体上依旧是O(n)

空间复杂度:O(1) 

void reorderList(ListNode* head)
{
    // 当链表为空/一个节点/两个节点,都不需要发生变换,直接返回
    if (head == nullptr || head->next == nullptr || head->next->next == nullptr)
        return;

    // 1.找到链表的中间节点,采用快慢双指针
    // 奇数个slow会落在中间节点
    // 偶数个slow会落在中间靠右的节点
    ListNode* fast = head;
    ListNode* slow = head;
    while (fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
    }

    // 2.逆序后半部分链表——头插法
    // 这里采取逆序mid后面的部分,不包括mid节点
    ListNode* newhead = new ListNode;
    ListNode* mid = slow->next; // 先记录下需要逆序的部分
    slow->next = nullptr; // 将前半部分和需要逆序的部分断开
    while (mid)
    {
        ListNode* next = mid->next;
        mid->next = newhead->next;
        newhead->next = mid;
        mid = next;
    }

    // 3.合并两个链表-head和newhead
    ListNode* ret = new ListNode;
    ListNode* tail = ret;
    ListNode* cur1 = head, * cur2 = newhead->next;
    while (cur1)
    {
        tail->next = cur1;
        tail = cur1;
        cur1 = cur1->next;

        if (cur2)
        {
            tail->next = cur2;
            tail = cur2;
            cur2 = cur2->next;
        }
    }
    head = ret->next;
    delete newhead;
    delete ret;
}

6.合并K个升序链表

lists的每一个元素都是一个链表,我们需要将这样链表以升序的格式全部合并最终返回。

 解法1:两两合并

虽然有多个链表,但是我们可以两个两个合并,用合并的结果在于下一个合并,重复该过程,直到将所有的链表都合并了。

时间复杂度:O(nk^2),我们假设平均每个链表的节点数是n,有k个链表,第一个链表需要合并k-1次,第二个链表合并k-2次,最后一个链表合并1次。所以加起来就是n*k^2次。

空间复杂度:O(1)

// 1.两两合并
ListNode* _mergeKLists(vector<ListNode*>& lists)
{
    if (lists.size() == 0) return nullptr;
    else if (lists.size() == 1) return lists[0];

    ListNode* ret = lists[0];
    for (int i = 1; i < lists.size(); ++i)
    {
        ListNode* tmp = new ListNode;
        ListNode* tail = tmp;
        ListNode* cur = lists[i];

        // 合并两个有序链表
        while (ret && cur)
        {
            if (ret && cur && ret->val <= cur->val)
            {
                tail->next = ret;
                tail = ret;
                ret = ret->next;
            }
            else if(ret && cur && ret->val > cur->val)
            {
                tail->next = cur;
                tail = cur;
                cur = cur->next;
            }
        }
        while (ret)
        {
            tail->next = ret;
            tail = ret;
            ret = ret->next;
        }
        while (cur)
        {
            tail->next = cur;
            tail = cur;
            cur = cur->next;
        }

        // 此时cur和ret的和在tmp中,将tmp的值赋给ret
        // cur与第一次的和继续合并
        ret = tmp->next;
        delete tmp;
    }
    return ret;
}

 解法2:优先级队列——小根堆

我们可以定义k个指针指向每个链表的头,让这k个节点进入小堆,此时堆顶数据就是最小的,我们将他尾插到ret链表,接着删除堆顶数据,将堆顶元素所在链表的下一个节点push到堆中。重复该过程。

时间复杂度:O(nlogk),每一个节点插入到堆中是logk,因为要进行向下调整

空间复杂度:O(k),优先级队列中同时最多包含K个节点

// 2.借助priority_queue
ListNode* __mergeKLists(vector<ListNode*>& lists)
{
    // 借助小根堆,先将vector中的所有头节点放入堆中,此时堆顶节点的val值就是最小的。
    // 取出堆顶节点尾插到哨兵位头节点后,接着从堆中删除该节点,将该节点的next插入到堆中。
    // 堆不为空,持续该过程。
    // 再将节点插入到堆中时要判断该节点是否为空
    size_t n = lists.size();
    if (n == 0) return nullptr;
    else if (n == 1) return lists[0];

    ListNode* ret = new ListNode;
    ListNode* tail = ret;
    priority_queue < ListNode*, vector<ListNode*>, decltype([](ListNode* l1, ListNode* l2) {return l1->val > l2->val; }) > heap;
    for (auto l : lists)
        if (l) heap.emplace(l);

    while (!heap.empty())
    {
        ListNode* min = heap.top();
        heap.pop();
        tail->next = min;
        tail = min;
        if (min->next)
            heap.emplace(min->next);
    }
    tail = ret->next;
    delete ret;
    return tail;
}

解法三:分治——归并策略

我们可以采取归并排序的策略,将所有的链表分为两部分,先合并左边,在合并右边,最后将这两个合并好的链表再一合并即可。

时间复杂度:O(nklogk),划分的过程中高度是logk,一共有nk个节点

空间复杂度:O(logk),递归的深度是logk,消耗了logk的栈空间

// 3. 分治——归并策略
    ListNode* mergeTwoList(ListNode* cur1, ListNode* cur2)
    {
        if(!(cur1) || !(cur2)) return !cur1 ? cur2 : cur1;

        ListNode ret(0);
        ListNode* tail = &ret;
        while(cur1 && cur2)
        {
            if(cur1->val <= cur2->val)
            {
                tail->next = cur1;
                tail = cur1;
                cur1 = cur1->next;
            }
            else
            {
                tail->next = cur2;
                tail = cur2;
                cur2 = cur2->next;
            }
        }
        tail->next = cur1?cur1:cur2;
        return ret.next;
    }

    ListNode* merge(vector<ListNode*>& lists, int left, int right)
    {
        if(left>=right) return left == right ? lists[left] : nullptr;
        int mid = (right-left)/2+left;

        // [left, mid] [mid+1, right]
        ListNode* cur1 = merge(lists, left, mid); // 接收左边合并后的结果
        ListNode* cur2 = merge(lists, mid+1, right); // 接收右边合并后的结果

        return mergeTwoList(cur1, cur2); // 合并两个有序数组
    }

    ListNode* mergeKLists(vector<ListNode*>& lists)
    {  
        return merge(lists,0,lists.size()-1);
    }

7.K个一组反转链表

给一个节点,在一个数k,将链表每k个为一组,进行反转,如果最后不足k个,就直接保留。

解法:模拟

1.我们可以首先求出需要反转多少次,n个节点,k个一组,那么就有n/k组,每组都要反转一次,一共反转n/k次。

2.头插法反转链表,需要注意,每进行一次反转要标记第一个头插到链表的节点,下一次反转将节点都插在标记节点的后面,而不是哨兵头结点的后面。

时间复杂度:O(n)

空间复杂度:O(1)

ListNode* reverseKGroup(ListNode* head, int k)
{
    if (!head || k == 1) return head;

    // 1.先算出需要进行几次逆序操作
    int n = 0;
    ListNode* cur = head;
    while (cur)
    {
        n += 1;
        cur = cur->next;
    }
    n /= k;

    // 2.n次逆序
    ListNode* newhead = new ListNode;
    ListNode* tail = newhead;
    cur = head;
    while (n--)
    {
        ListNode* tmp = tail;
        for (int i = 0; i < k; ++i)
        {
            if (i == 0) tail = cur; // 标记每次反转的第一个节点

            ListNode* next = cur->next;
            cur->next = tmp->next;
            tmp->next = cur;
            cur = next;
        }
    }
    if (cur) tail->next = cur;
    tail = newhead->next;
    delete newhead;
    return tail;
}

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

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

相关文章

blender学习25.3.8

【04-进阶篇】Blender材质及灯光Cycle渲染&后期_哔哩哔哩_bilibili 注意的问题 这一节有一个大重点就是你得打开显卡的渲染&#xff0c;否则cpu直接跑满然后渲染的还十分慢 在这里你要打开GPU计算&#xff0c;但是这还不够 左上角编辑&#xff0c;偏好设置&#xff0c;系…

【godot4.4】布局函数库Layouts

概述 为了方便编写一些自定义容器和控件、节点时方便元素布局&#xff0c;所以编写了一套布局的求取函数&#xff0c;统一放置在一个名为Layouts的静态函数库中。 本文介绍我自定义的一些布局计算和实现以及函数编写的思路&#xff0c;并提供完整的函数库代码&#xff08;持续…

Windows下配置Conda环境路径

问题描述&#xff1a; 安装好Conda之后&#xff0c;创建好自己的虚拟环境&#xff0c;同时下载并安装了Pycharm&#xff0c;但在Pycharm中找不到自己使用Conda创建好的虚拟环境。显示“Conda executable is not found” 解决办法&#xff08;依次尝试以下&#xff09; 起初怀…

OpenHarmony子系统开发编译构建指导

OpenHarmony子系统开发编译构建指导 概述 OpenHarmony编译子系统是以GN和Ninja构建为基座&#xff0c;对构建和配置粒度进行部件化抽象、对内建模块进行功能增强、对业务模块进行功能扩展的系统&#xff0c;该系统提供以下基本功能&#xff1a; 以部件为最小粒度拼装产品和独…

leetcode日记(80)复原IP地址

只能说之前动态规划做多了&#xff0c;看到就想到动态规划&#xff0c;然后想想其实完全不需要&#xff0c;回溯法就行了。 一开始用了很多莫名其妙的代码&#xff0c;写的很复杂……&#xff08;主要因为最后不能加‘.’&#xff09;其实想想只要最后加入vector时去掉最后一个…

LINUX网络基础 [五] - HTTP协议

目录 HTTP协议 预备知识 认识 URL 认识 urlencode 和 urldecode HTTP协议格式 HTTP请求协议格式 HTTP响应协议格式 HTTP的方法 HTTP的状态码 ​编辑HTTP常见Header HTTP实现代码 HttpServer.hpp HttpServer.cpp Socket.hpp log.hpp Makefile Web根目录 H…

【A2DP】SBC 编解码器互操作性要求详解

目录 一、SBC编解码器互操作性概述 二、编解码器特定信息元素(Codec Specific Information Elements) 2.1 采样频率(Sampling Frequency) 2.2 声道模式(Channel Mode) 2.3 块长度(Block Length) 2.4 子带数量(Subbands) 2.5 分配方法(Allocation Method) 2…

电脑内存智能监控清理,优化性能的实用软件

软件介绍 Memory cleaner是一款内存清理软件。功能很强&#xff0c;效果很不错。 Memory cleaner会在内存用量超出80%时&#xff0c;自动执行“裁剪进程工作集”“清理系统缓存”以及“用全部可能的方法清理内存”等操作&#xff0c;以此来优化电脑性能。 同时&#xff0c;我…

基于multisim的花样彩灯循环控制电路设计与仿真

1 课程设计的任务与要求 &#xff08;一&#xff09;、设计内容&#xff1a; 设计一个8路移存型彩灯控制器&#xff0c;基本要求&#xff1a; 1. 8路彩灯能演示至少三种花型&#xff08;花型自拟&#xff09;&#xff1b; 2. 彩灯用发光二极管LED模拟&#xff1b; 3. 选做…

Axure常用变量及使用方法详解

点击下载《Axure常用变量及使用方法详解.pdf》 摘要 Axure RP 作为一款领先的前端原型设计工具&#xff0c;提供了全面的 变量 和 函数 系统&#xff0c;以支持复杂的交互设计和动态内容展示。本文将从专业角度详细解析 Axure 中的 全局变量、中继器数据集变量/函数、元件变量…

MySql的安装及数据库的基本操作命令

1.MySQL的安装 1.1进入MySQL官方网站 1.2点击下载 1.3下拉选择MySQL社区版 1.4选择你需要下载的版本及其安装的系统和下载方式 直接安装以及压缩包 建议选择8.4.4LST LST表明此版本为长期支持版 新手建议选择红框勾选的安装方式 1.5 安装包下载完毕之后点击安装 2.数据库…

树莓派5首次开机保姆级教程(无显示器通过VNC连接树莓派桌面)

第一次开机详细步骤 步骤一&#xff1a;树莓派系统烧录1 搜索打开烧录软件“Raspberry Pi Imager”2 选择合适的设备、系统、SD卡3 烧录配置选项 步骤二&#xff1a;SSH远程树莓派1 树莓派插电2 网络连接&#xff08;有线或无线&#xff09;3 确定树莓派IP地址 步骤三&#xff…

分布式锁—7.Curator的分布式锁

大纲 1.Curator的可重入锁的源码 2.Curator的非可重入锁的源码 3.Curator的可重入读写锁的源码 4.Curator的MultiLock源码 5.Curator的Semaphore源码 1.Curator的可重入锁的源码 (1)InterProcessMutex获取分布式锁 (2)InterProcessMutex的初始化 (3)InterProcessMutex.…

电脑睡眠智能管控:定时、依状态灵活调整,多模式随心选

软件介绍 今天要给大家介绍一款十分实用的软件——DontSleep&#xff0c;从名字就能看出&#xff0c;它的核心功能之一是阻止电脑进入睡眠状态&#xff0c;不过它的能耐可远不止于此&#xff0c;还具备强大的定时以及根据电脑实时状况灵活调整的功能。 这款软件采用绿色单文件…

装饰器模式--RequestWrapper、请求流request无法被重复读取

目录 前言一、场景二、原因分析三、解决四、更多 前言 曾经遇见这么一段代码&#xff0c;能看出来是把request又重新包装了一下&#xff0c;核心信息都不会改变 后面了解到这叫 装饰器模式&#xff08;Decorator Pattern&#xff09; &#xff1a;也称为包装模式(Wrapper Pat…

C++20 格式化库:强大的字符串格式化工具

文章目录 格式化语法常见用法1. 填充和对齐2. 数值格式化3. 进制格式化4. 自定义类型 示例代码注意事项 C20 的格式化库是一个强大的工具&#xff0c;用于处理字符串的格式化操作。它提供了类似于 Python 中 str.format() 的功能&#xff0c;但语法和用法更符合 C 的风格。以下…

Linux基础--文件权限+软件包管理+管道符

目录 基础权限 更改文件权限 使用命令:chmod 更改文件属主和数组 使用命令: chown 权限掩码 使用命令:umask 高级权限 软件包管理 使用命令: rpm 使用命令: yum 管道符&#xff0c;重定向 基础权限 文件基础权限表 符号含义数字r读权限4w写权限2x执行权限1 更改文件…

css画出带圆角平行四边形效果

使用css画出平行四边形效果如下图 HTML代码 <div class"badge"><span>营业中</span> </div> 关键代码&#xff1a; transform: skewX(-15deg); /* 让元素倾斜&#xff0c;形成平行四边形的视觉效果 */ 如果倾斜的元素里面需要放文字&…

postman接口请求中的 Raw是什么

前言 在现代的网络开发中&#xff0c;API 的使用已经成为数据交换的核心方式之一。然而&#xff0c;在与 API 打交道时&#xff0c;关于如何发送请求体&#xff08;body&#xff09;内容类型的问题常常困扰着开发者们&#xff0c;尤其是“raw”和“json”这两个术语之间的区别…

Gartner:数据安全平台DSP提升数据流转及使用安全

2025 年 1 月 7 日&#xff0c;Gartner 发布“China Context&#xff1a;Market Guide for Data Security Platforms”&#xff08;《数据安全平台市场指南——中国篇》&#xff0c;以下简称指南&#xff09;&#xff0c;报告主要聚焦中国数据安全平台&#xff08;Data Securit…