C语言中的双链表和单链表详细解释与实现

C语言中的双链表详细解释与实现

双链表(Doubly Linked List)是一种常见的数据结构,它比单链表更灵活,因为每个节点都包含指向前驱和后继节点的指针。

双链表的基本结构

节点定义

c

复制

下载

复制代码
typedef struct Node {
    int data;           // 数据域
    struct Node* prev;  // 前驱指针
    struct Node* next;  // 后继指针
} Node;

双链表结构

c

复制

下载

复制代码
typedef struct {
    Node* head;  // 头指针
    Node* tail;  // 尾指针
    int size;    // 链表长度
} DoublyLinkedList;

基本操作实现

1. 初始化双链表

c

复制

下载

复制代码
void initList(DoublyLinkedList* list) {
    list->head = NULL;
    list->tail = NULL;
    list->size = 0;
}

2. 创建新节点

c

复制

下载

复制代码
Node* createNode(int data) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    if (newNode == NULL) {
        printf("内存分配失败!\n");
        exit(1);
    }
    newNode->data = data;
    newNode->prev = NULL;
    newNode->next = NULL;
    return newNode;
}

3. 在头部插入节点

c

复制

下载

复制代码
void insertAtHead(DoublyLinkedList* list, int data) {
    Node* newNode = createNode(data);
    
    if (list->head == NULL) {  // 空链表
        list->head = list->tail = newNode;
    } else {
        newNode->next = list->head;
        list->head->prev = newNode;
        list->head = newNode;
    }
    list->size++;
}

4. 在尾部插入节点

c

复制

下载

复制代码
void insertAtTail(DoublyLinkedList* list, int data) {
    Node* newNode = createNode(data);
    
    if (list->tail == NULL) {  // 空链表
        list->head = list->tail = newNode;
    } else {
        newNode->prev = list->tail;
        list->tail->next = newNode;
        list->tail = newNode;
    }
    list->size++;
}

5. 在指定位置插入节点

c

复制

下载

复制代码
void insertAtPosition(DoublyLinkedList* list, int data, int position) {
    if (position < 0 || position > list->size) {
        printf("无效的位置!\n");
        return;
    }
    
    if (position == 0) {
        insertAtHead(list, data);
    } else if (position == list->size) {
        insertAtTail(list, data);
    } else {
        Node* newNode = createNode(data);
        Node* current = list->head;
        
        // 移动到要插入位置的前一个节点
        for (int i = 0; i < position - 1; i++) {
            current = current->next;
        }
        
        newNode->next = current->next;
        newNode->prev = current;
        current->next->prev = newNode;
        current->next = newNode;
        
        list->size++;
    }
}

6. 删除头节点

c

复制

下载

复制代码
void deleteHead(DoublyLinkedList* list) {
    if (list->head == NULL) {
        printf("链表为空,无法删除!\n");
        return;
    }
    
    Node* temp = list->head;
    list->head = list->head->next;
    
    if (list->head == NULL) {  // 删除后链表为空
        list->tail = NULL;
    } else {
        list->head->prev = NULL;
    }
    
    free(temp);
    list->size--;
}

7. 删除尾节点

c

复制

下载

复制代码
void deleteTail(DoublyLinkedList* list) {
    if (list->tail == NULL) {
        printf("链表为空,无法删除!\n");
        return;
    }
    
    Node* temp = list->tail;
    list->tail = list->tail->prev;
    
    if (list->tail == NULL) {  // 删除后链表为空
        list->head = NULL;
    } else {
        list->tail->next = NULL;
    }
    
    free(temp);
    list->size--;
}

8. 删除指定位置的节点

c

复制

下载

复制代码
void deleteAtPosition(DoublyLinkedList* list, int position) {
    if (position < 0 || position >= list->size) {
        printf("无效的位置!\n");
        return;
    }
    
    if (position == 0) {
        deleteHead(list);
    } else if (position == list->size - 1) {
        deleteTail(list);
    } else {
        Node* current = list->head;
        
        // 移动到要删除的节点
        for (int i = 0; i < position; i++) {
            current = current->next;
        }
        
        current->prev->next = current->next;
        current->next->prev = current->prev;
        
        free(current);
        list->size--;
    }
}

9. 查找节点

c

复制

下载

复制代码
Node* findNode(DoublyLinkedList* list, int data) {
    Node* current = list->head;
    while (current != NULL) {
        if (current->data == data) {
            return current;
        }
        current = current->next;
    }
    return NULL;  // 未找到
}

10. 打印链表(从头到尾)

c

复制

下载

复制代码
void printListForward(DoublyLinkedList* list) {
    Node* current = list->head;
    printf("链表(正向): ");
    while (current != NULL) {
        printf("%d ", current->data);
        current = current->next;
    }
    printf("\n");
}

11. 打印链表(从尾到头)

c

复制

下载

复制代码
void printListBackward(DoublyLinkedList* list) {
    Node* current = list->tail;
    printf("链表(反向): ");
    while (current != NULL) {
        printf("%d ", current->data);
        current = current->prev;
    }
    printf("\n");
}

12. 释放链表内存

c

复制

下载

复制代码
void freeList(DoublyLinkedList* list) {
    Node* current = list->head;
    while (current != NULL) {
        Node* temp = current;
        current = current->next;
        free(temp);
    }
    list->head = list->tail = NULL;
    list->size = 0;
}

完整示例代码

c

复制

下载

复制代码
#include <stdio.h>
#include <stdlib.h>

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

typedef struct {
    Node* head;
    Node* tail;
    int size;
} DoublyLinkedList;

// 前面定义的所有函数在这里实现

int main() {
    DoublyLinkedList list;
    initList(&list);
    
    // 插入操作
    insertAtHead(&list, 10);
    insertAtHead(&list, 20);
    insertAtTail(&list, 30);
    insertAtPosition(&list, 15, 1);
    insertAtPosition(&list, 25, 3);
    
    // 打印链表
    printListForward(&list);   // 输出: 链表(正向): 20 15 10 25 30 
    printListBackward(&list);  // 输出: 链表(反向): 30 25 10 15 20 
    
    // 查找操作
    Node* found = findNode(&list, 10);
    if (found != NULL) {
        printf("找到节点: %d\n", found->data);
    } else {
        printf("未找到节点\n");
    }
    
    // 删除操作
    deleteHead(&list);
    deleteTail(&list);
    deleteAtPosition(&list, 1);
    
    printListForward(&list);   // 输出: 链表(正向): 15 25 
    
    // 释放内存
    freeList(&list);
    
    return 0;
}

双链表的应用场景

  1. 浏览器历史记录:前进和后退功能

  2. 文本编辑器:撤销和重做操作

  3. 音乐播放器:上一曲和下一曲功能

  4. 实现双端队列(Deque)

  5. 更复杂的数据结构基础:如平衡树等

双链表的优缺点

优点

  • 可以双向遍历

  • 删除操作更高效(已知节点时O(1))

  • 插入操作更灵活

缺点

  • 每个节点需要额外空间存储前驱指针

  • 操作比单链表稍复杂

通过这个完整的C语言实现,你应该对双链表有了深入的理解。在实际编程中,可以根据需要扩展这些基本操作,例如添加排序、合并等功能。

单链表如下:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

// 学生信息结构体

typedef struct Student {

int id;

char name[20];

int age;

float score;

struct Student *next;

} Student;

// 1. 创建链表

Student* createList() {

Student *head = (Student*)malloc(sizeof(Student));

if (head == NULL) {

printf("内存分配失败!\n");

exit(1);

}

head->next = NULL;

return head;

}

// 2. 插入节点 ( 头插法 )

void insertNode(Student *head) {

Student *newNode = (Student*)malloc(sizeof(Student));

if (newNode == NULL) {

printf("内存分配失败!\n");

return;

}

printf("请输入学号: ");

scanf("%d", &newNode->id);

printf("请输入姓名: ");

scanf("%s", newNode->name);

printf("请输入年龄: ");

scanf("%d", &newNode->age);

printf("请输入成绩: ");

scanf("%f", &newNode->score);

newNode->next = head->next;

head->next = newNode;

printf("添加成功!\n");

}

// 3. 删除节点 ( 根据学号 )

void deleteNode(Student *head, int id) {

Student *p = head->next;

Student *prev = head;

while (p != NULL) {

if (p->id == id) {

prev->next = p->next;

free(p);

printf("删除成功!\n");

return;

}

prev = p;

p = p->next;

}

printf("未找到学号为%d的学生!\n", id);

}

// 4. 查找学生信息

void searchStudent(Student *head, int id) {

Student *p = head->next;

while (p != NULL) {

if (p->id == id) {

printf("学号: %d, 姓名: %s, 年龄: %d, 成绩: %.2f\n",

p->id, p->name, p->age, p->score);

return;

}

p = p->next;

}

printf("未找到学号为%d的学生!\n", id);

}

// 5. 打印整个链表

void printList(Student *head) {

Student *p = head->next;

if (p == NULL) {

printf("链表为空!\n");

return;

}

printf("学生信息如下:\n");

printf("学号\t姓名\t年龄\t成绩\n");

while (p != NULL) {

printf("%d\t%s\t%d\t%.2f\n", p->id, p->name, p->age, p->score);

p = p->next;

}

}

// 6. 计算链表长度

int listLength(Student *head) {

int count = 0;

Student *p = head->next;

while (p != NULL) {

count++;

p = p->next;

}

return count;

}

// 7. 清空链表

void clearList(Student *head) {

Student *p = head->next;

Student *temp;

while (p != NULL) {

temp = p;

p = p->next;

free(temp);

}

head->next = NULL;

printf("链表已清空!\n");

}

// 主函数

int main() {

Student *head = createList();

int choice, id;

while (1) {

printf("\n学生信息管理系统\n");

printf("1. 添加学生\n");

printf("2. 删除学生\n");

printf("3. 查找学生\n");

printf("4. 显示所有学生\n");

printf("5. 统计学生人数\n");

printf("6. 清空链表\n");

printf("0. 退出\n");

printf("请选择操作: ");

scanf("%d", &choice);

switch (choice) {

case 1:

insertNode(head);

break;

case 2:

printf("请输入要删除的学生学号: ");

scanf("%d", &id);

deleteNode(head, id);

break;

case 3:

printf("请输入要查找的学生学号: ");

scanf("%d", &id);

searchStudent(head, id);

break;

case 4:

printList(head);

break;

case 5:

printf("当前共有%d名学生\n", listLength(head));

break;

case 6:

clearList(head);

break;

case 0:

clearList(head);

free(head);

printf("程序已退出!\n");

return 0;

default:

printf("无效的选择!\n");

}

}

return 0;

}

算法说明

  1. 链表结构:使用带头节点的单链表结构,头节点不存储数据,仅作为链表起始标志。
  2. 插入操作:采用头插法,新节点总是插入到头节点之后,时间复杂度O(1)。
  3. 删除操作:根据学号查找并删除节点,需要遍历链表,时间复杂度O(n)。
  4. 查找操作:根据学号线性查找,时间复杂度O(n)。
  5. 内存管理:每个节点动态分配内存,清空链表时需要逐个释放。
  6. 用户交互:通过简单的菜单系统实现功能选择。
相关推荐
大樊子2 分钟前
JavaScript 中的单例模式
开发语言·javascript·单例模式
sevevty-seven4 分钟前
解决 Arduino IDE 2.3.6 在 Windows 上无法启动:缺少 Documents 文件夹与注册表路径错误
ide·windows·stm32
满怀101512 分钟前
【Python核心库实战指南】从数据处理到Web开发
开发语言·前端·python
andlbds23 分钟前
Ubuntu20.04安装Pangolin遇到的几种报错的解决方案
开发语言·c++
HyperAI超神经1 小时前
【vLLM 学习】Aqlm 示例
java·开发语言·数据库·人工智能·学习·教程·vllm
小柒的博客1 小时前
从C语言变量看内存
c语言·开发语言
炯哈哈1 小时前
【上位机——MFC】菜单类与工具栏
开发语言·c++·mfc·上位机
2401_846535952 小时前
spark和hadoop的区别与联系
开发语言·spark
爱编程的王小美2 小时前
Scala 入门指南
开发语言·后端·scala
YuforiaCode2 小时前
第十四届蓝桥杯 2023 C/C++组 飞机降落
c语言·c++·蓝桥杯