DS:顺序表、单链表的相关OJ题训练

news2024/11/26 20:39:47

欢迎各位来到 Harper.Lee 的学习小世界!

博主主页传送门:Harper.Lee的博客主页

想要一起进步的uu可以来后台找我交流哦!

        在DS:单链表的实现  和  DS:顺序表的实现这两篇文章中,我详细介绍了顺序表和单链表的实现方法及其相关注意点,uu们可以点击跳转学习。俗话说“光说不练假把式”,下面我们通过几道经典OJ算法题来进行顺序表和单链表的相关训练!

一、力扣--203. 移除链表元素

        题目:给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回新的头节点 。

思路1:遍历原链表,将值为val的节点释放掉(很像单链表中删除指定位置的节点)。

思路2:找值不为val的节点,将其尾插到不带头的新链表中,返回该新链表的头节点。
         创建的结构体指针newHead和newTail均指为空。改变的是newTail,而不是newHead。 a. 创建链表的第一个节点:从原链表第一个节点开始判断,该节点的值如果不是val,那么他就是新链表第一个节点newHead从第一个节点开始创建新链表,相当于是头插(同时也是尾插); b. 其余节点依次进行遍历并判断节点的值是否为val:如果是,就进行释放操作 ,如果不是,就尾插入新链表中,用newTail来接收; c. 遍历链表使用pcur。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;//说明该结构体中的内容,但是如果要使用该结构体,
 就需要struct ListNode来定义结构体,太麻烦,所以重命名 
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val) {
    //创建一个新链表
    ListNode* newHead,*newTail;//????
    newHead = newTail=NULL;

    //遍历原链表
    ListNode* pcur = head;//head是传进来的参数(结构体指针)
    while(pcur)//pcur用来遍历链表
    {
        //找值不为val的节点,尾插到新链表。
        if(pcur->val!=val)
        {
            //链表为空
            if(newHead==NULL)
            {
                newHead=newTail=pcur;//插入的第一个节点既是头节点也是尾节点
            }
        //链表不为空
           else
             {
                //newtail的下一个位置插入数据
                newTail->next=pcur;
                newTail=newTail->next;
             }
        }
        pcur=pcur->next;//pcur往下走继续遍历
    }
    if(newTail)
        newTail->next = NULL;//newTail的next指针要置为空
    return newHead;
}

二、力扣--206. 反转链表

        题目描述:给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

        思路1:创建一个新链表newHead和newTail,从第一个节点(作为newTail且不再改变)开始遍历原链表同时将原链表的节点利用头插法建立该新链表。

        思路2:创建3个指针,完成原链表的翻转 。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;//已知存储的数据和指向下一个节点的指针
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;//将结构体指针struct ListNode重命名为ListNode
struct ListNode* reverseList(struct ListNode* head) {
    //先判断链表是否为空
    if(head==NULL)
    {
        return head;
    }
    //创建三个指针:n1 n2 n3 ,让n2的next指向n1,
    //n1 = n2,n2 = n3,n3 = n3->next
    ListNode* n1, *n2,*n3;
    n1 = NULL,n2 = head;n3 = n2->next;//初始化三个指针,n2指向n3
    while(n2)
    {
        //先反转方向,再n1=n2; n2=n3;n3=n3->next;
        n2->next=n1; //n2的next指针指向
        n1=n2;
        n2=n3;
        if(n3)//n3 走到为空了,不能继续走也就是不能解引用了
            n3=n3->next;
    }
    return n1;
    //因为链表的接待有数据范围,所以说明链表 可能为空,n3 = n2->next,可能不合适
}

三、力扣--21. 合并两个有序链表 

        题目描述:将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

        从前往后找小数据,数据会丢失,所以从后往前找大数据.

第一个版本的代码(会报错) :

typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {

    ListNode* l1 = list1;
    ListNode* l2 = list2;

    //创建新链表
    ListNode* newTail,*newHead;
    newHead = newTail = NULL;

    while(l1 && l2)//使用 &&,因为两个要同时满足非空链表 
    {
        if(l1->val<l2->val)//开始比较存储的值的大小
        {
            //l1拿下来进行尾插
            if(newHead == NULL)//头节点为空
            {
                newHead=newTail=l1;
            }else{//新链表不为空
                newTail->next=l1;
                newTail=newTail->next;
            }
            l1=l1->next;//原来的链表也要继续往前遍历
        
        }else{
            //l2拿下来尾插
            if(newHead==NULL)
            {
                newHead=newTail=l2;
            }else{
                newTail->next=l2;
                newTail=newTail->next;
            }
            l2=l2->next;
        }
    }
    //跳出循环,面临两种情况:l1走到空 或者 l2走到空,上下一个节点,止咳尾插,不用再比较了
    if(l2)//l2不为空
    {
        newTail->next = l2;
    }
    if(l1)
    {
        newTail->next = l1;
    }
    return newHead;
}

1、 报错:

分析报错具体原因:

         如果list1为空链表,l1也为空,不能进入while循环,所以来到跳出while循环的 if 部分。l2不为空,执行 newTail->next = l2; 但是因为没有进入while循环,所以新链表也无法在while循环中创建节点,也就是说,新链表仍然时空连,而此刻 newTail->next 操作 相当于对空指针进行解引用操作。因而报出上图所示错误。

解决办法:链表为空有一个提示,说明链表可能同时为空。根据提修改代码:判断链表为空的情况:做另外的处理。

        首先链表是已经排为升序了的,如果其中一个链表为空,可直接返回里另外一个链表;如果两个链表都为空,那么就直接返回NULL。先增加判断链表是否为空的情况。

(第二个版本代码)正确代码如下:

typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
    //判断链表是否为空:(添加部分在这里)
    if(list1 == NULL)
    {
        return list2;
    }
    if(list2 == NULL)
    {
        return list1;
    }

    ListNode* l1 = list1;
    ListNode* l2 = list2;

    //创建新链表
    ListNode* newTail,*newHead;
    newHead = newTail = NULL;

    while(l1 && l2)//使用 &&,因为两个要同时满足非空链表 
    {
        if(l1->val<l2->val)//开始比较存储的值的大小
        {
            //l1拿下来进行尾插
            if(newHead == NULL)//头节点为空
            {
                newHead=newTail=l1;
            }else{//新链表不为空
                newTail->next=l1;
                newTail=newTail->next;
            }
            l1=l1->next;//原来的链表也要继续往前遍历
        
        }else{
            //l2拿下来尾插
            if(newHead==NULL)
            {
                newHead=newTail=l2;
            }
            else
            {
                newTail->next=l2;
                newTail=newTail->next;
            }
            l2=l2->next;
        }
    }
    //跳出循环,面临两种情况:l1走到空 或者 l2走到空,上下一个节点,止咳尾插,不用再比较了
    if(l2)//l2不为空
    {
        newTail->next = l2;
    }
    if(l1)
    {
        newTail->next = l1;
    }
    return newHead;
}

2、存在重复代码,如何优化代码? 

        在上面的代码中观察发有一段重复出现的代码:将节点拿下来进行尾插处理。我们将这部分代码进行优化处理。一种优化方法是:函数封装。此外,还可以进行

代码重复的原因:新链表存在空链表和非空链表两种情况。

解决思路:让新链表不为空。

解决办法:之前的newTaail和newHead是将其创建为空(NULL),现在为它动态申请一块内存

        newHead = newTail = NULL;  改成newHead = newTail = (ListNode*)malloc(sizeof(ListNode));    动态申请空间但是不存储任何数据。此时链表不为空,头尾指针都指向一个有效的地址(节点)。然后将while循环中判断链表是否为空的代码删掉:

删除如下注释:

//while循环中删除判断部分:    
    while(l1 && l2)//使用 &&,因为两个要同时满足非空链表 
    {
        if(l1->val<l2->val)//开始比较存储的值的大小
        {
            //l1拿下来进行尾插
            //if(newHead == NULL)//头节点为空
            //{
                //newHead=newTail=l1;
            //else{//新链表不为空
                newTail->next=l1;
                newTail=newTail->next;
             // }
            l1=l1->next;//原来的链表也要继续往前遍历
        
        }else{
            //l2拿下来尾插
            //if(newHead==NULL)
            //{
                //newHead=newTail=l2;
           // }
           // else
           // {
                newTail->next=l2;
                newTail=newTail->next;
           // }
            l2=l2->next;
        }
    }

         3、 注意,此时不能或者直接返回newHead,因为最后得到的链表多了一部分,这一部分是因为系统随机给动态申请的空间赋随机值。因此要返回不包含此部分的链表。return newHead->next;(最后的链表如下图所示:)

        4、此外,动态内存申请的空间都是要释放掉的,而这里没有释放操作但不会报错,是因为:我们写的程序需要提交到力扣服务端进行运行后,会自动释放但是如果是我们自己写代码,为了养成好的空间开辟习惯,记得释放空间。如果直接 free(newHead);  这样就找不到原来的空间了,也就不能通过return newHead->next;将最终结果返回了。因此,利用结构体指针ret记录空间,然后再释放,返回ret。

最终代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
    //判断链表是否为空:
    //newHead = newTail = NULL;
    newHead = newTail = (ListNode*)malloc(sizeof(ListNode));

    ListNode* l1 = list1;
    ListNode* l2 = list2;

    //创建新链表
    ListNode* newTail,*newHead;
    // newHead = newTail = NULL;
    newHead = newTail = (ListNode*)malloc(sizeof(ListNode));//动态申请空间但是不存储任何数据 
    while(l1 && l2)//使用 &&,因为两个要同时满足非空链表 
    {
        if(l1->val<l2->val)
        {
            newTail->next=l1;
            newTail=newTail->next;
            l1=l1->next;//原来的链表也要继续往前遍历
        }
        else
        {
            newTail->next=l2;
            newTail=newTail->next;
            l2=l2->next;
        }
    }
    if(l2)//l2不为空
    {
        newTail->next = l2;
    }
    if(l1)
    {
        newTail->next = l1;
    }
    ListNode* ret = newHead->next;
    free(newHead);
    newHead = NULL;
    return ret;
}

         5、刚动态申请空间的那部分节点没有存入数据,它实际是链表分类中的一种,叫做带头链表。头节点叫做哨兵位,只起站岗的作用,没有任何有效意义。

此题和之前的合并两个有序数组有点相似。

四、力扣--876. 链表的中间节点

        题目描述:给你单链表的头结点 head ,请你找出并返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。

思路一:count 记录节点数,直接返回(count/2)节点的next节点

思路二: 创建指针数组用来记录每一次遍历的数组,然后长度除以对应的数组的指针就是中间节点。

思路三:快慢指针法奇数个节点slow每次走一步,fast每次走两步,重复这个操作,直到fast->next==NULL时,停止,此时,slow指向的就是中间节点;偶数个节点slow每次走一步,fast每次走两步,重复这个操作,直到fast==NULL时,停止,此时,slow指向的就是中间节点。即使链表很长好,此方法也可以,因为对链表只需遍历一遍。其中的原理在于:满足2slow=fast。

 typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) {
    //创建快慢指针:
    ListNode* slow = head;
    ListNode* fast = head;
    while(fast && fast->next)//fast和fats->next不能交换位置,
    //如果链表是在有欧束个节点的情况下,fast的最后一次会直接走到空,fast->next就是对其进行解引用操作,->相当于解引用操作符
    {
        slow=slow->next;
        fast=fast->next->next;
    }
    //此时slow指向的就是中间节点
    return slow;
}

1、循环条件可以进行交换吗?

         循环条件:while(fast && fast->next),其中循环条件不能进行交换,否则会出现链表为偶数个节点的情况下,fast走到头时,fast == NULL已经满足了,fast->next为空,空指针解引用报错的情况。

2、快慢指针总结:

        慢指针每次走一步,快指针每次走两步,慢指针*2 = 快指针。快指针遍历完一遍链表时,快指针在链表的中间位置  。如果链表的节点有偶数个,fast==NULL;如果链表的节点是奇数个,fast->next==NULL。

五、牛客--环形链表的约瑟夫问题

循环链表的经典应用

背景:著名的Josephus问题
据说著名犹太历史学家Josephus有过以下的故事:在罗⻢⼈占领乔塔帕特后,39个犹太⼈与
Josephus及他的朋友躲到⼀个洞中,39个犹太⼈决定宁愿死也不要被⼈抓到,于是决定了⼀个⾃杀⽅式,41个⼈排成⼀个圆圈,由第1个⼈开始报数,每报数到第3⼈该⼈就必须⾃杀,然后再由下⼀个重新报数,直到所有⼈都⾃杀⾝亡为⽌。
然⽽Josephus?和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己排在第16个与第31个位置,于是逃过了这场死亡游戏。 

详细分析过程如下图片所示(第一次 尝试传入手写笔记照片,可能效果不太理想): 

创建代还链表的过程:

        首先创建第一个节点,该节点既是phead也是ptail,然后继续创建节点,并将之前先创建的节点与该节点连接在一起(ptail = ptail->next;),直到最后一个节点连接进去。 但此时还没有形成带环链表,加上 ptail->next = phead; 头尾相连,链表成环。

代码如下:

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param n int整型 
 * @param m int整型 
 * @return int整型
 */

#include <stdlib.h>
typedef struct ListNode ListNode;
//创建节点:
ListNode* buyNode(int x)
{
    ListNode* node = (ListNode*)malloc(sizeof(ListNode));
    if(node ==  NULL)
    {
        exit(1);//内存申请失败
    }
    node->val = x;
    node->next = NULL;
    return node;
}
//创建带环链表:
ListNode* creatCircle(int n)
{
    //先创建第一个节点
    ListNode* phead = buyNode(1);//第一个节点存储数据1
    ListNode* ptail = phead;//ptail用来尾插
    for(int i = 2;i <= n;i++)
    { 
        ptail->next = buyNode(i);//将新创建的节点与链表连接在一起
        ptail = ptail->next;//ptail也要继续前进
    }
    ptail->next = phead;//(首尾相连,链表成环)
    //return 的方法
    return ptail;//不返回phead,而是返回ptail
}
int ysf(int n, int m ) {
    //1. 根据n创建带环链表
    ListNode* prev = creatCircle(n);//这三句代码就是图片中重复操作的那部分过程
    ListNode* pcur = prev->next;
    int count = 1;//初始时count为1,因为前面一句代码中pcur指向了第一个节点,就需要报数
    //开始报数
    //当链表只有一个节点时, 跳出循环(最后一个节点的next指向它自己)
    while(pcur->next != pcur)//当链表只有最后一个节点且指向它自己时,循环结束
    {
        if(count == m)
        {
            //销毁pcur节点,首先prev改变指向
            prev->next = pcur->next;
            free(pcur);//此时pcur是野指针
            pcur = prev->next;
            count = 1;
        }
        else
        {
            //此时不需要销毁节点(因为报数不是m,不需要销毁)
            prev = pcur;
            pcur = pcur->next;
            count++;
        }
    }
    //此时剩下的一个节点就是要返回的节点里的值
    return pcur->val;
}

         1、末尾return之前为了养成良好的代码习惯,可以先把pcur原来的值val存储下来,然后将pcur节点释放掉。

        2、创建环形链表时要返回链表的 ptail 尾节点。

        return 头节点还是尾节点,要看需要如何使用该链表。首先phead报数为1,然后phead的后一个节点指针 ptail (这个过程需要用到ptail)需要走到phead的前面。按照这个过程,则需要返回ptail。

六、力扣--02.04 面试题:分割链表

        题目描述:给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。你不需要 保留 每个分区中各节点的初始相对位置。

        注意这不是排序,而是分割链表,不需要保证 x 两边节点的相对位置

        思路1:在原链表上进行修改。若pcur节点的值小于x,往前走;若pcur节点的值大于或者等于x,尾插在原链表后面,删除旧节点。

        思路2:在带头新链表(newHead和newTail)上进行修改。若pcur节点的值小于x,节点头插在新链表中;若pcur节点的值大于或者等于x,将节点尾插在新链表中。

        思路3:创建两个新链表大链表(greaterHead和greaterTail)和小链表(lessHead和额lessTail)。 小于x的节点就尾插入小链表,大于等于x的节点就尾插入大链表中。然后将小链表的尾节点和大链表的首尾相连,合并小链表和大链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

typedef struct ListNode ListNode; 
struct ListNode* partition(struct ListNode* head, int x){
    if(head == NULL)
    {
        return head;
    }
    //创建两个带头链表:
    ListNode* lessHead,*lessTail;
    ListNode* greaterHead,*greaterTail;
    lessHead = lessTail = (ListNode*)malloc(sizeof(ListNode));
    greaterHead = greaterTail = (ListNode*)malloc(sizeof(ListNode)); 

    //遍历原链表,将原链表中的节点尾插到大小链表中
    ListNode * pcur = head;
    while(pcur)
    {
        if(pcur->val < x)
        {
            //尾插到小链表中
            lessTail->next = pcur;
            lessTail = lessTail->next;
        }
        else
        {
            greaterTail->next = pcur;
            greaterTail = greaterTail->next;
        }
        pcur = pcur->next;
    }

    //小链表的尾节点和大链表的第一个有效的节点首尾相连
    lessTail->next = greaterHead->next;
    greaterTail->next = NULL;
    //从小链表的第一个有效节点开始返回,返回之前动态申请的空间还需要释放掉
    ListNode* ret = greaterHead = NULL;
    free(lessHead);
    free(greaterHead);
    lessHead = greaterHead = NULL;
    return ret; 
}

创作不易,喜欢的uu支持一下哦! 

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

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

相关文章

Unity SteamVR入门

概述 VR项目现在在当前已经是非常热门的技术&#xff0c;可以给玩家身临其境的感觉&#xff0c;接下来让我们学习这部分的内容吧&#xff01; SteamVR Input SteamVR绑定流程&#xff0c;在Windows窗口的点击SteamVR-input&#xff0c;图1&#xff0c;在这里可以选择你需要绑定…

Photoshop前言

Photoshop前言 分辨率图像格式工具界面组件 分辨率 分辨率是指单位长度内包含的像素点的数量&#xff0c;其单位通常为像素/英寸&#xff08;ppi&#xff09;&#xff0c;300ppi表示每英寸包含300个像素点。对于1英寸1英寸大小的图像&#xff0c;若分辨率为72ppi&#xff0c;则…

Unity MeshRenderer 入门

概述 在项目制作过程中&#xff0c;肯定缺少不了模型的使用&#xff0c;那就一定接触过MeshRenderer&#xff0c;也许还有你不理解的地方&#xff0c;接下来让我们来学习一下这部分的内容吧。 Mesh Filter&#xff08;网格过滤器&#xff09; Mesh:提供一个网格的参考&#xf…

# notepad++ 编辑器英文版,如何打开自动换行

notepad 编辑器英文版&#xff0c;如何打开自动换行 在Notepad中&#xff0c;如果你想要开启自动换行功能&#xff0c;可以按照以下步骤操作&#xff1a; 1、打开 Notepad 编辑器。 1.1. 依次点击菜单栏中的【视图】&#xff0c;英文版对应【View】。1.2. 在【视图】下拉菜单…

Integer中的缓存机制

先看一个示例&#xff1a; public static void main(String[] args) {Integer a127;Integer b127;System.out.println(ab);Integer c128;Integer d128;System.out.println(cd);} 输出&#xff1a; true false 为什么明明都是同一个数字进行比较&#xff0c;当数字等于127的…

串口单线半双工转换电路

用来把单线半双工模式的串口转换成双线&#xff0c;然后才能连接到普通的双线USB 串口模块&#xff0c;比如CH340 之类的。电路设计来自大佬的博客&#xff1a;AVR half-duplex software UART supporting single pin operation。他在Arduino 上用软件模拟串口功能&#xff0c;利…

【火猫DOTA2】电竞世界杯DOTA2项目将在7月份的前三周举办

1、电竞世界杯将于今年7月3日至8月25日在沙特利雅得举办。近日主办方公布了各个项目的举办时间,其中DOTA2项目将在7月份的前三周举办。转载:火猫TV资讯https://www.huomaotv.com/ 目前Falcons、XG、GG和Liquid这五支赢得了足够EPT积分的队伍已经确定直邀沙特。剩下的三个名额还…

nginx的前世今生(二)

书接上回&#xff1a; 上回书说到&#xff0c;nginx的前世今生&#xff0c;这回我们继续说 3.缓冲秘籍&#xff0c;洪流控水 Nginx的缓冲区是其处理数据传输和提高性能的关键设计之一&#xff0c;主要用于暂存和管理进出的数据流&#xff0c;以应对不同组件间速度不匹配的问题…

图片高效批量美化,支持将图片进行旋转180度并生成浴室玻璃图片,让图片瞬间焕然一新!

图片已成为我们生活与工作中不可或缺的一部分。然而&#xff0c;一张张手动美化图片却是一项既耗时又耗力的任务。为了帮助您轻松应对这一挑战&#xff0c;我们推出了一款高效批量美化图片的工具&#xff0c;并支持将图片旋转180度及生成浴室玻璃效果&#xff0c;让您的图片瞬间…

安装英伟达nvidia p4计算卡驱动@FreeBSD14

FreeBSD也能跑cuda AI训练拉&#xff01; 在FreeBSD安装好pytorch和飞桨cpu版本后&#xff0c;尝试安装英伟达nvidia p4计算卡驱动。毕竟全靠cpu速度太慢了&#xff0c;还是GPU快啊&#xff01;在磕磕绊绊几天后&#xff0c;终于成功成功安装好nvidia p4的cuda驱动&#xff0c…

拆单算法交易(Algorithmic Trading)

TWAP TWAP交易时间加权平均价格Time Weighted Average Price 模型&#xff0c;是把一个母单的数量平均地分配到一个交易时段上。该模型将交易时间进行均匀分割&#xff0c;并在每个分割节点上将拆分的订单进行提交。例如&#xff0c;可以将某个交易日的交易时间平均分为N 段&am…

密码学基础练习五道 RSA、elgamal、elgamal数字签名、DSA数字签名、有限域(GF)上的四则运算

1.RSA #include <stdlib.h>#include <stdio.h>#include <string.h>#include <math.h>#include <time.h>#define PRIME_MAX 200 //生成素数范围#define EXPONENT_MAX 200 //生成指数e范围#define Element_Max 127 //加密单元的…

26版SPSS操作教程(高级教程第十八章)

目录 前言 粉丝及官方意见说明 第十八章一些学习笔记 第十八章一些操作方法 经典判别分析 数据假设 具体操作 结果解释 判别结果的图形化展示 结果解释 判别效果验证 结果解释 适用条件的判断 结果解释 贝叶斯判别分析 具体操作 结果解释 逐步判别法 结束语…

Redis---------实现查询缓存业务

目录 数据库与缓存之间的工作业务逻辑&#xff1a; 接下来看查询缓存代码实现&#xff0c;主要是捋清楚业务逻辑&#xff0c;代码实现是死的&#xff1a; Controller: Service: P37作业实现&#xff1a;总体逻辑跟上面的业务逻辑差不多 Controller&#xff1a; Service&#…

【项目构建】04:动态库与静态库制作

OVERVIEW 1.编译动态链接库&#xff08;1&#xff09;编译动态库&#xff08;2&#xff09;链接动态库&#xff08;3&#xff09;运行时使用动态库 2.编译静态链接库&#xff08;1&#xff09;编译静态库&#xff08;2&#xff09;链接静态库&#xff08;3&#xff09;运行时使…

数字身份管理:Facebook如何利用区块链技术?

随着数字化进程的加速&#xff0c;个人身份管理已成为一个关键议题。在这方面&#xff0c;区块链技术正在逐渐展现其巨大潜力。作为全球最大的社交媒体平台&#xff0c;Facebook也在积极探索和应用区块链技术来改进其数字身份管理系统。本文将深入探讨Facebook如何利用区块链技…

【Docker学习】docker start深入研究

docker start也是很简单的命令。但因为有了几个选项&#xff0c;又变得复杂&#xff0c;而且... 命令&#xff1a; docker container start 描述&#xff1a; 启动一个或多个已停止的容器。 用法&#xff1a; docker container start [OPTIONS] CONTAINER [CONTAINER...] 别名&…

【软件工程】需求分析

目录 前言需求分析UML概述用例图用例图的组成用例图中的符号和含义包含的两种使用场景 用例图补充&#xff1a;“系统”用例模型建模确定系统参与者确定系统用例 用例文档用例文档组成部分 活动图组成元素初始节点和终点活动节点转换决策与分支、合并分岔与汇合 类图类的表示类…

【DevOps】怎么找合适的Docker镜像?

目录 一、Docker Hub介绍 主要特点和功能 使用场景 二、怎么找合适的镜像 步骤 1: 访问 Docker Hub 步骤 2: 使用搜索功能 步骤 3: 分析搜索结果 步骤 4: 阅读详细描述 步骤 5: 下载并使用镜像 例子 三、怎么样使用国内镜像加速 常用的国内 Docker 镜像加速器地址 …

2023下半年软件设计师上午题——冒泡排序

快速排除法&#xff0c;根据冒泡排序特性&#xff0c;每一趟排序都会确实最大/最小值&#xff0c;故升序两趟后&#xff0c;最后两个元素应该是已经排序好的第二大&#xff0c;和最大的元素&#xff0c;所以排除B,D&#xff0c;再因为每次排序都会两两交换&#xff0c;所以排除…