数据结构(线性表)

1线性表的定义与操作

1.1线性表的定义

线性表是一种基础的数据结构,其主要特点是:数据元素之间存在一种线性关系(一对一)。线性表的每一个数据元素都有一个唯一的前驱和后继,除了第一个元素没有前驱,最后一个元素没有后继

  1. 有限性:线性表中数据元素的个数是有限的。
  2. 序列性:数据元素之间存在着先后顺序,即线性表中的数据元素是有顺序的。
  3. 相同数据类型:线性表中的数据元素具有相同的数据类型。

线性表(Linear List)是由n个数据元素组成的有限序列,可以用以下形式表示:

  • 顺序线性表:数据元素在内存中占据连续的存储空间。
  • 链式线性表:数据元素在内存中不必连续,但每个元素都包含指向下一个元素的地址(指针)

1.2线性表的基本操作

  1. 初始化操作

    • 功能:创建一个空的线性表,为后续的操作做好准备。
    • 实现:通常分配一定的存储空间,并将线性表的长度设置为 0。
  2. 插入操作

    • 功能:在指定位置插入一个新的数据元素。
    • 实现:首先要判断插入位置是否合法,如果合法,则从插入位置开始,将后面的元素依次向后移动一位,然后在指定位置插入新元素,并更新线性表的长度。
    • 例如,在线性表 [1, 2, 3] 中,在位置 2 插入元素 4,结果为 [1, 2, 4, 3]。
  3. 删除操作

    • 功能:删除指定位置的数据元素。
    • 实现:首先判断删除位置是否合法,如果合法,则将删除位置后面的元素依次向前移动一位,覆盖被删除的元素,并更新线性表的长度。
    • 例如,在线性表 [1, 2, 3, 4] 中,删除位置 2 的元素,结果为 [1, 2, 4]。
  4. 查找操作

    • 功能:在线性表中查找指定的数据元素,并返回其位置。
    • 实现:从线性表的第一个元素开始,依次比较每个元素与目标元素是否相等,直到找到目标元素或遍历完整个线性表。
    • 例如,在线性表 [1, 2, 3, 4] 中,查找元素 3,返回位置 2。
  5. 遍历操作

    • 功能:依次访问线性表中的每个数据元素。
    • 实现:使用循环从线性表的第一个元素开始,依次访问每个元素,直到遍历完整个线性表。
    • 例如,对于线性表 [1, 2, 3, 4],遍历输出为 “1 2 3 4”。
  6. 获取长度操作

    • 功能:返回线性表中数据元素的个数。
    • 实现:直接返回存储线性表长度的变量值。
  7. 判断空操作

    • 功能:判断线性表是否为空。
    • 实现:如果线性表的长度为 0,则为空,返回 true;否则返回 false。

2线性表的顺序存储

2.1顺序表的定义与初始化

一、顺序表的定义

顺序表,又称线性表的顺序存储结构,是用一组地址连续的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。在顺序表中,各个表项的逻辑顺序与其存放的物理顺序一致,即第i个表项存储于第i个物理位置(1≤i≤n)。

顺序表具有以下特点:

  • 随机访问:可通过首地址和元素序号在单位时间O(1)内找到指定的元素。
  • 存储密度高:因为每个结点存储空间只用来存储数据元素,没有额外的开销。
  • 物理位置相邻:逻辑上相邻的元素在物理位置上也相邻,因此插入和删除元素需要移动大量元素,比较耗时。

二、顺序表的初始化

  1. 确定顺序表的存储结构

    • 通常可以使用数组来实现顺序表。定义一个数组来存储数据元素,并记录顺序表的当前长度。
  2. 分配存储空间

    • 根据实际需求确定数组的大小,并在内存中分配相应的空间。可以在程序运行时动态分配空间,也可以使用固定大小的数组。
  3. 设置初始状态

    • 将顺序表的长度初始化为 0,表示当前没有数据元素。同时,可以将数组的所有元素初始化为特定的值,如 0 或 null。


以下是用 C 语言实现顺序表初始化的示例代码:

方式一:静态数组定义和初始化

这是最简单的定义和初始化方式,适用于固定大小的顺序表。

#include <stdio.h>

#define MAXSIZE 100  // 定义顺序表的最大容量
typedef int ElemType;  // 定义顺序表中元素的类型

typedef struct {
    ElemType data[MAXSIZE];  // 存储数据的静态数组
    int length;  // 当前表长
} SqList;  // 定义顺序表的结构体类型

// 初始化顺序表
void InitList(SqList *L) {
    L->length = 0;  // 初始化表长为0
}

int main() {
    SqList L;  // 声明一个顺序表变量
    InitList(&L);  // 初始化顺序表
    // ... 其他操作
    return 0;
}

方式二:动态数组定义和初始化

这种方式允许在运行时动态分配内存,适用于不确定顺序表大小的情况。

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

typedef int ElemType;  // 定义顺序表中元素的类型

typedef struct {
    ElemType *data;  // 存储数据的动态数组
    int length;  // 当前表长
    int maxSize;  // 数组的最大容量
} SqList;  // 定义顺序表的结构体类型

// 初始化顺序表
void InitList(SqList *L, int maxSize) {
    L->data = (ElemType *)malloc(maxSize * sizeof(ElemType));  // 动态分配内存
    if (L->data == NULL) {
        printf("内存分配失败\n");
        exit(1);
    }
    L->length = 0;  // 初始化表长为0
    L->maxSize = maxSize;  // 设置最大容量
}

// 释放顺序表的内存
void FreeList(SqList *L) {
    free(L->data);
    L->data = NULL;
    L->length = 0;
    L->maxSize = 0;
}

int main() {
    SqList L;  // 声明一个顺序表变量
    InitList(&L, 100);  // 初始化顺序表,最大容量为100
    // ... 其他操作
    FreeList(&L);  // 释放顺序表的内存
    return 0;
}

方式三:内存分配与初始化函数结合

这种方式将内存分配和初始化结合在一起,适用于需要初始化默认值的情况。

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

typedef int ElemType;  // 定义顺序表中元素的类型

typedef struct {
    ElemType *data;  // 存储数据的动态数组
    int length;  // 当前表长
    int maxSize;  // 数组的最大容量
} SqList;  // 定义顺序表的结构体类型

// 初始化顺序表并分配内存
SqList* CreateList(int maxSize) {
    SqList *L = (SqList *)malloc(sizeof(SqList));  // 动态分配顺序表结构体内存
    if (L == NULL) {
        printf("内存分配失败\n");
        exit(1);
    }
    L->data = (ElemType *)malloc(maxSize * sizeof(ElemType));  // 动态分配数组内存
    if (L->data == NULL) {
        printf("内存分配失败\n");
        free(L);
        exit(1);
    }
    L->length = 0;  // 初始化表长为0
    L->maxSize = maxSize;  // 设置最大容量
    return L;
}

// 释放顺序表的内存
void DestroyList(SqList *L) {
    free(L->data);
    free(L);
}

int main() {
    SqList *L = CreateList(100);  // 创建顺序表,最大容量为100
    // ... 其他操作
    DestroyList(L);  // 释放顺序表的内存
    return 0;
}

2.2顺序表的基本操作

1插入运算

插入运算的步骤

  1. 检查位置合法性
    • 确保插入位置 pos 在有效范围内,即 1 ≤ pos ≤ length + 1,其中 length 是顺序表的当前长度。
    • 如果 pos 小于 1 或大于 length + 1,则插入位置非法,通常返回错误或抛出异常。
  2. 检查容量
    • 确保顺序表的容量(即数组的大小)足够容纳新元素。
    • 如果当前容量不足,需要进行扩容操作(通常是分配一个更大的数组,并将原数组的内容复制到新数组中)。
  3. 插入元素
    • 从插入位置 pos 开始,将顺序表中该位置及其后的所有元素向后移动一个位置,以腾出空间插入新元素。
    • 将新元素插入到 pos - 1 的位置(因为数组索引从 0 开始)。
  4. 更新长度
    • 将顺序表的长度 length 增加 1。

以下是一个简单的 c语言实现,展示了如何在顺序表中插入元素

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

#define MAXSIZE 100  // 定义顺序表的最大容量
typedef int ElemType;  // 定义顺序表中元素的类型

typedef struct {
    ElemType data[MAXSIZE];  // 存储数据的数组
    int length;  // 当前表长
} SqList;  // 定义顺序表的结构体类型

// 初始化顺序表
void InitList(SqList *L) {
    L->length = 0;  // 初始化表长为0
}

// 插入元素到顺序表的指定位置
int InsertList(SqList *L, int pos, ElemType e) {
    if (L->length >= MAXSIZE) {
        printf("顺序表已满,无法插入元素 %d\n", e);
        return 0;  // 插入失败
    }
    if (pos < 1 || pos > L->length + 1) {
        printf("插入位置 %d 不合法\n", pos);
        return 0;  // 插入失败
    }
    // 从后向前移动元素,为新元素腾出位置
    for (int i = L->length; i >= pos; i--) {
        L->data[i] = L->data[i - 1];
    }
    L->data[pos - 1] = e;  // 插入新元素
    L->length++;  // 更新当前表长
    return 1;  // 插入成功
}

// 打印顺序表中的所有元素
void DisplayList(const SqList *L) {
    for (int i = 0; i < L->length; i++) {
        printf("%d ", L->data[i]);
    }
    printf("\n");
}

int main() {
    SqList L;  // 声明一个顺序表变量
    InitList(&L);  // 初始化顺序表

    // 插入1到10的数字到顺序表中
    for (int i = 1; i <= 10; i++) {
        InsertList(&L, i, i);
    }

    // 打印顺序表中的所有元素
    printf("顺序表中的元素:");
    DisplayList(&L);

    // 在位置3插入一个新元素 99
    if (InsertList(&L, 3, 99)) {
        printf("插入元素后的顺序表:");
        DisplayList(&L);
    }

    return 0;
}
2删除运算

删除运算的步骤

  1. 检查位置合法性
    • 确保删除位置 pos 在有效范围内,即 1 ≤ pos ≤ length,其中 length 是顺序表的当前长度。
    • 如果 pos 小于 1 或大于 length,则删除位置非法,通常返回错误或抛出异常。
  2. 删除元素
    • 从删除位置 pos 开始,将顺序表中该位置之后的所有元素向前移动一个位置,以填补被删除元素留下的空位。
    • 注意,不需要为被删除元素分配新的存储空间或进行其他特殊处理,因为顺序表的内存是连续的,只需调整后续元素的位置即可。
  3. 更新长度
    • 将顺序表的长度 length 减少 1。
// 删除顺序表中指定位置的元素
int deleteElem(SeqList *L, int pos) {
    if (L->length == 0) {
        return 0; // 顺序表为空,删除失败
    }
    if (pos < 1 || pos > L->length) {
        return 0; // 位置不合法,删除失败
    }
    for (int i = pos; i < L->length; i++) {
        L->data[i - 1] = L->data[i];
    }
    L->length--;
    return 1; // 删除成功
}

以下是一个完整的 C 代码示例,包括顺序表的定义、初始化、删除运算以及显示顺序表内容的功能。

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

#define MAXSIZE 100  // 定义顺序表的最大容量
typedef int ElemType;  // 定义顺序表中元素的类型

typedef struct {
    ElemType data[MAXSIZE];  // 存储数据的数组
    int length;  // 当前表长
} SqList;  // 定义顺序表的结构体类型

// 初始化顺序表
void InitList(SqList *L) {
    L->length = 0;  // 初始化表长为0
}

// 插入元素到顺序表的指定位置
int InsertList(SqList *L, int pos, ElemType e) {
    if (L->length >= MAXSIZE) {
        printf("顺序表已满,无法插入元素 %d\n", e);
        return 0;  // 插入失败
    }
    if (pos < 1 || pos > L->length + 1) {
        printf("插入位置 %d 不合法\n", pos);
        return 0;  // 插入失败
    }
    // 从后向前移动元素,为新元素腾出位置
    for (int i = L->length; i >= pos; i--) {
        L->data[i] = L->data[i - 1];
    }
    L->data[pos - 1] = e;  // 插入新元素
    L->length++;  // 更新当前表长
    return 1;  // 插入成功
}

// 删除顺序表中指定位置的元素
int DeleteList(SqList *L, int pos, ElemType *e) {
    if (L->length == 0) {
        printf("顺序表为空,无法删除元素\n");
        return 0;  // 删除失败
    }
    if (pos < 1 || pos > L->length) {
        printf("删除位置 %d 不合法\n", pos);
        return 0;  // 删除失败
    }
    *e = L->data[pos - 1];  // 保存被删除的元素
    // 从前向后移动元素,覆盖被删除的元素
    for (int i = pos - 1; i < L->length - 1; i++) {
        L->data[i] = L->data[i + 1];
    }
    L->length--;  // 更新当前表长
    return 1;  // 删除成功
}

// 打印顺序表中的所有元素
void DisplayList(const SqList *L) {
    for (int i = 0; i < L->length; i++) {
        printf("%d ", L->data[i]);
    }
    printf("\n");
}

int main() {
    SqList L;  // 声明一个顺序表变量
    InitList(&L);  // 初始化顺序表

    // 插入1到10的数字到顺序表中
    for (int i = 1; i <= 10; i++) {
        InsertList(&L, i, i);
    }

    // 打印顺序表中的所有元素
    printf("顺序表中的元素:");
    DisplayList(&L);

    // 从顺序表中删除位置3的元素
    ElemType deletedElem;
    if (DeleteList(&L, 3, &deletedElem)) {
        printf("删除了元素 %d\n", deletedElem);
        printf("删除元素后的顺序表:");
        DisplayList(&L);
    }

    return 0;
}
3按值查找

按值查找的步骤

  1. 遍历顺序表:从顺序表的第一个元素开始,逐个检查每个元素的值是否等于指定的值。

  2. 记录位置:如果找到了与指定值相等的元素,则记录该元素的位置(索引)。注意,根据具体实现,位置可能是从0开始还是从1开始的索引。

  3. 返回结果:如果遍历完整个顺序表后找到了元素,则返回记录的位置;如果未找到,则返回一个表示失败的值。

// 按值查找元素在顺序表中的位置
int searchElem(SeqList *L, int elem) {
    for (int i = 0; i < L->length; i++) {
        if (L->data[i] == elem) {
            return i + 1;
        }
    }
    return -1; // 未找到元素
}

 以下是一个简单的 c语言实现,展示了如何在顺序表中找到具体元素

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

#define MAXSIZE 100  // 定义顺序表的最大容量
typedef int ElemType;  // 定义顺序表中元素的类型

typedef struct {
    ElemType data[MAXSIZE];  // 存储数据的数组
    int length;  // 当前表长
} SqList;  // 定义顺序表的结构体类型

// 初始化顺序表
void InitList(SqList *L) {
    L->length = 0;  // 初始化表长为0
}

// 插入元素到顺序表的指定位置
int InsertList(SqList *L, int pos, ElemType e) {
    if (L->length >= MAXSIZE) {
        printf("顺序表已满,无法插入元素 %d\n", e);
        return 0;  // 插入失败
    }
    if (pos < 1 || pos > L->length + 1) {
        printf("插入位置 %d 不合法\n", pos);
        return 0;  // 插入失败
    }
    // 从后向前移动元素,为新元素腾出位置
    for (int i = L->length; i >= pos; i--) {
        L->data[i] = L->data[i - 1];
    }
    L->data[pos - 1] = e;  // 插入新元素
    L->length++;  // 更新当前表长
    return 1;  // 插入成功
}

// 按值查找顺序表中的元素
int LocateElem(const SqList *L, ElemType e) {
    for (int i = 0; i < L->length; i++) {
        if (L->data[i] == e) {
            return i + 1;  // 返回元素位置(从1开始)
        }
    }
    return 0;  // 未找到元素
}

// 打印顺序表中的所有元素
void DisplayList(const SqList *L) {
    for (int i = 0; i < L->length; i++) {
        printf("%d ", L->data[i]);
    }
    printf("\n");
}

int main() {
    SqList L;  // 声明一个顺序表变量
    InitList(&L);  // 初始化顺序表

    // 插入1到10的数字到顺序表中
    for (int i = 1; i <= 10; i++) {
        InsertList(&L, i, i);
    }

    // 打印顺序表中的所有元素
    printf("顺序表中的元素:");
    DisplayList(&L);

    // 按值查找元素 5
    int pos = LocateElem(&L, 5);
    if (pos) {
        printf("元素 5 的位置是 %d\n", pos);
    } else {
        printf("未找到元素 5\n");
    }

    // 按值查找元素 20
    pos = LocateElem(&L, 20);
    if (pos) {
        printf("元素 20 的位置是 %d\n", pos);
    } else {
        printf("未找到元素 20\n");
    }

    return 0;
}


3线性表的链式存储

优点

  1. 动态大小
    • 链表不需要在创建时指定大小,它可以根据需要动态地增长和缩小。
  2. 高效插入和删除
    • 在链表中,插入和删除操作(特别是在已知节点位置的情况下)通常可以在O(1)时间复杂度内完成,因为只需要调整相邻节点的指针。
  3. 无需连续内存
    • 链表节点可以分散在内存中的任何位置,这有助于减少内存碎片问题。
  4. 灵活性
    • 链表可以通过各种方式(如双向链表、循环链表等)进行扩展,以满足不同的需求。

缺点

  1. 访问速度慢
    • 链表不支持随机访问,因此查找特定位置的元素需要从头节点开始顺序遍历,时间复杂度为O(n)。
  2. 额外空间开销
    • 每个链表节点都需要额外的空间来存储指针,这增加了内存的使用量。
  3. 内存管理复杂
    • 链表节点的动态分配和释放需要仔细管理,以避免内存泄漏和碎片化。
  4. 缓存性能较差
    • 由于链表节点在内存中通常不是连续存储的,因此可能无法充分利用CPU缓存,导致访问速度下降。

应用场景

  • 链表适用于需要频繁进行插入和删除操作,但对随机访问要求不高的场景。
  • 双向链表和循环链表等扩展形式可以用于需要双向遍历或循环遍历的场景。
  • 在实现某些数据结构(如栈、队列、哈希表等)时,链表也可以作为底层存储结构。

3.1单向链表的结构

一、结构特点

  1. 节点组成
    • 单向链表由一系列节点(Node)组成,每个节点包含两个部分:数据域(Data)和指针域(Next)。
    • 数据域用于存储节点的数据,可以是任意类型,由编程者自行指定。
    • 指针域用于存储下一个节点的地址(或指针),指向链表中的下一个节点。
  2. 链接方向
    • 单向链表的链接方向是单向的,即只能从头节点开始,通过指针域依次访问后续节点。
  3. 头节点与头指针
    • 头节点是为了操作方便而设立的,通常放在第一个元素节点之前,其数据域一般无实际意义(但也可以用来存放链表长度等信息)。
    • 头指针是指向链表头节点的指针,无论链表是否为空,头指针均不为空,是链表的必要元素。
  4. 无头与有头链表
    • 无头链表:不设立头节点,头指针直接指向第一个元素节点(如果存在)。
    • 有头链表:设立头节点,头指针指向头节点,头节点的指针域指向第一个元素节点。

 二单链表的定义与初始化

 一、单链表的定义

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

 二、单链表的初始化

ListNode* initList() {
    // 创建一个空的头节点
    ListNode* head = (ListNode*)malloc(sizeof(ListNode));
    head->data = 0; // 头节点的数据域可以存储一些特殊信息,这里初始化为 0
    head->next = NULL;
    return head;
}

 以下是一个用C语言初始化单链表的示例(包含头节点):

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

// 定义链表节点
typedef int ElemType;  // 定义链表中元素的类型

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

// 初始化链表
LinkedList InitList() {
    LinkedList L = (LinkedList)malloc(sizeof(Node));  // 申请头节点空间
    if (!L) {
        printf("内存分配失败\n");
        exit(1);  // 内存分配失败,退出程序
    }
    L->next = NULL;  // 初始化头节点,指针域为空
    return L;  // 返回初始化后的链表
}

// 测试初始化链表
int main() {
    LinkedList L = InitList();  // 初始化链表
    if (L->next == NULL) {
        printf("链表初始化成功,头节点指向 NULL\n");
    }
    // 这里可以继续对链表进行插入、删除等操作
    free(L);  // 释放头节点的内存
    return 0;
}

3.2单链表的基本操作

1插入结点建立单链表

在单链表中插入节点是一个常见的操作,它允许我们在链表的特定位置添加一个新的节点。

  1. 在链表头部插入节点:新节点成为链表的新头节点。
  2. 在链表尾部插入节点:新节点成为链表的最后一个节点。
  3. 在链表中间插入节点:在指定位置插入新节点,新节点位于指定位置的节点之前。

单链表的定义

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

// 定义链表节点的类型
typedef int ElemType;  // 定义链表中元素的类型

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

初始化单链表

// 初始化链表
LinkedList InitList() {
    LinkedList L = (LinkedList)malloc(sizeof(Node));  // 申请头节点空间
    if (!L) {
        printf("内存分配失败\n");
        exit(1);  // 内存分配失败,退出程序
    }
    L->next = NULL;  // 初始化头节点,指针域为空
    return L;  // 返回初始化后的链表
}

在链表头部插入节点

// 在链表头部插入节点
void InsertHead(LinkedList L, ElemType e) {
    Node *s = (Node *)malloc(sizeof(Node));  // 申请新节点空间
    if (!s) {
        printf("内存分配失败\n");
        exit(1);  // 内存分配失败,退出程序
    }
    s->data = e;  // 新节点数据域赋值
    s->next = L->next;  // 新节点的指针域指向原头节点的下一个节点
    L->next = s;  // 头节点的指针域指向新节点
}

在链表尾部插入节点

// 在链表尾部插入节点
void InsertTail(LinkedList L, ElemType e) {
    Node *p = L;
    // 找到链表的最后一个节点
    while (p->next) {
        p = p->next;
    }
    Node *s = (Node *)malloc(sizeof(Node));  // 申请新节点空间
    if (!s) {
        printf("内存分配失败\n");
        exit(1);  // 内存分配失败,退出程序
    }
    s->data = e;  // 新节点数据域赋值
    s->next = NULL;  // 新节点的指针域指向NULL
    p->next = s;  // 原最后一个节点的指针域指向新节点
}

在指定位置插入节点

// 在链表的第pos个位置插入节点
int InsertAtPosition(LinkedList L, int pos, ElemType e) {
    Node *p = L;
    int i = 0;
    // 找到插入位置的前一个节点
    while (p && i < pos - 1) {
        p = p->next;
        i++;
    }
    if (!p || i > pos - 1) {
        printf("插入位置不合法\n");
        return 0;  // 插入失败
    }
    // 创建新节点
    Node *s = (Node *)malloc(sizeof(Node));
    if (!s) {
        printf("内存分配失败\n");
        exit(1);  // 内存分配失败,退出程序
    }
    s->data = e;  // 新节点数据域赋值
    s->next = p->next;  // 新节点的指针域指向插入位置的下一个节点
    p->next = s;  // 插入位置前一个节点的指针域指向新节点
    return 1;  // 插入成功
}

打印链表

// 打印链表中的所有元素
void DisplayList(LinkedList L) {
    Node *p = L->next;
    while (p) {
        printf("%d ", p->data);
        p = p->next;
    }
    printf("\n");
}

主函数

int main() {
    LinkedList L = InitList();  // 初始化链表

    // 在链表头部插入节点
    InsertHead(L, 1);
    InsertHead(L, 2);

    // 在链表尾部插入节点
    InsertTail(L, 3);
    InsertTail(L, 4);

    // 在指定位置插入节点
    InsertAtPosition(L, 3, 5);  // 在第3个位置插入元素5

    // 打印链表中的所有元素
    printf("链表中的元素:");
    DisplayList(L);

    return 0;
}
2有头节点的单链表

在有头节点的单链表中,链表包含一个额外的节点,称为头节点(或哑节点、哨兵节点)。头节点不存储实际的数据,而是作为链表的起点,其next指针指向链表的第一个实际数据节点。头节点的存在可以简化链表的操作,特别是在插入和删除节点时,因为它允许我们在链表头部进行这些操作而无需检查是否为空链表。

定义

在C语言中,有头节点的单链表可以通过定义一个结构体来表示链表节点,其中包含一个数据域和一个指向下一个节点的指针域。头节点也是这个结构体的一个实例,但其数据域通常不会被使用。

typedef struct Node {  
    int data; // 数据域,可以存储任意类型的数据,这里以int为例  
    struct Node* next; // 指针域,指向下一个节点  
} Node;

初始化

初始化有头节点的单链表时,需要为头节点分配内存,并将其next指针设置为NULL,表示链表当前为空。以下是一个初始化有头节点的单链表的函数示例:

Node* initLinkedList() {  
    Node* head = (Node*)malloc(sizeof(Node)); // 为头节点分配内存  
    if (head == NULL) {  
        // 内存分配失败,可以打印错误信息或进行其他错误处理  
        printf("内存分配失败\n");  
        return NULL; // 返回NULL表示初始化失败  
    }  
    head->next = NULL; // 初始化头节点的next指针为NULL,表示链表为空  
    return head; // 返回头节点的指针  
}
3求单链表的长度
#include <stdio.h>
#include <stdlib.h>

// 定义链表节点的类型
typedef int ElemType;

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

// 初始化有头节点的单链表
LinkedList InitList() {
    LinkedList L = (LinkedList)malloc(sizeof(Node));  // 申请头节点空间
    if (!L) {
        printf("内存分配失败\n");
        exit(1);
    }
    L->next = NULL;  // 初始化头节点的下一个节点为空
    return L;
}

// 在链表尾部插入节点
void InsertTail(LinkedList L, ElemType e) {
    Node *p = L;
    while (p->next) {
        p = p->next;
    }
    Node *s = (Node *)malloc(sizeof(Node));  // 申请新节点空间
    if (!s) {
        printf("内存分配失败\n");
        exit(1);
    }
    s->data = e;      // 新节点数据域赋值
    s->next = NULL;   // 新节点的指针域指向NULL
    p->next = s;      // 原最后一个节点的指针域指向新节点
}

// 求链表的长度
int GetLength(LinkedList L) {
    int length = 0;               // 初始化长度为0
    Node *p = L->next;           // 从头节点的下一个节点开始遍历
    while (p) {
        length++;                 // 计数
        p = p->next;             // 移动到下一个节点
    }
    return length;               // 返回链表的长度
}

// 打印链表中的所有元素
void DisplayList(LinkedList L) {
    Node *p = L->next;
    while (p) {
        printf("%d ", p->data);
        p = p->next;
    }
    printf("\n");
}

// 主函数
int main() {
    LinkedList L = InitList();  // 初始化链表

    // 插入一些元素到链表中
    InsertTail(L, 10);
    InsertTail(L, 20);
    InsertTail(L, 30);
    InsertTail(L, 40);

    // 打印链表
    printf("链表中的元素:");
    DisplayList(L);

    // 求链表的长度
    int length = GetLength(L);
    printf("链表的长度是:%d\n", length);

    return 0;
}
4输出表中所有元素
#include <stdio.h>
#include <stdlib.h>

// 定义链表节点的类型
typedef int ElemType;

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

// 初始化有头节点的单链表
LinkedList InitList() {
    LinkedList L = (LinkedList)malloc(sizeof(Node));  // 申请头节点空间
    if (!L) {
        printf("内存分配失败\n");
        exit(1);
    }
    L->next = NULL;  // 初始化头节点的下一个节点为空
    return L;
}

// 在链表尾部插入节点
void InsertTail(LinkedList L, ElemType e) {
    Node *p = L;
    while (p->next) {
        p = p->next;
    }
    Node *s = (Node *)malloc(sizeof(Node));  // 申请新节点空间
    if (!s) {
        printf("内存分配失败\n");
        exit(1);
    }
    s->data = e;      // 新节点数据域赋值
    s->next = NULL;   // 新节点的指针域指向NULL
    p->next = s;      // 原最后一个节点的指针域指向新节点
}

// 打印链表中的所有元素
void DisplayList(LinkedList L) {
    Node *p = L->next;  // 从头节点的下一个节点开始遍历
    while (p) {
        printf("%d ", p->data);  // 输出当前节点的数据域
        p = p->next;             // 移动到下一个节点
    }
    printf("\n");
}

// 主函数
int main() {
    LinkedList L = InitList();  // 初始化链表

    // 插入一些元素到链表中
    InsertTail(L, 10);
    InsertTail(L, 20);
    InsertTail(L, 30);
    InsertTail(L, 40);

    // 打印链表中的所有元素
    printf("链表中的元素:");
    DisplayList(L);

    return 0;
}
5删除指定元素
#include <stdio.h>
#include <stdlib.h>

// 定义链表节点的类型
typedef int ElemType;

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

// 初始化有头节点的单链表
LinkedList InitList() {
    LinkedList L = (LinkedList)malloc(sizeof(Node));  // 申请头节点空间
    if (!L) {
        printf("内存分配失败\n");
        exit(1);
    }
    L->next = NULL;  // 初始化头节点的下一个节点为空
    return L;
}

// 在链表尾部插入节点
void InsertTail(LinkedList L, ElemType e) {
    Node *p = L;
    while (p->next) {
        p = p->next;
    }
    Node *s = (Node *)malloc(sizeof(Node));  // 申请新节点空间
    if (!s) {
        printf("内存分配失败\n");
        exit(1);
    }
    s->data = e;      // 新节点数据域赋值
    s->next = NULL;   // 新节点的指针域指向NULL
    p->next = s;      // 原最后一个节点的指针域指向新节点
}

// 删除链表中指定的元素
void DeleteElement(LinkedList L, ElemType e) {
    Node *p = L;  // p 指向头节点
    Node *q;      // q 用于指向要删除的节点

    // 遍历链表,找到目标元素的前一个节点
    while (p->next && p->next->data != e) {
        p = p->next;
    }

    // 如果找到了目标元素
    if (p->next) {
        q = p->next;           // q 指向要删除的节点
        p->next = q->next;     // 修改前一个节点的指针域,跳过目标节点
        free(q);               // 释放目标节点的内存空间
        printf("元素 %d 已删除\n", e);
    } else {
        printf("链表中不存在元素 %d\n", e);
    }
}

// 打印链表中的所有元素
void DisplayList(LinkedList L) {
    Node *p = L->next;  // 从头节点的下一个节点开始遍历
    while (p) {
        printf("%d ", p->data);  // 输出当前节点的数据域
        p = p->next;             // 移动到下一个节点
    }
    printf("\n");
}

// 主函数
int main() {
    LinkedList L = InitList();  // 初始化链表

    // 插入一些元素到链表中
    InsertTail(L, 10);
    InsertTail(L, 20);
    InsertTail(L, 30);
    InsertTail(L, 40);

    // 打印链表中的所有元素
    printf("删除前的链表:");
    DisplayList(L);

    // 删除指定元素
    DeleteElement(L, 30);

    // 打印删除后的链表
    printf("删除后的链表:");
    DisplayList(L);

    return 0;
}
6查找特定元素
#include <stdio.h>
#include <stdlib.h>

// 定义链表节点的类型
typedef int ElemType;

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

// 初始化有头节点的单链表
LinkedList InitList() {
    LinkedList L = (LinkedList)malloc(sizeof(Node));  // 申请头节点空间
    if (!L) {
        printf("内存分配失败\n");
        exit(1);
    }
    L->next = NULL;  // 初始化头节点的下一个节点为空
    return L;
}

// 在链表尾部插入节点
void InsertTail(LinkedList L, ElemType e) {
    Node *p = L;
    while (p->next) {
        p = p->next;
    }
    Node *s = (Node *)malloc(sizeof(Node));  // 申请新节点空间
    if (!s) {
        printf("内存分配失败\n");
        exit(1);
    }
    s->data = e;      // 新节点数据域赋值
    s->next = NULL;   // 新节点的指针域指向NULL
    p->next = s;      // 原最后一个节点的指针域指向新节点
}

// 查找链表中指定的元素
int FindElement(LinkedList L, ElemType e) {
    Node *p = L->next;  // 从头节点的下一个节点开始遍历
    int index = 1;      // 记录元素的索引位置

    // 遍历链表,查找目标元素
    while (p) {
        if (p->data == e) {
            return index;  // 返回元素的索引位置
        }
        p = p->next;       // 移动到下一个节点
        index++;           // 索引位置加1
    }

    return -1;  // 如果未找到,返回-1
}

// 打印链表中的所有元素
void DisplayList(LinkedList L) {
    Node *p = L->next;  // 从头节点的下一个节点开始遍历
    while (p) {
        printf("%d ", p->data);  // 输出当前节点的数据域
        p = p->next;             // 移动到下一个节点
    }
    printf("\n");
}

// 主函数
int main() {
    LinkedList L = InitList();  // 初始化链表

    // 插入一些元素到链表中
    InsertTail(L, 10);
    InsertTail(L, 20);
    InsertTail(L, 30);
    InsertTail(L, 40);

    // 打印链表中的所有元素
    printf("链表中的元素:");
    DisplayList(L);

    // 查找指定元素
    ElemType target = 30;
    int index = FindElement(L, target);

    if (index != -1) {
        printf("元素 %d 在链表中的位置是 %d\n", target, index);
    } else {
        printf("链表中不存在元素 %d\n", target);
    }

    return 0;
}

3.3循环链表

循环链表是一种特殊的链表结构,其特点是链表中的最后一个节点指向头节点,形成一个闭环。这种结构使得链表的遍历可以从任何节点开始,并且在遍历到链表末尾时可以无缝地回到链表的开头。循环链表可以是单向的(每个节点只有一个指针指向下一个节点)或双向的(每个节点有两个指针,分别指向前一个节点和后一个节点)。

循环链表的特点

  1. 闭环结构:链表的最后一个节点指向头节点,形成一个闭环。
  2. 遍历灵活:可以从任何节点开始遍历,并且在遍历到链表末尾时可以回到链表的开头。
  3. 节省空间:相比于双向链表,单向循环链表只需要一个指针域,节省了空间。
  4. 应用场景:常用于需要循环操作的场景,如循环队列、约瑟夫环问题等。

循环链表的基本操作

1. 初始化循环链表

初始化一个空的循环链表,创建一个头节点,并使其指向自身。

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

// 定义链表节点的类型
typedef int ElemType;

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

// 初始化循环链表
CircularLinkedList InitCircularList() {
    CircularLinkedList L = (CircularLinkedList)malloc(sizeof(Node));  // 申请头节点空间
    if (!L) {
        printf("内存分配失败\n");
        exit(1);
    }
    L->next = L;  // 头节点的下一个节点指向自身,形成闭环
    return L;
}

2. 插入节点

在循环链表中插入节点,可以选择在链表的头部或尾部插入。

// 在链表尾部插入节点
void InsertTail(CircularLinkedList L, ElemType e) {
    Node *p = L;
    while (p->next != L) {  // 找到链表的最后一个节点
        p = p->next;
    }
    Node *s = (Node *)malloc(sizeof(Node));  // 申请新节点空间
    if (!s) {
        printf("内存分配失败\n");
        exit(1);
    }
    s->data = e;      // 新节点数据域赋值
    s->next = L;      // 新节点的指针域指向头节点
    p->next = s;      // 原最后一个节点的指针域指向新节点
}

3. 删除节点

删除循环链表中的指定节点。

// 删除链表中指定的元素
void DeleteElement(CircularLinkedList L, ElemType e) {
    Node *p = L;  // p 指向头节点
    Node *q;      // q 用于指向要删除的节点

    // 遍历链表,找到目标元素的前一个节点
    while (p->next != L && p->next->data != e) {
        p = p->next;
    }

    // 如果找到了目标元素
    if (p->next != L) {
        q = p->next;           // q 指向要删除的节点
        p->next = q->next;     // 修改前一个节点的指针域,跳过目标节点
        free(q);               // 释放目标节点的内存空间
        printf("元素 %d 已删除\n", e);
    } else {
        printf("链表中不存在元素 %d\n", e);
    }
}

4. 查找节点

查找循环链表中的指定元素。

// 查找链表中指定的元素
int FindElement(CircularLinkedList L, ElemType e) {
    Node *p = L->next;  // 从头节点的下一个节点开始遍历
    int index = 1;      // 记录元素的索引位置

    // 遍历链表,查找目标元素
    while (p != L) {
        if (p->data == e) {
            return index;  // 返回元素的索引位置
        }
        p = p->next;       // 移动到下一个节点
        index++;           // 索引位置加1
    }

    return -1;  // 如果未找到,返回-1
}

5. 打印链表

遍历并打印循环链表中的所有元素。

// 打印链表中的所有元素
void DisplayList(CircularLinkedList L) {
    Node *p = L->next;  // 从头节点的下一个节点开始遍历
    while (p != L) {
        printf("%d ", p->data);  // 输出当前节点的数据域
        p = p->next;             // 移动到下一个节点
    }
    printf("\n");
}

主函数示例

// 主函数
int main() {
    CircularLinkedList L = InitCircularList();  // 初始化循环链表

    // 插入一些元素到链表中
    InsertTail(L, 10);
    InsertTail(L, 20);
    InsertTail(L, 30);
    InsertTail(L, 40);

    // 打印链表中的所有元素
    printf("链表中的元素:");
    DisplayList(L);

    // 查找指定元素
    ElemType target = 30;
    int index = FindElement(L, target);

    if (index != -1) {
        printf("元素 %d 在链表中的位置是 %d\n", target, index);
    } else {
        printf("链表中不存在元素 %d\n", target);
    }

    // 删除指定元素
    DeleteElement(L, 30);

    // 打印删除后的链表
    printf("删除后的链表:");
    DisplayList(L);

    return 0;
}

3.4双向链表

双向链表是一种链表结构,与单向链表不同,双向链表中的每个节点除了包含数据域外,还包含两个指针,分别指向前一个节点和后一个节点。这种结构使得链表的操作更加灵活,但也增加了空间的开销。

双向链表的特点

  1. 双向遍历:可以从任何一个节点开始向前或向后遍历链表。
  2. 灵活性高:插入和删除操作更加方便,因为可以直接访问前一个节点和后一个节点。
  3. 空间开销大:每个节点包含两个指针,相比于单向链表,空间开销更大。
  4. 应用场景:适用于需要频繁插入和删除操作的场景,如文本编辑器中的撤销/重做功能。

双向链表的基本操作

1. 初始化双向链表

初始化一个空的双向链表,创建一个头节点,并使其前后指针都指向自身(如果链表为空)。

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

// 定义链表节点的类型
typedef int ElemType;

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

// 初始化双向链表
DoubleLinkedList InitDoubleList() {
    DoubleLinkedList L = (DoubleLinkedList)malloc(sizeof(Node));  // 申请头节点空间
    if (!L) {
        printf("内存分配失败\n");
        exit(1);
    }
    L->prev = L;  // 头节点的前指针指向自身
    L->next = L;  // 头节点的后指针指向自身
    return L;
}

2. 插入节点

在双向链表中插入节点,可以选择在链表的头部或尾部插入。

// 在链表尾部插入节点
void InsertTail(DoubleLinkedList L, ElemType e) {
    Node *p = L;
    while (p->next != L) {  // 找到链表的最后一个节点
        p = p->next;
    }
    Node *s = (Node *)malloc(sizeof(Node));  // 申请新节点空间
    if (!s) {
        printf("内存分配失败\n");
        exit(1);
    }
    s->data = e;      // 新节点数据域赋值
    s->next = L;      // 新节点的后指针指向头节点
    s->prev = p;      // 新节点的前指针指向原最后一个节点
    p->next = s;      // 原最后一个节点的后指针指向新节点
    L->prev = s;      // 头节点的前指针指向新节点
}

3. 删除节点

删除双向链表中的指定节点。

// 删除链表中指定的元素
void DeleteElement(DoubleLinkedList L, ElemType e) {
    Node *p = L->next;  // p 指向第一个节点

    // 遍历链表,找到目标元素
    while (p != L && p->data != e) {
        p = p->next;
    }

    // 如果找到了目标元素
    if (p != L) {
        p->prev->next = p->next;  // 修改前一个节点的后指针
        p->next->prev = p->prev;  // 修改后一个节点的前指针
        free(p);                  // 释放目标节点的内存空间
        printf("元素 %d 已删除\n", e);
    } else {
        printf("链表中不存在元素 %d\n", e);
    }
}

4. 查找节点

查找双向链表中的指定元素。

// 查找链表中指定的元素
int FindElement(DoubleLinkedList L, ElemType e) {
    Node *p = L->next;  // 从第一个节点开始遍历
    int index = 1;      // 记录元素的索引位置

    // 遍历链表,查找目标元素
    while (p != L) {
        if (p->data == e) {
            return index;  // 返回元素的索引位置
        }
        p = p->next;       // 移动到下一个节点
        index++;           // 索引位置加1
    }

    return -1;  // 如果未找到,返回-1
}

5. 打印链表

遍历并打印双向链表中的所有元素。

// 打印链表中的所有元素
void DisplayList(DoubleLinkedList L) {
    Node *p = L->next;  // 从第一个节点开始遍历
    while (p != L) {
        printf("%d ", p->data);  // 输出当前节点的数据域
        p = p->next;             // 移动到下一个节点
    }
    printf("\n");
}

主函数示例

// 主函数
int main() {
    DoubleLinkedList L = InitDoubleList();  // 初始化双向链表

    // 插入一些元素到链表中
    InsertTail(L, 10);
    InsertTail(L, 20);
    InsertTail(L, 30);
    InsertTail(L, 40);

    // 打印链表中的所有元素
    printf("链表中的元素:");
    DisplayList(L);

    // 查找指定元素
    ElemType target = 30;
    int index = FindElement(L, target);

    if (index != -1) {
        printf("元素 %d 在链表中的位置是 %d\n", target, index);
    } else {
        printf("链表中不存在元素 %d\n", target);
    }

    // 删除指定元素
    DeleteElement(L, 30);

    // 打印删除后的链表
    printf("删除后的链表:");
    DisplayList(L);

    return 0;
}

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

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

相关文章

selenium-Alert类用于操作提示框/确认弹框(4)

之前文章我们提到&#xff0c;在webdriver.WebDriver类有一个switch_to方法&#xff0c;通过switch_to.alert()可以返回Alert对象&#xff0c;而Alert对象主要用于网页中弹出的提示框/确认框/文本输入框的确认或者取消等动作。 Alert介绍 当在页面定位到提示框/确认框/文本录入…

Vulnhub靶场案例渗透[7]- DC7

文章目录 1. 靶场搭建2. 信息收集2.1 确定靶机ip2.2 服务信息收集2.3 社工信息收集 3. 提权 1. 靶场搭建 靶场源地址 检验下载文件的检验码&#xff0c;对比没问题使用vmware打开 # windwos 命令 Get-FileHash <filePath> -Algorithm MD5 # linux md5sum filepath2. 信…

计算机网络(以Linux讲解)

计算机网络 网络协议初识协议分层OSI七层模型TCP/IP五层模型--初识 网络中的地址管理IP地址MAC地址 网络传输基本流程网络编程套接字预备知识网络字节序socket编程UDP socketTCP socket地址转换函数Jsoncpp 进程间关系与守护进程进程组会话控制终端作业控制守护进程 网络命令TC…

线性代数 行列式

一、行列式 1、定义 一个数学概念&#xff0c;主要用于 线性代数中&#xff0c;它是一个可以从方阵&#xff08;即行数和列数相等的矩阵&#xff09;形成的一个标量&#xff08;即一个单一的数值&#xff09; 2、二阶行列式 &#xff0c;像这样将一个式子收缩称为一个 2*2 的…

Node.js入门——fs、path模块、URL端口号、模块化导入导出、包、npm软件包管理器

Node.js入门 1.介绍 定义&#xff1a;跨平台的JS运行环境&#xff0c;使开发者可以搭建服务器端的JS应用程序作用&#xff1a;使用Node.Js编写服务器端代码Node.js是基于Chrome V8引擎进行封装&#xff0c;Node中没有BOM和DOM 2.fs模块-读写文件 定义&#xff1a;封装了与…

Python异常处理详解:try, except, else, finally的使用方法与示例

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storm…

【Iceberg分析】Spark集成Iceberg采集输出

Spark集成Iceberg采集输出 文章目录 Spark集成Iceberg采集输出Iceberg提供了两类指标和提供了两类指标输出器ScanReportCommitReport LoggingMetricsReporterRESTMetricsReporter验证示例相关环境配置结果说明 Iceberg提供了两类指标和提供了两类指标输出器 ScanReport 包含在…

论文笔记:Prompt-Based Meta-Learning For Few-shot Text Classification

论文来源&#xff1a;EMNLP 2022 论文地址&#xff1a;2022.emnlp-main.87.pdf (aclanthology.org) 代码地址&#xff1a;GitHub - MGHZHANG/PBML GB/T 7714 Zhang H, Zhang X, Huang H, et al. Prompt-Based Meta-Learning For Few-shot Text Classification[C]//Proceedi…

一维数组的引用

#define SIZE 5 int main(void) { int i 0; int arr[SIZE] { 86,85,85,896,45 };//同理五个数据只是偶然&#xff0c;可能会更多 //输入 for (i 0;i < SIZE;i) { printf("请输入你的第%d个值&#xff1a;",i1); scanf_s(&…

设计模式之适配器模式(通俗易懂--代码辅助理解【Java版】)

文章目录 设计模式概述1、适配器模式2、适配器模式的使用场景3、优点4、缺点5、主要角色6、代码示例1&#xff09;UML图2&#xff09;源代码&#xff08;1&#xff09;定义一部手机&#xff0c;它有个typec口。&#xff08;2&#xff09;定义一个vga接口。&#xff08;3&#x…

拆解学习【无线充,EMMC,锂电池电量计,OTA】(二)

主要学习到了&#xff1a;无线充&#xff0c;EMMC&#xff0c;手表CPU方案&#xff0c;锂电池电量计&#xff0c;OTA。 无线充电功能是产品的核心卖点之一&#xff0c;充电头网通过拆解发现&#xff0c;手表内部使用恒玄BES2500BP智能手表单芯片解决方案&#xff0c;内置四核C…

图书馆自习室座位预约管理微信小程序+ssm(lw+演示+源码+运行)

摘 要 随着电子商务快速发展世界各地区,各个高校对图书馆也起来越重视.图书馆代表着一间学校或者地区的文化标志&#xff0c;因为图书馆丰富的图书资源能够带给我们重要的信息资源&#xff0c;图书馆管理系统是学校管理机制重要的一环&#xff0c;,面对这一世界性的新动向和新…

linux线程 | 线程的控制(二)

前言&#xff1a; 本节内容是线程的控制部分的第二个小节。 主要是列出我们的线程控制部分的几个细节性问题以及我们的线程分离。这些都是需要大量的代码去进行实验的。所以&#xff0c; 准备好接受新知识的友友们请耐心观看。 现在开始我们的学习吧。 ps:本节内容适合了解线程…

如何用AI两小时上线自己的小程序

ChatGPT这个轰动全球的产品自问世以来&#xff0c;已经过了将近2年的时间&#xff0c;各行各业的精英们如火如荼的将AI能力应用到自己生产的产品中来。 为分担人类的部分工作&#xff0c;AI还具有非常大的想象空间&#xff0c;例如对于一个程序员来说&#xff0c;使用AI生成快…

Redis——持久化

文章目录 Redis持久化Redis的两种持久化的策略定期备份&#xff1a;RDB触发机制rdb的触发时机&#xff1a;手动执行save&bgsave保存测试不手动执行bgsave测试bgsave操作流程测试通过配置&#xff0c;自动生成rdb快照RDB的优缺点 实时备份&#xff1a;AOFAOF是否会影响到red…

Redis:分布式 - 主从复制

Redis&#xff1a;分布式 - 主从复制 概念配置主从模式info replicationslave-read-onlytcp-nodelay 命令slaveof 主从结构一主一从一主多从 主从复制流程数据同步命令全量同步部分同步实时同步 节点晋升 概念 Redis的最佳应用&#xff0c;还是要在分布式系统中。对于非分布式…

前端优化,解决页面加载慢

问题&#xff1a;vue项目使用vite打包后&#xff0c;部署在nginx服务器上&#xff0c;页面上访问时很慢&#xff0c;发现有个js文件很大导致加载很慢 先说结论&#xff1a; 方式时间未优化前21s开启压缩&#xff08;6级&#xff09;6s去掉大依赖&#xff08;flowable&#xf…

YoloV8改进策略:BackBone改进|CAFormer在YoloV8中的创新应用,显著提升目标检测性能

摘要 在目标检测领域,模型性能的提升一直是研究者和开发者们关注的重点。近期,我们尝试将CAFormer模块引入YoloV8模型中,以替换其原有的主干网络,这一创新性的改进带来了显著的性能提升。 CAFormer,作为MetaFormer框架下的一个变体,结合了深度可分离卷积和普通自注意力…

解决海外社媒风控问题的工具——云手机

随着中国企业逐步进入海外市场&#xff0c;海外社交媒体的风控问题严重影响了企业的推广效果与账号运营。这种背景下&#xff0c;云手机作为一种新型技术解决方案&#xff0c;正日益成为企业应对海外社媒风控的重要工具。 由于海外社媒的严格监控&#xff0c;企业经常面临账号流…

【计算机网络 - 基础问题】每日 3 题(三十八)

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞…