【数据结构】链表(单链表与双链表实现+原理+源码)

简介: 【数据结构】链表(单链表与双链表实现+原理+源码)

一、链表定义

链表是一种数据结构,它由一系列节点组成,这些节点按顺序连接在一起形成链式结构。每个节点包含数据和指向下一个节点的引用(指针)。链表的最后一个节点通常指向一个特定的值(如空值或null),表示链表的结束。

链表是一种数据结构,它由一系列节点组成,这些节点按顺序连接在一起形成链式结构。每个节点包含数据和指向下一个节点的引用(指针)。链表的最后一个节点通常指向一个特定的值(如空值或null),表示链表的结束。

链表可以分为单链表和双链表两种主要类型:

1. 单链表(Singly Linked List):每个节点包含数据和指向下一个节点的指针。链表的最后一个节点指向null。

1. 节点1      节点2      节点3
2. | 数据1 | -> | 数据2 | -> | 数据3 | -> null

2. 双链表(Doubly Linked List):每个节点包含数据、指向下一个节点的指针,以及指向前一个节点的指针。这使得在双链表中可以更方便地进行前向和后向遍历。

null <- | 数据1 | <-> | 数据2 | <-> | 数据3 | -> null

链表优点:  链表相对于数组的优势在于插入和删除操作的效率较高,因为不需要移动大量元素,只需调整节点的指针。然而,链表的缺点是访问元素时需要按顺序遍历,而数组可以通过索引直接访问元素。链表在内存中不需要连续的存储空间,因此可以更灵活地分配内存。

二、链表实战

1、单链表(C语言实现版本)

#include <stdio.h>
#include <stdlib.h>
 
// 定义节点结构
struct Node {
    int data;           // 节点数据
    struct Node* next;  // 指向下一个节点的指针
};
 
// 定义链表结构
struct LinkedList {
    struct Node* head;   // 链表头指针
};
 
// 初始化链表
void initLinkedList(struct LinkedList* list) {
    list->head = NULL;  // 将头指针初始化为NULL,表示链表为空
}
 
// 在链表末尾添加节点
void append(struct LinkedList* list, int data) {
    // 创建新节点
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
    new_node->data = data;
    new_node->next = NULL;
 
    // 判断链表是否为空
    if (list->head == NULL) {
        // 如果为空,将新节点设为头节点
        list->head = new_node;
    } else {
        // 如果不为空,找到链表末尾,将新节点链接到末尾
        struct Node* current = list->head;
        while (current->next != NULL) {
            current = current->next;
        }
        current->next = new_node;
    }
}
 
// 在链表开头添加节点
void prepend(struct LinkedList* list, int data) {
    // 创建新节点
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
    new_node->data = data;
    new_node->next = list->head;
 
    // 将新节点设为头节点
    list->head = new_node;
}
 
// 删除节点
void deleteNode(struct LinkedList* list, int data) {
    struct Node* current = list->head;
    struct Node* prev = NULL;
 
    // 遍历链表,找到待删除节点及其前一个节点
    while (current != NULL && current->data != data) {
        prev = current;
        current = current->next;
    }
 
    // 如果找到待删除节点
    if (current != NULL) {
        // 如果待删除节点不是头节点
        if (prev != NULL) {
            prev->next = current->next;
        } else {
            // 如果待删除节点是头节点
            list->head = current->next;
        }
        free(current);  // 释放内存
    }
}
 
// 更新节点
void updateNode(struct LinkedList* list, int oldData, int newData) {
    struct Node* current = list->head;
 
    // 遍历链表,找到待更新节点
    while (current != NULL && current->data != oldData) {
        current = current->next;
    }
 
    // 如果找到待更新节点
    if (current != NULL) {
        current->data = newData;  // 更新节点数据
    }
}
 
// 搜索节点
struct Node* searchNode(struct LinkedList* list, int data) {
    struct Node* current = list->head;
 
    // 遍历链表,找到包含指定数据的节点
    while (current != NULL && current->data != data) {
        current = current->next;
    }
 
    return current;  // 返回节点指针
}
 
// 显示链表内容
void display(struct LinkedList* list) {
    struct Node* current = list->head;
    while (current != NULL) {
        printf("%d -> ", current->data);
        current = current->next;
    }
    printf("NULL\n");
}
 
// 释放链表内存
void freeLinkedList(struct LinkedList* list) {
    struct Node* current = list->head;
    struct Node* next = NULL;
    while (current != NULL) {
        next = current->next;
        free(current);
        current = next;
    }
    list->head = NULL;
}
 
int main() {
    struct LinkedList myLinkedList;
    initLinkedList(&myLinkedList);
 
    // 添加节点
    append(&myLinkedList, 1);
    append(&myLinkedList, 2);
    append(&myLinkedList, 3);
 
    // 显示链表内容
    printf("链表内容:");
    display(&myLinkedList);
 
    // 在开头添加节点
    prepend(&myLinkedList, 0);
 
    // 显示链表内容
    printf("在开头添加节点后的链表:");
    display(&myLinkedList);
 
    // 删除节点
    deleteNode(&myLinkedList, 2);
 
    // 显示链表内容
    printf("删除节点后的链表:");
    display(&myLinkedList);
 
    // 更新节点
    updateNode(&myLinkedList, 1, 10);
 
    // 显示链表内容
    printf("更新节点后的链表:");
    display(&myLinkedList);
 
    // 搜索节点
    int searchData = 10;
    struct Node* searchResult = searchNode(&myLinkedList, searchData);
    if (searchResult != NULL) {
        printf("找到数据为 %d 的节点。\n", searchData);
    } else {
        printf("未找到数据为 %d 的节点。\n", searchData);
    }
 
    // 释放链表内存
    freeLinkedList(&myLinkedList);
 
    return 0;
}

执行结果详细:

2、双链表(C++)

#include <iostream>  
#include <cstdlib>  
#include <cstdio>  
  
using namespace std;  
typedef struct Node  
{  
    int data;  
    struct Node *prior;  
    struct Node *next;  
} LinkList;  
  
LinkList *Create()  
{  
    LinkList *head;  
    head=(LinkList*)malloc(sizeof(LinkList));  
    if(head!=NULL)  
    {  
        head->prior=NULL;  
        head->next=NULL;  
        return head;  
    }  
    else return NULL;  
}  
  
int Insert(LinkList *head,int e)  //尾插法
{  
    LinkList *p;  
    LinkList *q=head;  
    p=(LinkList*)malloc(sizeof(LinkList));  
    if(p!=NULL)  
    {  
        p->data=e;  
        p->prior=NULL;  
        p->next=NULL;  
        while(q->next!=NULL)  
        {  
            q=q->next;  
        }  
        q->next=p;  
        return 1;  
    }  
    return 0;  
}  
  
LinkList* Change(LinkList *head) //变成双向链表后返回一个尾指针  
{  
    LinkList *p,*q;  
    p=head;  
    q=p->next;  
    while(q!=NULL)  
    {  
        q->prior=p;  
        p=p->next;  
        q=q->next;  
    }  
    return p;  
}  
  
void Output1(LinkList *head) //从头到尾遍历输出  
{  
    LinkList *p;  
    p=head->next;  
    while(p!=NULL)  
    {  
        printf("%d ",p->data);  
        p=p->next;  
    }  
    printf("\n");  
}  
  
void Output2(LinkList *tail) //从尾到头遍历输出  
{  
    LinkList *p;  
    p=tail;  
    while(p->prior!=NULL)  
    {  
        printf("%d ",p->data);  
        p=p->prior;  
    }  
    printf("\n");  
}  
  
void FreeLink(LinkList *head)  //释放
{  
    LinkList *p,*q;  
    p=head;  
    q=NULL;  
    while(p!=NULL)  
    {  
        q=p;  
        p=p->next;  
        free(q);  
    }  
}  
  
int main()  
{  
    LinkList *phead,*tail;  
    int n,e,flag;  
    phead=Create();  
    if(phead!=NULL)  
    {  
        cout<<"请输入长度:";
 scanf("%d",&n);  
        for(int i=0;i<n;i++)  
        {  
            scanf("%d",&e);  
            flag=Insert(phead,e);  
        }
 cout<<"从头到尾输出为: ";
        Output1(phead);  
        tail=Change(phead); 
 cout<<"从尾到头输出为: ";
        Output2(tail);  
        FreeLink(phead);  
    }  
    return 0;  
}

代码执行结果:

三、分析总结

链表是一种常见的数据结构,具有一些优点和应用:

优点:

1. 动态内存分配:链表允许在运行时动态分配内存,这使得它更加灵活,不需要预先指定存储空间大小,通过动态分配内存可以实现降低时间运行成本。

2. 插入和删除效率高:在链表中插入和删除节点相对容易且效率较高。相比之下,数组在中间或开头插入/删除元素可能需要移动大量元素。

3. 大小可变:*链表可以根据需要动态增长或缩小,而不浪费内存。

应用:

1. 实现动态数据结构:链表常用于实现其他动态数据结构,如栈、队列、图等。

2. 内存分配:动态链表的能力使其在动态内存分配的场景中非常有用,例如,动态分配内存的链表可用于管理操作系统的进程列表。

3. 实现算法:链表常用于算法实现,例如,链表在排序算法、图算法等方面有广泛的应用。

4. 嵌入式系统: 在资源受限的嵌入式系统中,链表可以更好地处理动态数据。

5. LRU缓存淘汰算法:链表可以用于实现LRU(Least Recently Used)缓存淘汰算法,用于管理缓存中的数据。

6. 数据库:数据库中的索引通常使用链表实现,以支持高效的插入和删除操作。

总的来说,链表在许多场景中都是一种强大且灵活的数据结构,特别适合那些需要频繁插入和删除操作的应用。

小结

大家点赞、收藏、关注、评论啦 !

谢谢哦!如果不懂,欢迎大家下方讨论学习哦。


相关文章
|
2月前
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
63 4
|
8天前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
2月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
68 5
|
2月前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
114 4
|
2月前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
2月前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
2月前
|
算法
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
53 0
|
3月前
|
存储 Java
数据结构第三篇【链表的相关知识点一及在线OJ习题】
数据结构第三篇【链表的相关知识点一及在线OJ习题】
34 7
|
3月前
|
存储 Java
HashMap之链表转红黑树(树化 )-treefyBin方法源码解读(所有涉及到的方法均有详细解读,欢迎指正)
本文详细解析了Java HashMap中链表转红黑树的机制,包括树化条件(链表长度达8且数组长度≥64)及转换流程,确保高效处理大量数据。
113 1
|
3月前
|
存储 安全 Java
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
30 3