2.1 线性表的逻辑结构与存储结构

news2024/11/24 11:34:55

        在之前的数据结构知识铺垫2:物理结构与逻辑结构一文中, 我们介绍了物理结构与逻辑结构, 物理结构即存储结构. 本篇文章我们着重探讨一下线性表的逻辑结构与存储结构. 

1. 线性表的逻辑结构

        

 图1. 线性表的逻辑结构

        线性表是具有相同特性的数据元素的有限序列, 每个元素至多有一个前驱和一个后继. 

2. 线性表的存储结构

        说到存储结构, 我们就要想到计算机中的内存的特点, 数据元素存储在计算机的内存中, 就只有连续存储非连续存储两种方式. 线性表的存储结构也可以按照这两种方式分类. 

1. 线性表的顺序存储结构

        数组是典型的顺序存储结构的线性表. 

#include <iostream>

const int MAX_SIZE = 10;

int main()
{
    int number[MAX_SIZE];       //为数组开辟的存储空间的大小为MAX_SIZE
    int length = 6;             //length为数组中的元素个数, length <= MAX_SIZE
    for (int i = 0; i < length; i++)
    {
        number[i] = i;
        printf("%d\n", number[i]);
    }
}

代码1: 线性表的顺序存储

2. 线性表的链式存储结构

1. 单链表

        这里我们引入一个概念: 头结点. 头结点是不含任何数据信息的结点. 因此单链表又可以分为没有头结点的单链表和有头结点的单链表. 

        我们先回顾一下如何定义一个结构体: 

typedef struct LNode {
    int data;
    struct LNode *next;
}LNode;

代码2: 定义一个结构体

        再来看一下这段代码: 

#include <iostream>

typedef struct LNode {
    int data;
    struct LNode* next;
}LNode;

int main()
{
    LNode* A;
    LNode B;
}

代码3: A是指针类型, B是LNode类型的变量

        A和B有什么区别? (以下内容来自Chat GPT)

        1. A是一个指针类型,指向LNode结构体的地址,而B是一个LNode类型的变量。

        2. A需要通过动态内存分配来进行创建和初始化,而B是一个静态变量,不需要额外的内存分配。

        3. A可以通过操作指针来访问和修改LNode结构体中的数据和指针,而B直接访问和修改LNode结构体中的数据和指针。

        4. A可以指向一个链表的第一个节点,从而对整个链表进行操作,而B只能表示一个节点,不能表示整个链表。

        两种定义方式哪种更好?(以下内容来自Chat GPT)

        哪种方式更好取决于具体的需求和场景。

        如果需要创建一个链表,进行动态的插入、删除操作,并且需要在不同的函数之间传递链表的头指针,那么使用指针类型的变量(如A)更合适,因为它可以通过指针操作来方便地修改链表的指针和数据,同时在函数之间传递链表的头指针也更方便。

        如果只需要创建一个简单的节点,不需要进行复杂的链表操作,并且节点的生命周期在一个函数内,不需要在函数之间传递,那么使用普通的结构体变量(如B)更简洁和方便,不需要进行动态内存分配和释放。

        综上所述,根据具体的需求和场景来选择合适的方式更好。

        LNode* A; 与LNode *A; 有什么区别?(以下内容来自Chat GPT)

        在C语言中,LNode* A; 和LNode *A;是等效的,都表示定义了一个指针变量A,指向LNode类型的对象。

        C语言中的指针声明可以采用两种方式,即“指针型修饰符在前”和“指针型修饰符在后”的写法,它们的区别仅仅是语法风格上的差异。

        在LNode* A;中,指针型修饰符*位于类型名LNode之后,这种写法更符合C++中的风格习惯。

        而在LNode *A;中,指针型修饰符*位于变量名A之前,这种写法更符合C语言中的传统写法。

        总之,LNode* A;和LNode *A;都可以正确地定义一个指向LNode类型的指针变量A,具体使用哪种写法取决于个人的编程风格和习惯。

        由于我们后面的学习中会大量地使用对链表的插入删除等操作, 因此我们以后均采用LNode *A;这样的方式. 仅仅是定义结点还不够, 我们还需要给结点分配内存空间. 看下面的代码: 

#include <iostream>

typedef struct LNode {
    int data;
    struct LNode* next;
}LNode;

int main()
{
    LNode *A;
    A = (LNode*)malloc(sizeof(LNode));
    LNode* B;
    B = new LNode;
}

代码4: 分配内存空间的两种方式(前者为C, 后者为C++)

        A = (LNode*)malloc(sizeof(LNode));什么意思? (以下内容来自Chat GPT)

        A = (LNode*)malloc(sizeof(LNode)); 的意思是动态地分配了一块内存空间,大小为LNode所占的字节数,并将其起始地址赋值给指针变量A。

具体解释如下: 

        sizeof(LNode)用来获取结构体LNode所占的字节数。sizeof是C语言中的运算符,用于获取变量、类型或表达式所占用的字节数。

        malloc函数用于在堆内存中分配一块指定大小的内存空间。它的参数是所需分配的内存大小(以字节为单位)。在这里,我们分配了sizeof(LNode)个字节的内存空间。

        (LNode*)是类型转换操作符,将malloc返回的通用指针(void*类型)强制转换为LNode类型的指针。因为malloc返回的是通用指针,需要进行类型转换才能将其赋值给LNode指针。

        A是指针变量,它存储了malloc分配的内存空间的起始地址。通过将malloc的返回值赋给A,可以使A指向所分配的内存空间。

        总之,这行代码的作用是动态地分配了一块内存空间,用于存储一个LNode类型的对象,并将其起始地址赋值给指针变量A。这样,通过A可以访问到这块内存空间,并对其中的数据进行操作。

        需要注意的是,这段代码使用了C语言的malloc函数,而不是C++的new操作符。因此,为了正确释放分配的内存空间,应该在适当的时候使用free(A)来释放A所指向的内存空间free(A)new。

       A = (LNode*)malloc(sizeof(LNode));和A = new LNode;哪种方式更好?(以下内容来自Chat GPT)

         A = (LNode*)malloc(sizeof(LNode)); 和 A = new LNode; 这两种方式都可以用来分配内存空间,不同之处在于它们的使用方式和特点。

        malloc函数是C语言标准库中的函数,而new操作符是C++的运算符。

        malloc函数分配的是一块原始的内存空间,需要通过类型转换将其转换为所需的类型。
        new操作符分配的是一个完整的对象,返回的是指向该对象的指针,不需要进行类型转换。
        malloc函数需要手动指定所需的内存空间大小,而new操作符会根据对象的类型自动计算所需的内存空间大小。

        malloc函数分配的内存空间需要使用free函数手动释放,而new操作符分配的内存空间会在对象被销毁时自动释放。

        综上所述,A = new LNode; 更符合C++的风格和习惯,更安全可靠,因为它能够自动计算所需的内存空间大小,并在对象销毁时自动释放内存。因此,在C++中,使用new操作符来分配内存空间是更好的选择。

        因此我们使用new操作符来分配内存空间.

        先看没有头结点的单链表. 

 没有头结点的单链表

        图中的Head即为代码的指针p, 并非头结点. 

#include <iostream>

typedef struct LNode {
    int data;
    struct LNode* next;
}LNode;

/// <summary>
/// 遍历输出链表各个结点的值
/// </summary>
/// <param name="firstNode"></param>
void traverseLinkedList(LNode *p) {
    while (p != NULL) {
        // 对当前节点进行操作,打印节点的数据
        printf("%d\n", p->data);

        // 移动到下一个节点
        p = p->next;
    }
}

/// <summary>
/// 判断链表是否非空
/// </summary>
/// <param name="p"></param>
bool linkedListIsNotNull(LNode *p) {
    if (p == NULL) {
        return false;
    }
    return true;
}

int main()
{
    LNode *A;
    A = new LNode;
    LNode *B;
    B = new LNode;
    LNode *C;
    C = new LNode;

    A->data = 10;
    B->data = 20;
    C->data = 30;

    A->next = B;
    B->next = C;
    C->next = NULL;

    LNode *p = A;        //p即为图中的Head
    bool isNotNull = linkedListIsNotNull(p);
    if (isNotNull) {
        traverseLinkedList(p);
    }
    else {
        printf("链表为空!");
    }
}


代码5: 没有头结点的单链表

没有头结点的单链表的判空条件: LNode *p = A; 若p == NULL; 则链表为空. 

       


        再看有头结点的单链表. 

 有头结点的单链表

        图中的Head即为代码的指针p, 并非头结点, Head指向的结点才是头结点. 

#include <iostream>

typedef struct LNode {
    int data;
    struct LNode* next;
}LNode;

/// <summary>
/// 遍历输出链表各个结点的值
/// </summary>
/// <param name="firstNode"></param>
void traverseLinkedList(LNode *p) {
    p = p->next;
    while (p != NULL) {
        // 对当前节点进行操作,打印节点的数据
        printf("%d\n", p->data);

        // 移动到下一个节点
        p = p->next;
    }
}

/// <summary>
/// 判断链表是否非空
/// </summary>
/// <param name="p"></param>
bool linkedListIsNotNull(LNode *p) {
    if (p->next == NULL) {
        return false;
    }
    return true;
}

int main()
{
    LNode* H;
    H = new LNode;
    LNode *A;
    A = new LNode;
    LNode *B;
    B = new LNode;
    LNode *C;
    C = new LNode;

    H->data = NULL;
    A->data = 10;
    B->data = 20;
    C->data = 30;

    H->next = A;
    A->next = B;
    B->next = C;
    C->next = NULL;

    LNode *p = H;        //p即为图中的Head
    bool isNotNull = linkedListIsNotNull(p);
    if (isNotNull) {
        traverseLinkedList(p);
    }
    else {
        printf("链表为空!");
    }
}


代码6: 有头结点的单链表

有头结点的单链表的判空条件: LNode *p = H; 若p->next == NULL; 则链表为空. 

2. 双链表

        先看没有头结点的双链表: 

 没有头结点的双链表

        图中的Head即为代码的指针p, 并非头结点. 

#include <iostream>

typedef struct LNode {
    int data;
    struct LNode* next;
    struct LNode* prior;
}LNode;

/// <summary>
/// 遍历输出链表各个结点的值
/// </summary>
/// <param name="firstNode"></param>
void traverseLinkedList(LNode *p) {
    while (p != NULL) {
        // 对当前节点进行操作,打印节点的数据
        printf("%d\n", p->data);

        // 移动到下一个节点
        p = p->next;
    }
}

/// <summary>
/// 判断链表是否非空
/// </summary>
/// <param name="p"></param>
bool linkedListIsNotNull(LNode *p) {
    if (p == NULL) {
        return false;
    }
    return true;
}

int main()
{
    LNode *A;
    A = new LNode;
    LNode *B;
    B = new LNode;
    LNode *C;
    C = new LNode;

    A->data = 10;
    B->data = 20;
    C->data = 30;

    A->next = B;
    B->next = C;
    C->next = NULL;

    A->prior = NULL;
    B->prior = A;
    C->prior = B;

    LNode *p = A;        //p即为图中的Head
    bool isNotNull = linkedListIsNotNull(p);
    if (isNotNull) {
        traverseLinkedList(p);
    }
    else {
        printf("链表为空!");
    }
}


代码7: 没有头结点的双链表

没有头结点的双链表的判空条件: LNode *p = A; 若p == NULL; 则链表为空. 

       


        再看有头结点的双链表: 

 有头结点的双链表

        图中的Head即为代码的指针p, 并非头结点, Head指向的结点才是头结点. 

#include <iostream>

typedef struct LNode {
    int data;
    struct LNode* next;
    struct LNode* prior;
}LNode;

/// <summary>
/// 遍历输出链表各个结点的值
/// </summary>
/// <param name="firstNode"></param>
void traverseLinkedList(LNode *p) {
    p = p->next;
    while (p != NULL) {
        // 对当前节点进行操作,打印节点的数据
        printf("%d\n", p->data);

        // 移动到下一个节点
        p = p->next;
    }
}

/// <summary>
/// 判断链表是否非空
/// </summary>
/// <param name="p"></param>
bool linkedListIsNotNull(LNode *p) {
    if (p->next == NULL) {
        return false;
    }
    return true;
}

int main()
{
    LNode* H;
    H = new LNode;
    LNode *A;
    A = new LNode;
    LNode *B;
    B = new LNode;
    LNode *C;
    C = new LNode;

    H->data = NULL;
    A->data = 10;
    B->data = 20;
    C->data = 30;

    H->next = A;
    A->next = B;
    B->next = C;
    C->next = NULL;

    H->prior = NULL;
    A->prior = H;
    B->prior = A;
    C->prior = B;

    LNode *p = H;        //p即为图中的Head
    bool isNotNull = linkedListIsNotNull(p);
    if (isNotNull) {
        traverseLinkedList(p);
    }
    else {
        printf("链表为空!");
    }
}


代码8: 有头结点的双链表

        有头结点的双链表的判空条件: LNode *p = H; 若p->next == NULL; 则链表为空. 

3. 循环链表

1. 单循环链表

        先看没有头结点的单循环链表: 

 没有头结点的单循环链表

        图中的Head即为代码的指针p, 并非头结点. 

#include <iostream>

typedef struct LNode {
    int data;
    struct LNode* next;
}LNode;

/// <summary>
/// 遍历输出链表各个结点的值
/// </summary>
/// <param name="firstNode"></param>
void traverseLinkedList(LNode *p) {
    int *address = &p->data;        //记录首个结点的数据的存放地址, 注意这里要取的是&p->data而不是&p, 因为p只是一个指针, 它的内存地址&p是不会改变的
    while (p != NULL) {
        // 对当前结点进行操作,打印结点的数据
        printf("%d\n", p->data);

        // 移动到下一个结点
        p = p->next;

        if (address == &p->data) {  //当p指向的数据的存放地址等于address时, 说明完成了一次遍历, 则跳出循环
            break;
        }
    }
}

/// <summary>
/// 判断链表是否非空
/// </summary>
/// <param name="p"></param>
bool linkedListIsNotNull(LNode *p) {
    if (p == NULL) {
        return false;
    }
    return true;
}

int main()
{
    LNode *A;
    A = new LNode;
    LNode *B;
    B = new LNode;
    LNode *C;
    C = new LNode;

    A->data = 10;
    B->data = 20;
    C->data = 30;

    A->next = B;
    B->next = C;
    C->next = A;

    LNode *p = A;        //p即为图中的Head
    bool isNotNull = linkedListIsNotNull(p);
    if (isNotNull) {
        traverseLinkedList(p);
    }
    else {
        printf("链表为空!");
    }
}


代码9: 没有头结点的单循环链表

        其中address记录首个结点的数据的存放地址. 注意这里address要取的是&p->data而不是&p, 因为p只是一个指针, 它的内存地址&p是不会改变的

没有头结点的单循环链表的判空条件: LNode *p = A; 若p == NULL; 则链表为空. 

       


        再看有头结点的单循环链表: 

 有头结点的单循环链表

        图中的Head即为代码的指针p, 并非头结点, Head指向的结点才是头结点. 

#include <iostream>

typedef struct LNode {
    int data;
    struct LNode* next;
}LNode;

/// <summary>
/// 遍历输出链表各个结点的值
/// </summary>
/// <param name="firstNode"></param>
void traverseLinkedList(LNode* p) {
    p = p->next;
    int* address = &p->data;        //记录首个结点的数据的存放地址, 注意这里要取的是&p->data而不是&p, 因为p只是一个指针, 它的内存地址&p是不会改变的
    while (p != NULL) {
        // 对当前结点进行操作,打印结点的数据
        printf("%d\n", p->data);

        // 移动到下一个结点
        p = p->next;

        if (address == &p->data) {  //当p指向的数据的存放地址等于address时, 说明完成了一次遍历, 则跳出循环
            break;
        }
    }
}

/// <summary>
/// 判断链表是否非空
/// </summary>
/// <param name="p"></param>
bool linkedListIsNotNull(LNode* p) {
    if (p->next == p) {
        return false;
    }
    return true;
}

int main()
{
    LNode* H;
    H = new LNode;

    LNode* A;
    A = new LNode;
    LNode* B;
    B = new LNode;
    LNode* C;
    C = new LNode;

    H->data = NULL;
    A->data = 10;
    B->data = 20;
    C->data = 30;

    H->next = A;
    A->next = B;
    B->next = C;
    C->next = H;

    LNode* p = H;        //p即为图中的Head
    bool isNotNull = linkedListIsNotNull(p);
    if (isNotNull) {
        traverseLinkedList(p);
    }
    else {
        printf("链表为空!");
    }
}


代码10: 有头结点的单循环链表        

有头结点的单循环链表的判空条件: LNode *p = H; 若p->next == p; 则链表为空

2. 双循环链表

        先看没有头结点的双循环链表: 

 没有头结点的双循环链表

        图中的Head即为代码的指针p, 并非头结点. 

#include <iostream>

typedef struct LNode {
    int data;
    struct LNode *next;
    struct LNode *prior;
}LNode;

/// <summary>
/// 遍历输出链表各个结点的值
/// </summary>
/// <param name="firstNode"></param>
void traverseLinkedList(LNode* p) {
    int* address = &p->data;        //记录首个结点的数据的存放地址, 注意这里要取的是&p->data而不是&p, 因为p只是一个指针, 它的内存地址&p是不会改变的
    while (p != NULL) {
        // 对当前结点进行操作,打印结点的数据
        printf("%d\n", p->data);

        // 移动到下一个结点
        p = p->next;

        if (address == &p->data) {  //当p指向的数据的存放地址等于address时, 说明完成了一次遍历, 则跳出循环
            break;
        }
    }
}

/// <summary>
/// 判断链表是否非空
/// </summary>
/// <param name="p"></param>
bool linkedListIsNotNull(LNode* p) {
    if (p == NULL) {
        return false;
    }
    return true;
}

int main()
{
    LNode* A;
    A = new LNode;
    LNode* B;
    B = new LNode;
    LNode* C;
    C = new LNode;

    A->data = 10;
    B->data = 20;
    C->data = 30;

    A->next = B;
    B->next = C;
    C->next = A;

    A->prior = C;
    B->prior = A;
    C->prior = B;

    LNode* p = A;        //p即为图中的Head
    bool isNotNull = linkedListIsNotNull(p);
    if (isNotNull) {
        traverseLinkedList(p);
    }
    else {
        printf("链表为空!");
    }
}


代码11: 没有头结点的双循环链表

 没有头结点的双循环链表的判空条件: LNode *p = A; 若p == NULL; 则链表为空. 

       


        再看有头结点的双循环链表: 

 有头结点的双循环链表. 

        图中的Head即为代码的指针p, 并非头结点, Head指向的结点才是头结点. 

#include <iostream>

typedef struct LNode {
    int data;
    struct LNode *next;
    struct LNode *prior;
}LNode;

/// <summary>
/// 遍历输出链表各个结点的值
/// </summary>
/// <param name="firstNode"></param>
void traverseLinkedList(LNode* p) {
    p = p->next;
    int* address = &p->data;        //记录首个结点的数据的存放地址, 注意这里要取的是&p->data而不是&p, 因为p只是一个指针, 它的内存地址&p是不会改变的
    while (p != NULL) {
        // 对当前结点进行操作,打印结点的数据
        printf("%d\n", p->data);

        // 移动到下一个结点
        p = p->next;

        if (address == &p->data) {  //当p指向的数据的存放地址等于address时, 说明完成了一次遍历, 则跳出循环
            break;
        }
    }
}

/// <summary>
/// 判断链表是否非空
/// </summary>
/// <param name="p"></param>
bool linkedListIsNotNull(LNode* p) {
    if (p->next == p || p->prior == p) {
        return false;
    }
    return true;
}

int main()
{
    LNode* H;
    H = new LNode;
    LNode* A;
    A = new LNode;
    LNode* B;
    B = new LNode;
    LNode* C;
    C = new LNode;

    H->data = NULL;
    A->data = 10;
    B->data = 20;
    C->data = 30;

    H->next = A;
    A->next = B;
    B->next = C;
    C->next = H;

    H->prior = C;
    A->prior = H;
    B->prior = A;
    C->prior = B;

    LNode* p = H;        //p即为图中的Head
    bool isNotNull = linkedListIsNotNull(p);
    if (isNotNull) {
        traverseLinkedList(p);
    }
    else {
        printf("链表为空!");
    }
}


代码12: 有头结点的双循环链表

         

有头结点的双循环链表的判空条件: LNode *p = H;若p->next == p 或 p->prior == p; 则链表为空

        不管是单链表, 双链表还是循环链表, 只要不含头结点, 判空条件均为Head == NULL为真.

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

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

相关文章

SHELL CLASH配置规则

本地环境 小米AX1800路由器&#xff0c;旧版&#xff08;黢黑的四方立柱体&#xff0c;没有天线&#xff09;&#xff0c;已降级至固定版本小米 AX1800 1.0.336 问题 因为某些网络原因&#xff0c;在WiFi中安装了shell clash作为互联网代理&#xff0c;使用部分软件时提示网…

ChatGPT在大学里该怎么用?24所英国顶级大学宣布立场!

自从ChatGPT横空出世以来&#xff0c;许多行业都被得到赋能。而在教育行业&#xff0c;许多大学生把它视为写作业的“神器”&#xff1a;节省查阅复杂文献的时间、编写简单的Python代码、辅助学生理解知识点&#xff0c;甚至直接“生成”论文...... 但这些行为&#xff0c;学校…

个人使用:Windows下 OpenCV 的下载安装(2021.12.4详细)

一、下载OpenCV   到OpenCV官网Release(发布)板块下载OpenCV-4.5.4 Windows。 下载后是这样的 然后双击他&#xff0c;解压&#xff0c;就是大佬们说的安装&#xff0c;实质就是解压一下&#xff0c;解压完出来一个文件夹&#xff0c;其他什么也没发生。你把这个文件夹放在哪…

4、Kubernetes 架构、理解 k8s 架构

kubeadm 用于初始化 Cluster。 kubectl 是 Kubernetes 命令行工具。通过 kubectl 可以部署和管理应用,查看各种资源,创建、删除和更新各种组件。 kubelet 运行在 Cluster 所有节点上,负责启动 Pod 和容器。 Kubernetes Cluster 由 Master 和 Node 组成,节点上运行着若干 Ku…

【Linux从入门到精通】gdb调式技巧大全

本篇文章会对开发工具:gdb调试器的使用进行讲解。希望本篇文章会对你有所帮助。 目录 一、gdb简单介绍 二、gdb 调试 2、1 加入调试信息 2、2 调试命令 2、2、1 gdb可执行程序——进入调式 2、2、2 l行号/函数名——显示代码 2、2、3 b行号/函数名——打断点 2、2、4 infob——…

mysql数据库的定时备份脚本(docker环境和非docker环境)

一、非docker安装的MySQL MySQL作为一种常用的数据库管理系统,拥有着众多的优秀特性,如高性能、高可靠性、高可扩展性等。然而,在数据备份上,也需要我们进行一定的处理,这样才能保证数据的安全性。因此,在这里我们将介绍如何定时备份MySQL数据库。 我们可以通过MySQL自…

Filebeat+ELK (grok、mutate、mutiline、date)详解

目录 一&#xff1a;FilebeatELK 部署 1&#xff0e;安装 Filebeat 2&#xff0e;设置 filebeat 的主配置文件 3.在 Logstash 组件所在节点上新建一个 Logstash 配置文件 4.访问登录 二&#xff1a;grok 正则捕获插件 1、内置正则表达式调用 2、举例说明 3、自定义表达式…

飞腾开发者平台上线龙蜥专区,为开发者提供硬核技术支持

开源的本质在“人”&#xff0c;开源社区是由一个个做贡献的人构建起来的。近日&#xff0c;飞腾开发者平台联合国内领先的操作系统开源社区——龙蜥社区&#xff0c;推出龙蜥专区&#xff0c;旨在为广大开源爱好者提供社区前沿技术和一线开源文档。 龙蜥专区&#xff08;专区…

从低效率到高效率,开源大数据技术助力职场办公质的飞跃!

在办公流程化管理越来越流行的今天&#xff0c;如何让职场办公实现高效率飞跃和发展&#xff1f;应用开源大数据技术和低代码技术平台&#xff0c;可以实现这一目的&#xff0c;助力企业从传统的开发模式中剥离出来&#xff0c;实现高效率办公发展。今天&#xff0c;趁着大好契…

天池大赛中药说明书实体识别挑战冠军方案开源(一)方案及模型原理说明

目录 Introduction 导言赛题背景任务描述数据探索分析 核心思路数据预处理Baseline: BERT-CRF优化1&#xff1a;对抗训练优化2&#xff1a;混合精度训练&#xff08;FP16&#xff09;优化3&#xff1a;多模型融合优化4&#xff1a;半监督学习其他无明显提升的尝试方案最终线上成…

Linux开发工具【gdb】

Linux开发工具【gdb】 程序的发布方式有两种&#xff0c;debug模式&#xff08;调试版本&#xff09;和release模式&#xff08;发布版本&#xff09;。Linux下gcc/g出来的二进制程序&#xff0c;默认是release模式。无法保证我们的代码有时候会出现问题&#xff0c;这时就要使…

博客质量分计算——发布 version 5.0

目录 1. 背景2. 质量分 version 5.02.1 version 4 存在问题分析2.2 version 5.0 改进2.3 消融分析2.3.1 正向积极得分消融实验2.3.2 正向累积得分单变量实验2.3.3 非高分文章消融实验 2.4 V4 和 V5.0 版本质量分分布对比 3. 总结4. 参考 1. 背景 博客质量分顾名思义是用于衡量…

最新CMS指纹识别技术

点击星标&#xff0c;即时接收最新推文 本文部分节选于《web安全攻防渗透测试实战指南&#xff08;第二版&#xff09;》&#xff0c;即将上架&#xff0c;敬请期待。 指纹识别 1&#xff0e;CMS简介 CMS&#xff08;Content Management System&#xff0c;内容管理系统&#x…

微信小程序学习之数据绑定,事件绑定,事件传参与数据同步的学习记录

数据绑定&#xff0c;事件绑定&#xff0c;事件传参与数据同步 1. 数据绑定1.1. 在data中定义数据1.2. 在wxml中渲染数据 &#xff08;mustache语法&#xff09; 2. 事件绑定2.1. 事件2.2. 常用的事件2.3. 事件对象的属性列表2.4. target 和 currentTarget的区别 3. 事件传参与…

YOLOv5/v7 引入渐进特征金字塔网络 AFPN 结构 | 《2023年6月28日最新发表》

包含 yolov5-AFPN.yaml 和yolov7-AFPN.yaml 论文地址:https://arxiv.org/pdf/2306.15988.pdf 代码地址:https://github.com/gyyang23/AFPN 多尺度特征在目标检测任务中对于编码具有尺度变化的对象非常重要。一种常见的多尺度特征提取策略是采用经典的自顶向下和自底向上的特…

53 # 反转二叉树

由于我学习的视频这一节跟上一节重复。没找到该节的学习资源&#xff0c;我自己参考网上实现的&#xff0c;有问题还请指出。 如图&#xff0c;下面实现反转 // 节点 class Node {constructor(element, parent) {this.element element; // 存的数据this.parent parent; // …

Stable Diffusion - ReV Animated v1.2.2 的 2.5D 模型与提示词

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/131655644 ReV Animated v1.2.2 模型能够创建类似 2.5D 的图像生成。该模型是 checkpoint 合并&#xff0c;是其他模型的产物&#xff0c;以创建源…

微擎后台getshell,低权限也可以

/web/index.php?csite&aeditor 这个文件可以编辑html&#xff0c;然后前台会解析成php 没测试最新版 比如编辑专题&#xff1a;/web/index.php?csite&aeditor&dopage&multiid0 上架抓包 改html内容为php 复制前台url 访问之 博客原文&#xff1a; 微擎后…

Unity经营类美食小摊小游戏

Unity经营类美食小摊小游戏 挺有意思的小游戏 关卡页面 游戏主页面 有顾客上门 需要给顾客搭配他们想要的美食 会不断地有顾客过来&#xff0c;这个时候就考验手速的时候了&#xff0c;真实模拟经营 服务到位立马有钱 项目地址&#xff1a; https://download.csdn.net/downl…

PHY芯片的使用(三)在U-boot下网络PHY的移植

1、概述 以太网 PHY 驱动移植&#xff0c;主要包括 Linux、 u-boot 及设备树等三个方面标准框架下的移植。本文以裕太8511PHY为例展开说明。一般国产芯片厂商会提供在uboot/linux下PHY的驱动。在uboot/linux的PHY框架是有区别的&#xff0c;这点要注意。 YT8511 PHY 驱动的移…