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 name20;

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. 用户交互:通过简单的菜单系统实现功能选择。
相关推荐
isyangli_blog3 小时前
OpenDayLight (Carbon 版本) 启动与组件安装
开发语言·php
vb2008113 小时前
FastAPI APIRouter
开发语言·python
Benszen3 小时前
KVM虚拟化解决方案
开发语言·perl
会编程的土豆3 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
東雪木3 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
杨充4 小时前
1.3 浮点型数据设计灵魂
开发语言·python·算法
噜噜噜阿鲁~4 小时前
python学习笔记 | 11.3、面向对象高级编程-多重继承
java·开发语言
basketball6164 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
春生野草4 小时前
反射、Tomcat执行
java·开发语言
雪的季节5 小时前
企业级 Qt 全功能项目
开发语言·数据库·qt