数据结构—单链表

1、链表的概念及结构

1.1链表的概念

链表是一种物理存储结构上非连续、非顺序的存储结构,但在逻辑上确是连续、顺序的,而数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

1.2链表的结构

如下图:

逻辑上的链表,pList是指向头个元素的指针 

物理上的链表

有人可能会有疑问,不是说链表只是在逻辑结构上是连续的,在物理存储结构上是不连续的,那为什么上图中一个个结点明明是挨在一起的,那么它在物理存储结构上肯定是连续的呀,其实不然,上图是为了方便大家理解,才用线条连接了结点,实际上在内存中,每个结点可能会隔得很远,仔细观察每个结点上面的红色文字,那就是这个结点的地址,而蓝色文字是下一个结点的地址,很明显能看到这两个结点并不是相邻的,因此也验证了顺序表在逻辑结构上确实是连续的,但在物理存储结构上确实是不连续的。

2、链表的实现

2.1 定义结点

介绍一下单链表的英文名——single linked list,我们简写成SL(区别于顺序表的SeqList或者SQL)。

注意:next指针的类型是SListNode*,千万不要写成SLTDataType*,因为next指针是结构体指针,是用来指向下一个结点的

typedef int SLTDataType;
typedef struct SListNode
{
    SLTDataType data;
    struct SListNode* next;
}SLTNode;

2.2链表的功能

链表要实现那些功能呢?其实这些功能我们都很熟悉,数据结构无非是对数据进行管理,要实现数据的增删查改,因此链表的基本功能也都是围绕着数据的增删查改展开。

注意:链表是不需要初始化的

//打印单链表
void SLTPrint(SLTNode* phead);
//创建一个结点
SLTNode* BuyLTNode(SLTDataType x);
//销毁单链表
void SLTDestroy(SLTNode** pphead);
//头插
void SLPushFront(SLTNode** pphead, SLTDataType x);
//尾插
void SLPushBack(SLTNode** pphead, SLTDataType x);
//头删
void SLPopFront(SLTNode** pphead);
//尾删
void SLPopBack(SLTNode** pphead);
// 单链表查找
SLTNode* STFind(SLTNode* phead, SLTDataType x);

// 在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//在pos之后插入
void SLInsertAfter(SLTNode* pos, SLTDataType x);

// 删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos);

// 删除pos位置后面的值
void SLEraseAfter(SLTNode* pos);


// 单链表结点修改
void SLTModify(SLTNode* phead, SLTNode* pos, SLTDataType x);

2.3链表功能实现

2.3.1打印单链表

注意:链表和顺序不同的是,顺序表传过来的指针是肯定不会为空的,而链表传过来的指针是可能为空的,比如说当链表中没有元素时,头指针所指向的就是NULL,如果在第一行写上断言就会有问题。

当cur指向空的时候就可以停止打印了。

void SLTPrint(SLTNode* phead)
{
    SLTNode* cur=phead;
    while(cur!=NULL)
    {
        printf("%d->",cur->data);
        cur=cur->next;
    }
    printf("NULL\n");

}

2.3.2 创建一个新结点

后面我们要在单链表中进行头插和尾插,此时插入的不再是像顺序表一样简单的SLDataType数据了,而是一个结点,这个结点是包括SLTDataType数据以及SLTDataType*的指针,因此,为了方便和减少代码的重复度,我们另写一个函数用来专门创建新结点

SLTNode* BuyLTNode(SLTDataType x)
{
    SLTNode * newnode=(SLTNode*)malloc(sizeof(SLTNode));
    if(newnode==NULL)
    {
        perror("malloc fail");
        return NULL;
    }
    newnode->data=x;
    newnode->next=NULL;//初始化

    return newnode;
}

2.3.3 单链表尾插

注意:在创建结点时,已经让 结点.next=NULL,所以不需要在插入完结点后,再让新结点的next指针为NULL。

有人可能会有疑问,为什么之前打印链表的时候不用断言指针,而在尾插时就要断言指针,以及为什么函数的形参是二级指针,而不使用一级指针。

因为,尾插分为两种情况(下面有写),当链表为空时,头指针phead指向NULL,尾插相当于头插,此时要改变phead的指向,让phead指向这个新结点,此时就需要二级指针来改变一级指针的值(如果我们用一级指针做形参,形参的改变不会影响实参,那么一级指针phead就不会被改变)。

至于这个什么时候要断言指针,什么时候不用断言指针:一级指针也就是phead,当链表为空的时候,phead就是为NULL,而二级指针永远指向phead,phead的地址是永远存在的,那么pphead就一定不可能为空,所以需要断言pphead。

//尾插
//第一个尾插需要二级指针,接下来就不用二级指针
//第一次是改变结构的指针plist
//第二次是改变结构体尾结点
void SLPushBack(SLTNode** pphead, SLTDataType x)
{
    SLTNode *newnode= BuyLTNode(x);
    //是否为空链表
    if(*pphead==NULL)
        *pphead=newnode;//改变结构的指针plist
    else
    {
        SLTNode *tail=*pphead;
        while(tail->next!=NULL)//找到链表最后一位,当tail->为空时,就把新开辟的链表节点接上去
            tail=tail->next;
        tail->next=newnode;//改变结构体尾结点
        //就是把newnode存放的新结点地址给tail->next,然后在存放在之前最后一个结点的struct SListNode* next;中,这样next指向的地址不是NULL,而是新加的结点的位置
        //不能用tail=newnode,因为tail和newnode是局部变量,当这函数结束后就释放了
    }
}

2.2.4 单链表尾删

要想删除链表中的元素,就必须保证原链表就有元素,因此要断言assert(*pphead)

尾删需要分情况去讨论

//尾删
void SLPopBack(SLTNode** pphead)
{
    //当没有结点(空链表)

    //暴力检查
    assert(*pphead);

    //温柔检查
//    if(*pphead==NULL)
//        return;
    //当只有一个结点时
    if((*pphead)->next==NULL)
    {
        free(*pphead);
        *pphead=NULL;
    }
    else
    {
        SLTNode *prev = NULL;//用来指向倒数第二个结点
        SLTNode *tail = *pphead;//用来释放最后一个指针空间
        //找尾
        while (tail->next) {
            prev = tail;
            tail = tail->next;
        }

        free(tail);//把最后一个指针空间释放掉
        prev->next = NULL;//把最后一个的前一个结点指针设为空
        //如果直接tail=NULL的话,倒数第二个结点就指向一个NULL,就成为了一个野指针

        //while(tail->next->next)//找到倒数第二个
        //    tail=tail->next;
        //free(tail->next);//把倒数第二个结点指向的结构体释放掉
        //tail->next=NULL;//把倒数第二个结点置为空
    }
}

2.2.5  单链表头删

头删没什么好说的,记住要断言*pphead,保证链表内容不为空。

//头删
void SLPopFront(SLTNode** pphead)
{
    //空链表
    assert(*pphead);

//    //一个结点
//    if((*pphead)->next==NULL)
//    {
//        free(*pphead);
//        *pphead=NULL;
//    }
//    //多个结点
//    else
//    {
//        SLTNode *del=*pphead;
//        //*pphead=del->nest;
//        *pphead=(*pphead)->next;
//        free(del);
//    }

    SLTNode* del = *pphead;
    *pphead = (*pphead)->next;
    free(del);

}

 2.2.6 单链表头插

现在是不是觉得头插很简单了啊!

//头插
void SLPushFront(SLTNode** pphead, SLTDataType x)
{
    SLTNode *newnode= BuyLTNode(x);

    newnode->next= *pphead;
    *pphead=newnode;//都需要二级指针
}

2.2.7 查找某个结点

这个函数返回值不再是void,而是SLTNode*,把找到的结点的地址返回去,这个函数一般会跟结点修改之类的函数一起使用。

SLTNode* STFind(SLTNode* phead, SLTDataType x)
{
    SLTNode *cur=phead;
    while (cur)
    {
        if(cur->data==x)
        {
            return cur;
        }

        cur=cur->next;
    }

    return NULL;
}

2.2.8 删除某个节点

// 删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos)
{
    assert(pphead);
    assert(pos);

    //如果只有一个结点
    if(pos==*pphead)
        SLPopFront(pphead);
    else
    {
        SLTNode *p1=*pphead;
        while(p1->next!=pos)
            p1=p1->next;
        p1->next=pos->next;
        free(pos);
    }
}

注意:

  1. pos也要断言,pos可不能为空呀!
  2. cur->next也要断言,因为当cur->next为NULL时,说明整个链表的结点都排查完了,最后还是没有找到地址为pos的结点,证明pos传值有误。

 2.2.9单链表结点修改

// 单链表结点修改
void SLTModify(SLTNode* phead, SLTNode* pos, SLTDataType x)
{
    SLTNode* cur=phead;
    while(cur!=pos)
    {
        cur=cur->next;
        assert(cur);
    }
    pos->data=x;
}

2.2.10 单链表在pos位前插入结点

// 在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
    assert(pphead);
    assert(pos);

    //如果只有一个结点
    if(*pphead==pos)
        SLPushFront(pphead,x);
    else
    {
        SLTNode *p1=*pphead;
        while(p1->next!=pos)
        {
            p1=p1->next;
        }

        SLTNode *newnode= BuyLTNode(x);
        p1->next=newnode;
        newnode->next=pos;
    }
}

2.2.11单链表在pos位后插入结点

//在pos之后插入
void SLInsertAfter(SLTNode* pos, SLTDataType x)
{
    assert(pos);

    SLTNode *newnode= BuyLTNode(x);
    newnode->next=pos->next;
    pos->next=newnode;
}

2.2.12销毁链表

销毁链表这一块,咱可不敢直接free(phead),因为链表在物理结构上是不连续存储的,销毁链表必须要一个结点一个结点去销毁!!!!最后不要忘记把phead置为NULL。

//单链表销毁
void SListDesTroy(SLTNode** pphead)
{
    assert(pphead && *pphead );

    SLTNode *pcur=*pphead;
    while(pcur)
    {
        SLTNode *next=pcur->next;
        free(pcur);
        pcur=next;
    }
    *pphead=NULL;

}

3、总代码

3.1 SList.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int SLTDataType;
typedef struct SListNode
{
    SLTDataType data;
    struct SListNode* next;
}SLTNode;

void SLTPrint(SLTNode* phead);
//头插
void SLPushFront(SLTNode** pphead, SLTDataType x);
//尾插
void SLPushBack(SLTNode** pphead, SLTDataType x);
//头删
void SLPopFront(SLTNode** pphead);
//尾删
void SLPopBack(SLTNode** pphead);
// 单链表查找
SLTNode* STFind(SLTNode* phead, SLTDataType x);

// 在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//在pos之后插入
void SLInsertAfter(SLTNode* pos, SLTDataType x);

// 删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos);

// 删除pos位置后面的值
void SLEraseAfter(SLTNode* pos);

//单链表销毁
void SListDesTroy(SLTNode** pphead);


// 单链表结点修改
void SLTModify(SLTNode* phead, SLTNode* pos, SLTDataType x);

3.2 SList.c

#include "SList.h"

void SLTPrint(SLTNode* phead)
{
    SLTNode* cur=phead;
    while(cur!=NULL)
    {
        printf("%d->",cur->data);
        cur=cur->next;
    }
    printf("NULL\n");

}

//创建一个新的动态内存
SLTNode* BuyLTNode(SLTDataType x)
{
    SLTNode * newnode=(SLTNode*)malloc(sizeof(SLTNode));
    if(newnode==NULL)
    {
        perror("malloc fail");
        return NULL;
    }
    newnode->data=x;
    newnode->next=NULL;//初始化

    return newnode;
}

//头插
void SLPushFront(SLTNode** pphead, SLTDataType x)
{
    SLTNode *newnode= BuyLTNode(x);

    newnode->next= *pphead;
    *pphead=newnode;//都需要二级指针
}


//尾插
//第一个尾插需要二级指针,接下来就不用二级指针
//第一次是改变结构的指针plist
//第二次是改变结构体尾结点
void SLPushBack(SLTNode** pphead, SLTDataType x)
{
    SLTNode *newnode= BuyLTNode(x);
    //是否为空链表
    if(*pphead==NULL)
        *pphead=newnode;//改变结构的指针plist
    else
    {
        SLTNode *tail=*pphead;
        while(tail->next!=NULL)//找到链表最后一位,当tail->为空时,就把新开辟的链表节点接上去
            tail=tail->next;
        tail->next=newnode;//改变结构体尾结点
        //就是把newnode存放的新结点地址给tail->next,然后在存放在之前最后一个结点的struct SListNode* next;中,这样next指向的地址不是NULL,而是新加的结点的位置
        //不能用tail=newnode,因为tail和newnode是局部变量,当这函数结束后就释放了
    }
}

//头删
void SLPopFront(SLTNode** pphead)
{
    //空链表
    assert(*pphead);

//    //一个结点
//    if((*pphead)->next==NULL)
//    {
//        free(*pphead);
//        *pphead=NULL;
//    }
//    //多个结点
//    else
//    {
//        SLTNode *del=*pphead;
//        //*pphead=del->nest;
//        *pphead=(*pphead)->next;
//        free(del);
//    }

    SLTNode* del = *pphead;
    *pphead = (*pphead)->next;
    free(del);

}

//尾删
void SLPopBack(SLTNode** pphead)
{
    //当没有结点(空链表)

    //暴力检查
    assert(*pphead);

    //温柔检查
//    if(*pphead==NULL)
//        return;
    //当只有一个结点时
    if((*pphead)->next==NULL)
    {
        free(*pphead);
        *pphead=NULL;
    }
    else
    {
        SLTNode *prev = NULL;//用来指向倒数第二个结点
        SLTNode *tail = *pphead;//用来释放最后一个指针空间
        //找尾
        while (tail->next) {
            prev = tail;
            tail = tail->next;
        }

        free(tail);//把最后一个指针空间释放掉
        prev->next = NULL;//把最后一个的前一个结点指针设为空
        //如果直接tail=NULL的话,倒数第二个结点就指向一个NULL,就成为了一个野指针

        //while(tail->next->next)//找到倒数第二个
        //    tail=tail->next;
        //free(tail->next);//把倒数第二个结点指向的结构体释放掉
        //tail->next=NULL;//把倒数第二个结点置为空
    }
}

// 单链表查找
SLTNode* STFind(SLTNode* phead, SLTDataType x)
{
    SLTNode *cur=phead;
    while (cur)
    {
        if(cur->data==x)
        {
            return cur;
        }

        cur=cur->next;
    }

    return NULL;
}

// 在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
    assert(pphead);
    assert(pos);

    //如果只有一个结点
    if(*pphead==pos)
        SLPushFront(pphead,x);
    else
    {
        SLTNode *p1=*pphead;
        while(p1->next!=pos)
        {
            p1=p1->next;
        }

        SLTNode *newnode= BuyLTNode(x);
        p1->next=newnode;
        newnode->next=pos;
    }
}

//在pos之后插入
void SLInsertAfter(SLTNode* pos, SLTDataType x)
{
    assert(pos);

    SLTNode *newnode= BuyLTNode(x);
    newnode->next=pos->next;
    pos->next=newnode;
}

// 删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos)
{
    assert(pphead);
    assert(pos);

    //如果只有一个结点
    if(pos==*pphead)
        SLPopFront(pphead);
    else
    {
        SLTNode *p1=*pphead;
        while(p1->next!=pos)
            p1=p1->next;
        p1->next=pos->next;
        free(pos);
    }
}

// 删除pos位置后面的值
void SLEraseAfter(SLTNode* pos)
{
    assert(pos);
    assert(pos->next);

    SLTNode *newnode=pos->next;
    pos->next=newnode->next;
    free(newnode);
}

//单链表销毁
void SListDesTroy(SLTNode** pphead)
{
    assert(pphead && *pphead );

    SLTNode *pcur=*pphead;
    while(pcur)
    {
        SLTNode *next=pcur->next;
        free(pcur);
        pcur=next;
    }
    *pphead=NULL;

}
// 单链表结点修改
void SLTModify(SLTNode* phead, SLTNode* pos, SLTDataType x)
{
    SLTNode* cur=phead;
    while(cur!=pos)
    {
        cur=cur->next;
        assert(cur);
    }
    pos->data=x;
}

3.3 text.c

测试函数可以根据大家的想法进行测试,下面是我的测试函数以及输出情况

#include"SList.h"

void TestSList1()
{
    SLTNode* plist = NULL;
    SLPushFront(&plist, 1);
    SLPushFront(&plist, 2);
    SLPushFront(&plist, 3);
    SLPushFront(&plist, 4);

    SLTPrint(plist);
}

void TestSList2()
{
    SLTNode *plist=NULL;
    SLPushFront(&plist, 1);
    SLPushFront(&plist, 2);
    SLPushFront(&plist, 3);
    SLPushFront(&plist, 4);
    SLPushBack(&plist, 5);
    SLPushBack(&plist, 6);
    SLPushBack(&plist, 7);
    SLPushBack(&plist, 8);
    SLTPrint(plist);

    SLTNode *pos= STFind(plist,3);
    if(pos)
    {
        SLInsert(&plist,pos,30);
    }
    SLTPrint(plist);

    pos= STFind(plist,6);
    if(pos)
    {
        SLInsertAfter(plist,88);
    }
    SLTPrint(plist);

    pos= STFind(plist,7);
    if(pos)
    {
        SLErase(&plist,pos);
    }
    SLTPrint(plist);

    pos= STFind(plist,1);
    if(pos)
    {
        SLEraseAfter(pos);
    }
    SLTPrint(plist);
    SListDesTroy(&plist);
}


void Swapi(int* p1, int* p2)
{
    int tmp = *p1;
    *p1 = *p2;
    *p2 = tmp;
}

void Swappi(int** pp1, int** pp2)
{
    int* tmp = *pp1;
    *pp1 = *pp2;
    *pp2 = tmp;
}

int main()
{
//    TestSList1();

    TestSList2();
//    int a = 0, b = 1;
//    Swapi(&a, &b);
//
//    int* px = &a, * py = &b;
//    Swappi(&px, &py);

    return 0;
}

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

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

相关文章

AOP基础

一、AOP概述 AOP&#xff1a;Aspect Oriented Programming&#xff08;面向切面编程、面向方面编程&#xff09;&#xff0c;其实就是面向特定方法编程。 使用场景&#xff1a;①记录操作日志&#xff1b;②权限控制&#xff1b;③事务管理等。 优势&#xff1a;①代码无侵入…

【GPTs分享】GPTs分享之 AskYourPDF Research Assistant​

一、简介 AskYourPDF Research Assistant 是一款高级人工智能研究助手&#xff0c;专门设计用于帮助用户高效从PDF文件和文章中提取信息。它结合了深度学习技术和自然语言处理能力&#xff0c;以便用户能够快速地查询、总结及处理文档内容&#xff0c;并能够生成与文章内容相关…

依泉LXSY LXLY电子远传水表说明书MODBUS-RTU通讯协议

这款水表的通讯协议主要包括以下内容&#xff1a; 概述 传输方式 数据帧格式 地址码 功能码 数据区 CRC校验码 生成一个CRC的流程 通讯应用格式详解 寄存器地址 通讯接口 表盘地址 这是厂家给的通讯协议&#xff0c;我亲测可以读取到水表的读数&#xff0c;精度只…

怎么理解load_average

之前我讲了cpu使用率的问题&#xff0c;cpu使用率是我们监控中非常关注的指标。 但是工作中&#xff0c;我们经常遇到业务应用已经很慢了&#xff0c;但是cpu利用率显示很低。 这种时候&#xff0c;你会发现top中load很高。 在top中&#xff0c;load average后面有3个数字。…

AtCoder ABC349 A-D题解

比赛链接:ABC349 Problem A: 签到。 #include <bits/stdc.h> using namespace std; const int maxn105; int A[maxn]; int main(){int N;cin>>N;int ans0;for(int i1;i<N;i){cin>>A[i];ans-A[i];}return 0; } Problem B: 开2个桶即可&#xff0c;具体…

西夏区第三届中华诗词大会活动方案

活动流程/比赛规则 1.【13:30-14:10】 参赛选手签到&#xff1b;领取参赛号码牌&#xff1b;分组抽签&#xff1b;拍摄赛前感言&#xff0c;集体祝福口号&#xff1b; 2.【14:10-14:25】 熟悉设备、答题环节、题目设置等&#xff0c;走台演练 3.【14:25-14:30】 播放暖场视频…

java音乐播放器系统设计与实现springboot-vue

后端技术 SpinrgBoot的主要优点有&#xff1a; 1、为所有spring开发提供了一个更快、更广泛的入门体验&#xff1b; 2、零配置&#xff1b; 3、集成了大量常用的第三方库的配置&#xff1b; Maven: 项目管理和构建自动化工具&#xff0c;用于java项目。 java: 广泛使用的编程语…

导航菜单 父级高亮 处理

// 预处理导航数据filterMenuFun (arr, uuids []) {// uuids 用来有级嵌套导航时&#xff0c;子集acitve时父级也有acitve样式arr.forEach(item > {item.uuids [item.uuid, ...uuids];item.children && this.filterMenuFun(item.children, item.uuids)});return a…

【触想智能】如何选购到一款合适的工业电脑一体机

工业电脑一体机是专为工业环境而设计的一种工业计算机。工业电脑一体机和普通的计算机不一样&#xff0c;它对产品的参数性能要求很高&#xff0c;因为它们通常会运行在高低温、电磁干扰、高粉尘、湿度大的恶劣环境中&#xff0c;所以相应的要求工业电脑一体机必须具备良好的宽…

openplc Linux 使用modbus RTU 从机通讯

1.Linux 环境下&#xff0c;openplc 默认使用的是modbus tcp协议通信。 想要使用串口 modbus rtu 通讯可以通过在runtime中添加SlaveDevices从机设备 2.添加设备&#xff0c;分配地址。 左边添加串口配置&#xff0c;右边是需要通讯的地址&#xff0c;从机地址都是从100开始&am…

【Linux】创建IDEA桌面快捷方式

Linux系统安装IDEA保姆级教程_linux安装idea-CSDN博客 在Ubuntu上安装Intellij IDEA并创建桌面快捷方式 - 极客子羽 - 博客园 (cnblogs.com) 下载安装包解压到指定目录 /opt/softWare 进入bin目录&#xff0c;ll查看 桌面打开终端&#xff0c;创建文件 touch idea.desktop s…

C语言C/S架构PACS影像归档和通信系统源码 医院PACS系统源码

C语言C/&#xff33;架构PACS影像归档和通信系统源码 医院PACS系统源码 医院影像科PACS系统&#xff0c;意为影像归档和通信系统。它是应用在医院影像科室的系统&#xff0c;主要的任务是把日常产生的各种医学影像&#xff08;包括核磁、CT、超声、各种X光机、各种红外仪、显微…

《游戏系统设计十二》灵活且简单的条件检查系统

目录 1、序言 2、需求 3、实现 3.1 思路 3.2 代码实现 4、总结 1、序言 每个游戏都有一些检查性的任务&#xff0c;在做一些判断的时候&#xff0c;判断等级是不是满足需求。 比如如下场景&#xff1a;在进入副本的时候需要检查玩家等级是否满足&#xff0c;满足之后才…

ELK+Kafka+Zookeeper日志收集系统

环境准备 节点IP节点规划主机名192.168.112.3Elasticsearch Kibana Logstash Zookeeper Kafka Nginxelk-node1192.168.112.3Elasticsearch Logstash Zookeeper Kafkaelk-node2192.168.112.3Elasticsearch Logstash Zookeeper Kafka Nginxelk-node3 基础环境 sys…

【错题集-编程题】腐烂的苹果(多源 BFS + 最短路)

题目链接&#xff1a;腐烂的苹果_牛客题霸_牛客网 (nowcoder.com) 一、分析题目 多源 BFS 问题&#xff0c;加一点最短路的思想&#xff0c;固定套路。 二、代码 //看了题解之后AC的代码 class Solution { private:int n, m;bool vis[1010][1010];int dx[4]{-1,0,1,0}, dy[4]{…

ceph介绍

一、前言 Ceph 是一个完全分布式的系统&#xff0c;它将数据分布在整个集群中的多个节点上&#xff0c;以实现高可用性和容错性&#xff0c;ceph支持对象存储、块存储、文件存储所以被称为统一存储&#xff0c;ceph的架构由以下组件组成:mon、mgr、osd、mds、cephfs、rgw&#…

域名信息查询同款WHOIS源码

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 域名查询一般是指查询域名的whois注册信息&#xff0c;域名WHOIS是当前域名系统中不可或缺的一项信息服务。在使用域名进行Internet冲浪时&#xff0c;很多用户希望进一步了解域名、名…

Kotlin语法入门--变量声明(2)

Kotlin语法入门–条件控制和循环语句&#xff08;2&#xff09; 文章目录 Kotlin语法入门--条件控制和循环语句&#xff08;2&#xff09;二、条件控制和循环语句1、if...else2、when2.1、常规用法2.2、特殊用法--并列&#xff1a;2.3、特殊用法--类型判断&#xff1a;2.4、特殊…

【笔试训练】day6

1.大数加法 思路&#xff1a; 高精度板子&#xff0c;停留一下都是罪过&#xff01; 代码&#xff1a; class Solution { public:string solve(string s, string t) {vector<int> a;vector<int> b;for(int is.size()-1;i>0;i--)a.push_back(s[i]-0);for(int …

AOP

代理模式 提出问题 现有缺陷 假设我们有一个计算类&#xff0c;里面有加减乘除四个方法&#xff0c;现在我们要为这四个方法添加日志&#xff0c;即在方法执行的前后分别输出一句话&#xff0c;这时我们会发现如下缺陷&#xff1a; 1.对核心业务有干扰。核心业务是加减乘除…