📜  链表的类型(1)

📅  最后修改于: 2023-12-03 15:12:32.480000             🧑  作者: Mango

链表的类型

链表是计算机科学中最基本、最重要的数据结构之一。像树、堆、图这样的数据结构都是以链表为基础的。链表可以通过指针来实现,它将一系列的节点按照一定的次序串联在一起。

链表的定义

在计算机科学中,链表是由一组节点组成的数据结构,每个节点包含了数据和指向下一个节点的指针。链表的头指针指向第一个节点。

链表的定义通常包括链表的数据类型(如整型、字符型等),节点的结构,以及链表操作的函数:

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

// 链表的结构体
typedef struct LinkedList {
    Node *head;         // 链表头指针
} LinkedList;

// 链表的操作函数
void append(LinkedList *list, int data);   // 在链表中插入一个节点
void remove(LinkedList *list, int data);   // 从链表中删除一个节点

上面的代码定义了链表节点的结构体、链表的结构体和链表操作的函数。其中 appendremove 是链表的两个基本操作,分别用于在链表中插入和删除节点。

链表的分类

链表可以分为单向链表、双向链表和循环链表。

单向链表

单向链表中每个节点只有一个指向下一个节点的指针。头节点指针指向第一个节点,尾节点的指针为空。

单向链表的特点是只能单向遍历,不能反向遍历。

单向链表示例:

head -> 1 -> 2 -> 3 -> 4 -> NULL
双向链表

双向链表中每个节点有两个指针,一个指向下一个节点,一个指向上一个节点。头节点的上一个指针为空,尾节点的下一个指针为空。

双向链表的特点是可以双向遍历,即可以从头节点开始往后遍历,也可以从尾节点开始往前遍历。

双向链表示例:

NULL <- head <-> 1 <-> 2 <-> 3 <-> 4 <-> NULL
循环链表

循环链表中最后一个节点的指针指向头节点,形成了一个循环。循环链表可以是单向的或双向的。

循环链表的特点是可以从任意节点开始循环遍历整个链表。

单向循环链表示例:

head -> 1 -> 2 -> 3 -> 4 -> head

双向循环链表示例:

NULL <- head <-> 1 <-> 2 <-> 3 <-> 4 <-> head ->
链表的操作

链表的基本操作包括插入节点、删除节点、查找节点、遍历链表等。下面介绍链表的基本操作。

插入节点

链表中插入一个节点通常有两种情况,一种是在链表末尾插入,一种是在链表中间插入。

在链表末尾插入节点的实现如下:

void append(LinkedList *list, int data) {
    Node *node = (Node *) malloc(sizeof(Node));  // 创建一个新节点
    node->data = data;      // 设置节点数据
    node->next = NULL;      // 将节点的 next 指针置为空指针

    if(list->head == NULL) {  // 如果链表为空,则将头指针指向新节点
        list->head = node;
    } else {
        Node *last = list->head;
        while(last->next != NULL) {  // 找到最后一个节点
            last = last->next;
        }
        last->next = node;  // 将最后一个节点的 next 指针指向新节点
    }
}

在链表中间插入节点的实现如下:

void insert(LinkedList *list, Node *prev, int data) {
    Node *node = (Node *) malloc(sizeof(Node));  // 创建一个新节点
    node->data = data;      // 设置节点数据

    if(prev == NULL) {      // 如果前一个节点为空,则在头部插入新节点
        node->next = list->head;
        list->head = node;
    } else {
        node->next = prev->next;   // 将新节点的 next 指针指向前一个节点的后继节点
        prev->next = node;         // 将前一个节点的 next 指针指向新节点
    }
}
删除节点

链表中删除一个节点通常也有两种情况,一种是删除头节点,一种是删除中间节点。

删除头节点的实现如下:

void remove(LinkedList *list, int data) {
    Node *node = list->head;
    Node *prev = NULL;

    while(node != NULL && node->data != data) {   // 找到要删除的节点
        prev = node;
        node = node->next;
    }

    if(node != NULL) {      // 如果找到了,则删除该节点
        if(prev == NULL) {
            list->head = node->next;
        } else {
            prev->next = node->next;
        }
        free(node);
    }
}

删除中间节点的实现如下:

void removeAt(LinkedList *list, int index) {
    Node *node = list->head;
    Node *prev = NULL;
    int i = 0;

    while(node != NULL && i != index) {   // 找到要删除的节点
        prev = node;
        node = node->next;
        i++;
    }

    if(node != NULL) {       // 如果找到了,则删除该节点
        if(prev == NULL) {
            list->head = node->next;
        } else {
            prev->next = node->next;
        }
        free(node);
    }
}
遍历链表

遍历链表的实现如下:

void traverse(LinkedList *list) {
    Node *node = list->head;

    while(node != NULL) {   // 依次遍历链表节点
        printf("%d ", node->data);
        node = node->next;
    }
}
总结

链表是计算机科学中最基本、最重要的数据结构之一,可以通过指针来实现。链表可以分为单向链表、双向链表和循环链表。链表的基本操作包括插入节点、删除节点、查找节点和遍历链表等。链表的优点是可以动态地分配和释放内存,缺点是不能随机访问节点。在使用链表时需要注意内存泄漏的情况。