C语言数据结构之链表

news2024/11/17 14:36:50

目录

顺序存储和链式存储

数组—顺序存储

链表—链式存储

单链表

单链表的基本设计

单链表概念&设计

单链表的基本操作

双向链表

双向链表的基本设计

双向链表的基本操作

循环链表

循环链表的介绍及创建

循环链表的基本操作


顺序存储和链式存储

数组—顺序存储

数组是一种顺序存储结构,它是由相同类型的元素按照一定顺序排列而成的数据集合。在内存中,数组的元素是连续存储的,每个元素占据相同大小的内存空间。

在数组中,每个元素都可以通过下标来访问和操作,数组的下标从0开始,依次递增。通过下标,可以快速地定位到数组中的特定元素。

顺序存储的特点:

  • 随机访问:由于数组元素在内存中是连续存储的,所以可以通过下标直接访问和修改任意位置的元素,具有较快的随机访问速度。
  • 内存连续性:数组中的元素在内存中是连续存储的,这样可以提高数据的读取效率,减少对内存的访问次数。
  • 固定大小:数组的大小在创建时就确定了,并且不能动态改变。如果需要存储更多的元素,就需要重新创建一个更大的数组,并将原数组的元素复制到新数组中。

顺序存储的优点:

  • 高效的随机访问:由于数组的元素在内存中是连续存储的,可以通过下标直接访问任意位置的元素,具有很高的访问效率。
  • 简单的内存管理:数组的内存分配和释放非常简单,只需要一次性分配一块连续的内存空间即可。

顺序存储的缺点:

  • 大小固定:数组在创建时需要指定大小,且大小固定不变。如果需要存储的元素个数超过了数组的大小,就需要重新分配更大的数组,涉及到数据的复制和内存管理的问题。
  • 插入和删除效率低:由于数组的大小固定,插入和删除元素时需要移动其他元素,导致效率较低。特别是在数组中间插入或删除元素时,需要移动后续元素的位置。

总结来说,数组是一种顺序存储结构,具有高效的随机访问和简单的内存管理等优点,但大小固定和插入删除效率低是其缺点。在使用数组时,需要根据实际需求权衡其优缺点,并选择合适的数据结构来存储和操作数据。

以C语言数组插入一个元素为例,当我们需要在一个数组{1,2,3,4,5,6,7}的第1个元素后(即第2个元素)的位置插入一个’A’时,我们需要做的有,将第1个元素后的整体元素后移,形成新的数组{1,2,2,3,4,5,6,7},再将第2个元素位置的元素替换为我们所需要的元素’A’,最终形成我们的预期,一个简单的插入操作要进行那么多的步骤,显然不是很核算。

由示意图的操作,我们可以看出这样做的弊端

  • 所需要移动的元素很多,浪费算力。
  • 必须为数组开足够多的空间,否则有溢出风险。

链表—链式存储

C语言使用中,由于以上出现的这些问题,我们链表的概念就应运而生。

链表是一种常见的数据结构,它使用链式存储方式来组织和管理数据。相比于数组的顺序存储,链表通过使用指针将多个节点连接起来,每个节点包含数据和指向下一个节点的指针。

链表的基本组成部分是节点(Node),每个节点包含两个字段:数据域(Data)和指针域(Next)。数据域用于存储具体的数据,指针域用于指向下一个节点。最后一个节点的指针域通常为空(NULL)。

链表的特点:

  1. 动态性:链表的大小可以动态地增加或减少,不像数组大小固定。
  2. 灵活性:链表可以在任意位置插入或删除节点,而不需要像数组一样移动其他元素。
  3. 内存分配灵活:链表中的节点可以分散存储在内存中的不同位置,不要求连续的内存空间。

链表的优点:

  1. 插入和删除效率高:由于链表的动态性和灵活性,插入和删除节点时只需要修改指针的指向,不需要移动其他元素,因此效率较高。
  2. 空间利用率高:链表中的节点可以根据实际需要动态分配内存,避免了固定大小的内存浪费。

链表的缺点:

  1. 随机访问效率低:由于链表中的节点不是连续存储的,不能通过下标直接访问元素,需要从头节点开始遍历链表才能找到目标节点,因此随机访问效率较低。
  2. 额外的内存空间开销:链表中每个节点都需要额外的指针域来存储下一个节点的地址,增加了内存空间的开销。

下面是一个简单的链表示例代码,实现了对一组整数进行逆序存储和打印的功能:

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

// 定义链表节点结构体
typedef struct Node {
    int data;           // 数据域
    struct Node* next;  // 指针域,指向下一个节点
} Node;

// 在链表头部插入新节点
void insertAtHead(Node** head, int data) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->data = data;
    newNode->next = *head;
    *head = newNode;
}

// 打印链表
void printLinkedList(Node* head) {
    Node* current = head;
    while (current != NULL) {
        printf("%d ", current->data);
        current = current->next;
    }
    printf("\n");
}

int main() {
    Node* head = NULL;  // 链表头节点初始化为空
    insertAtHead(&head, 3);
    insertAtHead(&head, 2);
    insertAtHead(&head, 1);
    printf("Linked List: ");
    printLinkedList(head);
    return 0;
}

上述代码中,定义了一个链表节点结构体Node,包含数据域data和指针域next。通过insertAtHead函数,在链表头部插入新节点,该函数接受一个指向头节点的指针,并在头节点之前插入新节点。printLinkedList函数用于打印链表中的所有元素。

在main函数中,首先创建一个空链表(头节点为NULL),然后调用insertAtHead函数插入三个节点,最后调用printLinkedList函数打印链表中的元素。

输出结果为:

Linked List: 1 2 3

这个示例展示了链表的基本操作,包括在头部插入节点和打印链表。通过指针的指向,可以将多个节点连接起来形成一个链表,并可以方便地进行插入、删除等操作。

接下来会依次给各位介绍:单链表,双链表,循环单链表,其功能不同但实现方式均大同小异。

单链表

单链表的基本设计

单链表概念&设计

单链表是一种常见的链表结构,它由一系列节点组成,每个节点包含数据和指向下一个节点的指针。单链表中的每个节点只有一个指针域,指向下一个节点,而最后一个节点的指针域为空(NULL)。

单链表的基本设计包括以下几个要素:

1、节点定义:定义一个节点结构体,包含数据域和指针域。例如,在C语言中可以使用如下方式定义节点:

typedef struct Node {
    int data;           // 数据域
    struct Node* next;  // 指针域,指向下一个节点
} Node;

2、头节点:单链表通常有一个头节点,它是链表的入口,用于指示链表的起始位置。头节点不存储实际的数据,它的指针域指向第一个真正存储数据的节点。头节点的作用是方便对链表的操作和管理。

3、创建链表:创建一个空链表时,需要初始化头节点,并将头节点的指针域置为空。例如,在C语言中可以使用如下方式创建空链表:

Node* head = (Node*)malloc(sizeof(Node));
head->next = NULL;

4、插入节点:在链表中插入新节点时,需要修改指针的指向,使得新节点插入到正确的位置。常见的插入方式有在链表头部插入和在链表尾部插入。在链表头部插入节点时,只需要将新节点的指针域指向原来的头节点,并将头节点指向新节点。在链表尾部插入节点时,需要找到最后一个节点,然后将最后一个节点的指针域指向新节点。

5、删除节点:删除链表中的节点时,需要修改前一个节点的指针域,使其指向被删除节点的下一个节点。然后释放被删除节点的内存空间。

6、遍历链表:遍历链表是指依次访问链表中的每个节点,可以通过指针的指向逐个遍历节点。常见的遍历方式是使用循环,从头节点开始,依次访问每个节点的数据域,并将指针移动到下一个节点,直到指针为空(到达链表末尾)。

这些是单链表的基本设计要素。通过合理地操作节点和指针的指向,可以实现对链表的插入、删除、查找等操作。在实际应用中,还可以根据需要扩展链表的功能,例如实现按索引访问节点、反转链表等操作。

单链表的基本操作

单链表的基本操作包括插入、删除和查找等操作。下面我将介绍这些基本操作的实现方法:

1、插入操作:

  • 在头部插入节点:创建一个新节点,将新节点的指针域指向原头节点,然后将头节点指向新节点。
  • 在尾部插入节点:遍历链表,找到最后一个节点,将最后一个节点的指针域指向新节点。

如图,在DATA1和DATA2数据结点之中插入一个NEW_DATA数据结点:

从原来的链表状态

到新的链表状态:

2、删除操作:

  • 删除指定节点:遍历链表,找到要删除的节点的前一个节点,将前一个节点的指针域指向要删除节点的下一个节点,然后释放要删除节点的内存空间。
  • 删除头节点:将头节点指向头节点的下一个节点,并释放原头节点的内存空间。
  • 删除尾节点:遍历链表,找到倒数第二个节点,将其指针域置为空,然后释放最后一个节点的内存空间。

参考如图

3、查找操作:

  • 按值查找:从头节点开始遍历链表,比较每个节点的数据域与目标值,直到找到匹配的节点或遍历到链表末尾。
  • 按索引查找:从头节点开始遍历链表,依次移动指针,直到达到指定索引位置的节点。

4、遍历操作:

  • 使用循环:从头节点开始,依次访问每个节点的数据域,并将指针移动到下一个节点,直到指针为空(到达链表末尾)。

需要注意的是,在进行插入和删除操作时,要确保链表的指针连接正确,避免出现内存泄漏或指针丢失的情况。

以下是一个简单示例代码,展示了单链表的基本操作:

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

typedef struct Node {
    int data;           // 数据域
    struct Node* next;  // 指针域,指向下一个节点
} Node;

// 在头部插入节点
void insertAtHead(Node** head, int data) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->data = data;
    newNode->next = *head;
    *head = newNode;
}

// 在尾部插入节点
void insertAtTail(Node** head, int data) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->data = data;
    newNode->next = NULL;

    if (*head == NULL) {
        *head = newNode;
    } else {
        Node* current = *head;
        while (current->next != NULL) {
            current = current->next;
        }
        current->next = newNode;
    }
}

// 删除指定节点
void deleteNode(Node** head, int data) {
    Node* current = *head;
    Node* prev = NULL;

    if (current != NULL && current->data == data) {
        *head = current->next;
        free(current);
        return;
    }

    while (current != NULL && current->data != data) {
        prev = current;
        current = current->next;
    }

    if (current == NULL) {
        return;
    }

    prev->next = current->next;
    free(current);
}

// 查找节点
Node* searchNode(Node* head, int data) {
    Node* current = head;
    while (current != NULL) {
        if (current->data == data) {
            return current;
        }
        current = current->next;
    }
    return NULL;
}

// 遍历链表
void printLinkedList(Node* head) {
    Node* current = head;
    while (current != NULL) {
        printf("%d ", current->data);
        current = current->next;
    }
    printf("\n");
}

int main() {
    Node* head = NULL;

    insertAtHead(&head, 3);
    insertAtHead(&head, 2);
    insertAtHead(&head, 1);

    printf("Linked List: ");
    printLinkedList(head);

    insertAtTail(&head, 4);
    insertAtTail(&head, 5);

    printf("Linked List after inserting at tail: ");
    printLinkedList(head);

    deleteNode(&head, 3);

    printf("Linked List after deleting node with value 3: ");
    printLinkedList(head);

    Node* foundNode = searchNode(head, 4);
    if (foundNode != NULL) {
        printf("Found node with value 4\n");
    } else {
        printf("Node with value 4 not found\n");
    }

    return 0;
}

上述代码中,定义了节点结构体Node,并实现了在头部插入节点、在尾部插入节点、删除节点、查找节点和遍历链表等操作。在main函数中,演示了对链表的插入、删除、查找和遍历操作。

输出结果为:

Linked List: 1 2 3
Linked List after inserting at tail: 1 2 3 4 5
Linked List after deleting node with value 3: 1 2 4 5
Found node with value 4

这个示例展示了单链表的基本操作,通过合理地操作指针和节点,可以实现对链表的增删查改等功能。

双向链表

双向链表的基本设计

双向链表的简介&概念

双向链表(Doubly Linked List)是一种常见的链表数据结构,与单向链表相比,每个节点除了包含指向下一个节点的指针外,还包含指向前一个节点的指针。

在双向链表中,每个节点都有两个指针,一个指向前一个节点,一个指向后一个节点。这使得在双向链表中可以方便地进行双向遍历和插入/删除操作。

一个完整的双向链表应该是头结点的pre指针指为空,尾结点的next指针指向空,其余结点前后相链。

双向链表的结点设计

对于每一个结点而言,

   +------+     +------+     +------+   
   |      |     |      |     |      |   
   | prev |<--->| data |<--->| next |   
   |      |     |      |     |      |   
   +------+     +------+     +------+   

其中,prev表示指向前一个节点的指针,data表示节点存储的数据,next表示指向后一个节点的指针。

双向链表的优点包括:

  • 可以双向遍历:由于每个节点都有指向前一个节点和后一个节点的指针,因此可以方便地从头到尾或从尾到头遍历链表。
  • 方便插入和删除操作:在双向链表中,插入和删除节点时只需要修改相邻节点的指针,不需要像单向链表那样要找到待操作节点的前一个节点。
  • 更灵活:相比于单向链表,双向链表的操作更加灵活,可以更方便地处理一些特定的问题。

然而,双向链表也有一些缺点:

  • 占用更多内存:由于每个节点都需要额外存储指向前一个节点的指针,相比于单向链表会占用更多的内存空间。
  • 需要更多的指针操作:在插入和删除节点时,需要同时修改前一个节点和后一个节点的指针,相对于单向链表需要更多的指针操作。

总的来说,双向链表在某些场景下可以提供更好的操作灵活性和效率,但也需要权衡其占用的额外内存空间和指针操作的复杂性。

双向链表的基本操作

1、定义节点结构体:

typedef struct Node {
    int data;           // 数据域
    struct Node* prev;  // 指向前一个节点的指针
    struct Node* next;  // 指向后一个节点的指针
} Node;

节点结构体中包含数据域以及指向前一个节点和后一个节点的指针。

2、创建双向链表:

Node* createLinkedList() {
    Node* head = (Node*)malloc(sizeof(Node));
    head->prev = NULL;
    head->next = NULL;
    return head;
}

创建一个空的双向链表,返回头节点的指针。

3、在双向链表的末尾插入节点:

void insertAtEnd(Node* head, int data) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->data = data;
    newNode->next = NULL;

    Node* current = head;
    while (current->next != NULL) {
        current = current->next;
    }

    current->next = newNode;
    newNode->prev = current;
}

遍历链表找到最后一个节点,然后将新节点插入到最后一个节点之后。

4、在双向链表的指定位置插入节点:

void insertAtPosition(Node* head, int data, int position) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->data = data;

    Node* current = head;
    int count = 0;
    while (current != NULL && count < position) {
        current = current->next;
        count++;
    }

    if (current == NULL) {
        printf("Invalid position\n");
        return;
    }

    newNode->next = current;
    newNode->prev = current->prev;
    current->prev->next = newNode;
    current->prev = newNode;
}

在双向链表的指定位置插入新节点。需要遍历链表找到指定位置的节点,然后更新相邻节点的指针,将新节点插入到两个节点之间。

5、删除双向链表中的节点:

void deleteNode(Node* head, int data) {
    Node* current = head->next;
    while (current != NULL) {
        if (current->data == data) {
            current->prev->next = current->next;
            if (current->next != NULL) {
                current->next->prev = current->prev;
            }
            free(current);
            break;
        }
        current = current->next;
    }
}

遍历链表找到要删除的节点,然后更新相邻节点的指针,跳过待删除的节点,并释放其内存空间。

6、遍历双向链表:

void printLinkedList(Node* head) {
    Node* current = head->next;
    while (current != NULL) {
        printf("%d ", current->data);
        current = current->next;
    }
    printf("\n");
}

遍历链表,从头节点的下一个节点开始,依次输出每个节点的数据。

7、释放内存:

void freeLinkedList(Node* head) {
    Node* current = head->next;
    while (current != NULL) {
        Node* temp = current;
        current = current->next;
        free(temp);
    }
    free(head);
}

在不再需要双向链表时,需要释放所有节点的内存空间,包括头节点。

以上是双向链表的基本操作,可以通过这些操作实现对双向链表的创建、插入、删除和遍历等功能。需要注意的是,在使用完毕后,要记得释放双向链表的内存空间,以防止内存泄漏。

循环链表

循环链表的介绍及创建

循环链表是一种特殊的链表数据结构,它与普通链表的区别在于,循环链表的尾节点指向头节点,形成一个循环的结构。这意味着在循环链表中,任何一个节点都可以作为起点开始遍历整个链表。

C语言中的循环链表可以通过以下方式进行创建和操作:

首先,我们需要定义循环链表的节点结构:

struct Node {
    int data;
    struct Node* next;
};

接下来,我们可以实现创建循环链表的函数:

struct Node* createNode(int data) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = data;
    newNode->next = NULL;
    return newNode;
}

void createCircularLinkedList(struct Node** headRef, int data) {
    struct Node* newNode = createNode(data);
    if (*headRef == NULL) {
        *headRef = newNode;
        newNode->next = *headRef;  // 将尾节点指向头节点,形成循环
    } else {
        struct Node* temp = *headRef;
        while (temp->next != *headRef) {  // 找到尾节点
            temp = temp->next;
        }
        temp->next = newNode;
        newNode->next = *headRef;  // 将新节点插入到尾节点后面,并将尾节点指向头节点
    }
}

以上代码中,createNode函数用于创建一个新的节点,并返回该节点的指针。createCircularLinkedList函数用于创建循环链表,它接受一个指向头节点的指针和要插入的数据作为参数。如果链表为空,则将新节点设置为头节点,并将尾节点指向头节点,形成循环。否则,找到尾节点,将新节点插入到尾节点后面,并更新尾节点的指针。

下面是一个示例,展示如何使用上述函数创建一个包含三个节点的循环链表:

int main() {
    struct Node* head = NULL;
    
    createCircularLinkedList(&head, 1);
    createCircularLinkedList(&head, 2);
    createCircularLinkedList(&head, 3);
    
    // 遍历循环链表并打印节点的值
    struct Node* current = head;
    do {
        printf("%d ", current->data);
        current = current->next;
    } while (current != head);
    
    return 0;
}

运行上述代码,将输出循环链表中每个节点的值:1 2 3。注意,我们使用了do-while循环来确保至少执行一次循环,即使链表为空。

通过上述方式,我们可以创建和操作循环链表。循环链表的特性使得在某些场景下更加方便,例如循环遍历和实现循环队列等。

循环链表的基本操作

循环链表是一种特殊的链表,它与普通链表不同的地方在于,循环链表的尾节点指向头节点,形成一个环。因此,循环链表可以通过任何一个节点遍历整个链表。

C语言中循环链表的基本操作包括插入、删除和遍历等。下面我们将详细介绍这些操作:

1、插入节点:在循环链表中插入一个新节点,可以在头部插入、尾部插入或者指定位置插入。

在头部插入节点:

void insertAtBeginning(struct Node** headRef, int data) {
    struct Node* newNode = createNode(data);
    if (*headRef == NULL) {
        *headRef = newNode;
        newNode->next = *headRef;  // 将尾节点指向头节点,形成循环
    } else {
        struct Node* last = *headRef;
        while (last->next != *headRef) {
            last = last->next;
        }
        newNode->next = *headRef;
        *headRef = newNode;
        last->next = *headRef;
    }
}

在尾部插入节点:

void insertAtEnd(struct Node** headRef, int data) {
    struct Node* newNode = createNode(data);
    if (*headRef == NULL) {
        *headRef = newNode;
        newNode->next = *headRef;  // 将尾节点指向头节点,形成循环
    } else {
        struct Node* last = *headRef;
        while (last->next != *headRef) {
            last = last->next;
        }
        last->next = newNode;
        newNode->next = *headRef;
    }
}

在指定位置插入节点:

void insertAtPosition(struct Node** headRef, int data, int position) {
    if (position < 0) {
        printf("Invalid position\n");
        return;
    }
    if (position == 0) {
        insertAtBeginning(headRef, data);
        return;
    }
    struct Node* newNode = createNode(data);
    struct Node* current = *headRef;
    int count = 0;
    while (count < position - 1 && current->next != *headRef) {
        current = current->next;
        count++;
    }
    if (count < position - 1) {
        printf("Invalid position\n");
        return;
    }
    newNode->next = current->next;
    current->next = newNode;
}

2、删除节点:从循环链表中删除一个节点,可以根据节点值、位置或者指定条件进行删除。
根据节点值删除节点:

void deleteNode(struct Node** headRef, int key) {
    if (*headRef == NULL) {
        printf("List is empty\n");
        return;
    }
    struct Node* current = *headRef;
    struct Node* prev = NULL;
    while (current->data != key && current->next != *headRef) {
        prev = current;
        current = current->next;
    }
    if (current->data != key) {
        printf("Node not found\n");
        return;
    }
    if (current == *headRef) {
        *headRef = current->next;
    }
    if (prev != NULL) {
        prev->next = current->next;
    }
    free(current);
}

根据位置删除节点:

void deleteAtPosition(struct Node** headRef, int position) {
    if (*headRef == NULL) {
        printf("List is empty\n");
        return;
    }
    if (position < 0) {
        printf("Invalid position\n");
        return;
    }
    if (position == 0) {
        struct Node* last = *headRef;
        while (last->next != *headRef) {
            last = last->next;
        }
        struct Node* temp = *headRef;
        *headRef = (*headRef)->next;
        last->next = *headRef;
        free(temp);
        return;
    }
    struct Node* current = *headRef;
    struct Node* prev = NULL;
    int count = 0;
    while (count < position && current->next != *headRef) {
        prev = current;
        current = current->next;
        count++;
    }
    if (count < position) {
        printf("Invalid position\n");
        return;
    }
    prev->next = current->next;
    free(current);
}

3、遍历循环链表:遍历并输出循环链表中的所有节点。

void traverseCircularLinkedList(struct Node* head) {
    if (head == NULL) {
        printf("List is empty\n");
        return;
    }
    struct Node* current = head;
    do {
        printf("%d ", current->data);
        current = current->next;
    } while (current != head);
}

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

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

相关文章

计算机操作系统重点概念整理-第五章 文件管理【期末复习|考研复习】

第五章 文件管理 【期末复习|考研复习】 计算机操作系统系列文章传送门&#xff1a; 第一章 计算机系统概述 第二章 进程管理 第三章 进程同步 第四章 内存管理 第五章 文件管理 第六章 输出输出I/O管理 文章目录 第五章 文件管理 【期末复习|考研复习】前言五、文件管理5.1 文…

iOS Xcode15 适配:Other Linker Flags:-ld_classic

0x00 适配是一条没有尽头的路 Xcode 14 毛问题都没有&#xff0c;Xcode 15 崩溃 看图说话 0x01 解决方案 Other Linker Flags 添加 -ld_classic 即可 0x02 我的小作品 欢迎体验我的作品之一&#xff1a;小挑战-XGame 拼图游戏&#xff0c;渐变色游戏&#xff0c;经典24点游…

中南林业科技大学javaweb实验报告

文章目录 &#x1f4cd; 前置说明实验一 Web服务器开发环境配置一、实验目的二、实验内容三、实验步骤3.1 JDK 的安装与配置3.2 IDEA 中配置 Tomcat3.3 创建 Web 工程3.4 配置 web 工程3.5 运行服务3.6 乱码的解决 四、实验心得 实验二 HTML和Javascript的应用一、实验目的二、…

AI 浪潮下的创业故事(二)|Azure OpenAI Service - ChatU

点击蓝字 关注我们 编辑&#xff1a;Alan Wang 排版&#xff1a;Rani Sun 微软 Reactor 为帮助广开发者&#xff0c;技术爱好者&#xff0c;更好的学习 .NET Core, C#, Python&#xff0c;数据科学&#xff0c;机器学习&#xff0c;AI&#xff0c;区块链, IoT 等技术&#xff0…

vue3 源码解析(2)— ref、toRef、toRefs、shallowRef 响应式的实现

前言 vue3 源码解析&#xff08;1&#xff09;— reactive 响应式实现 介绍完 reactive 之后还有另一个很重要的响应式API&#xff0c;其中包括 ref、toRef、toRefs 和 shallowRef。这些API在vue3中起着至关重要的作用&#xff0c;它们帮助我们更好地管理和跟踪响应式数据的变…

【数据结构】搜索树 与 Java集合框架中的Set,Map

作者主页&#xff1a;paper jie_博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文录入于《JAVA数据结构》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精力…

C# OpenCvSharp Yolov8 Face Landmarks 人脸特征检测

效果 项目 代码 using OpenCvSharp; using OpenCvSharp.Dnn; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms;namespace OpenCvSharp_Yolov8_Demo {public partial class frmMain…

C++数据结构X篇_25_堆排序(不稳定的排序)

本篇根据十大经典排序算法-堆排序算法详解进行整理和补充。 文章目录 1. 基础知识点1.1 完全二叉树1.2 堆的基础知识 2. 堆排序2.1 什么是堆排序2.2 算法原理2.2.1 理解方法12.2.2 理解方法2 2.3 算法实现 3. 堆排序算法特点3.1 时间复杂度3.2 空间复杂度3.3 稳定性 1. 基础知…

深度学习| U-Net网络

U-Net网络 基础知识和CNN的关系反卷积ReLU激活函数 U-Net入门U-Net网络结构图为什么需要跳跃连接U-Net的输入U-Net的应用 基础知识 理解U-Net网络结构需要相关知识点。 和CNN的关系 U-Net也是CNN&#xff08;Convolutional Neural Network&#xff0c;卷积神经网络&#xff…

网络架构学习1

文章目录 网络架构学习11. 传统CNN卷积神经网络1.1 基本思想1.2 VCG16(经典CNN网络架构之一) 2. 两种经典的网络架构2.1 FCN网络2.2 U-Net网络 3. FCNVMB(基于U-Net架构)3.1 FCNVMB 主要思想3.2 FCNVMB 提供的其他思想 网络架构学习1 1. 传统CNN卷积神经网络 1.1 基本思想 C…

Android SurfaceFlinger做Layer合成时,如何与HAL层进行交互

目录 零、本文讨论问题的范围一、问题&#xff1a;SurfaceFlinger图层合成选择实现方式的两难1.1 从OpenGL ES、HWC本身来讲1.2 以HWC为主导的判断逻辑 二、SurfaceFlinger与HAL层进行交互的具体实现框架2.1 SurfaceFlinger 调用 OpenGL ES 流程2.2 FrameBuffer2.3 SurfaceFlin…

c语言从入门到实战——数组

数组 前言1. 数组的概念2. 一维数组的创建和初始化2.1 数组创建2.2 数组的初始化2.3 数组的类型 3. 一维数组的使用3.1 数组下标3.2 数组元素的打印3.3 数组的输入 4. 一维数组在内存中的存储5. sizeof计算数组元素个数6. 二维数组的创建6.1 二维数组得概念6.2 二维数组的创建 …

Java集成腾讯云OCR身份证识别接口

一、背景 项目用到身份证识别获取人员信息的功能&#xff0c;于是想到了腾讯云提供这样的API。在整合代码过程都很顺利&#xff0c;利用腾讯云官方SDK很快集成进来。但是在上测试环境部署时有了新的问题&#xff0c;通过Nginx代理后的环境无法访问到目标腾讯云接口&#xff0c;…

buuctf_练[CSCCTF 2019 Qual]FlaskLight

[CSCCTF 2019 Qual]FlaskLight 文章目录 [CSCCTF 2019 Qual]FlaskLight掌握知识解题思路关键paylaod 掌握知识 内置函数的过滤&#xff0c;globals变量的过滤&#xff0c;调用内部变量或函数的OS函数进行命令执行 解题思路 打开题目链接&#xff0c;很明显看标题和内容是fla…

【动态基础】从暴力递归到动态规划

C面经汇总 系列综述&#xff1a; 目的&#xff1a;本系列是个人整理为了秋招和实习面试的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡背诵量与深入程度。 来源&#xff1a;材料主要源于算法大神&#xff08;左程云&#xff09;教你从暴力递归到动态规划进行的&#xf…

vue实现连接线

效果展示 实现代码 下载插件npm install --save leader-line-vue <template><div class"wrap"><div ref"start" class"start">start</div><div ref"end" class"end">end</div></d…

数据结构时间复杂度(补充)和空间复杂度

Hello&#xff0c;今天事10月27日&#xff0c;距离刚开始写博客已经过去挺久了&#xff0c;我也不知道是什么让我坚持这么久&#xff0c;但是学校的课真的很多&#xff0c;很少有时间多出来再学习&#xff0c;有些科目马上要考试了&#xff0c;我还不知道我呢不能过哈哈哈&…

Django 全局配置 settings 详解

文章目录 1 概述1.1 Django 目录结构 2 常用配置&#xff1a;settings.py2.1 注册 APP&#xff1a;INSTALLED_APPS2.2 模板路径&#xff1a;TEMPLATES2.3 静态文件&#xff1a;STATICFILES_DIRS2.4 数据库&#xff1a;DATABASES2.5 允许访问的主机&#xff1a;ALLOWED_HOSTS 1 …

[SQL开发笔记]UPDATE 语句:更新表中的记录

一、功能描述&#xff1a; UPDATE 语句&#xff1a;用于更新表中的记录 二、UPDATE 语句语法详解&#xff1a; UPDATE 语法 UPDATE table_nameSET column1value1,column2value2,...WHERE some_columnsome_value; 参数说明&#xff1a; 1.table_name&#xff1a;要修改的表…

【Docker】github Actions自动构建

通过github的Actions 实现代码push仓库后自动构建容器并发布到DockerHub. 创建项目 首先我们创建一个项目,这里我就用Vue项目进行演示. npm init vuelatest Actions-demo-swback进去项目&#xff0c;按照提示执行 npm install npm run dev 启动项目. 首先保证项目的正常启动…