当前位置: 首页 > article >正文

数据结构【单链表操作大全详解】【c语言版】(只有输入输出为了方便用的c++)

单链表操作的C++/C实现详解

在数据结构中,单链表是一种非常基础且重要的数据结构。它由一系列节点组成,每个节点包含数据和指向下一个节点的指针。今天我们就来深入探讨用C/C++实现的单链表及其各种操作。

一、单链表的定义

const int N = 1e5;
//单链表
typedef struct node {
    int val;  //当前链表值
    struct node* next;  //链表后面的值域(地址)
}lst;

这里定义了一个单链表节点的结构体lstval用于存储节点的数据,next是一个指针,指向链表中的下一个节点。通过这种方式,节点之间可以串联起来形成链表。const int N = 1e5; 定义了一个常量N,虽然在当前代码中没有直接使用,但可以作为链表最大长度的一个限制,在实际应用中可能会用于内存分配或边界检查等。

二、链表初始化

//链表初始化
lst* initlst()
{
    lst* head;
    head = (lst*)malloc(sizeof (lst));//开辟空间
    head->next = NULL;  //指向空
    return head;
}

initlst函数用于初始化一个单链表。首先使用malloc函数为头节点分配内存空间,然后将头节点的next指针设置为NULL,表示这是一个空链表。最后返回头节点指针,后续对链表的操作都可以通过这个头节点来进行。

三、打印链表

//打印链表
void printlst(lst*l)
{
    lst* p = l;  //找到头指针
    while (p!= NULL)
    {
        if (p->val!= NULL)
            cout << p->val << endl;  //头指针不空
        p = p->next;  //一直取出next
    }
}

printlst函数用于打印链表中的所有节点值。通过一个临时指针p从链表的头节点开始遍历,只要p不为NULL,就表示还没有到达链表末尾。在循环中,检查当前节点的val值是否有效(这里if (p->val!= NULL)判断有误,valint类型,不能与NULL比较,应直接输出p->val),然后输出当前节点的值,并将p移动到下一个节点。

四、尾插法相关操作

(一)单个节点尾插

void insert2(lst* l, int x)  //尾插法
{
    lst* s, * last;
    last = l;
    while (last->next!= NULL)  //找到最后的值
    {
        last = last->next;
    }
    s = (lst*)malloc(sizeof(lst));
    s->val = x;
    s->next = NULL;
    last->next = s;
    last = s;
    puts("完成插入");
}

insert2函数实现了将一个新节点尾插到链表中。首先找到链表的最后一个节点(通过遍历到last->nextNULL),然后为新节点分配内存,设置新节点的值和next指针(nextNULL表示这是链表的最后一个节点),最后将新节点连接到链表的末尾,并更新last指针。

(二)多个节点尾插初始化

void initinsert2(lst *l, int n) {
    //尾插法の初始化
    lst* s, * last;
    last = l;
    puts("请输入");
    for (int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        s = (lst*)malloc(sizeof(lst));
        s->val = x;
        s->next = NULL;
        last->next = s;
        last = s;
    }
    puts("完成插入");
}

initinsert2函数用于通过尾插法初始化链表,一次性插入n个节点。它与insert2函数的原理类似,通过循环多次执行尾插操作,每次从用户输入中获取一个值,创建新节点并插入到链表末尾。

五、头插法相关操作

(一)单个节点头插

void insert1(lst* &l, int x)  //头插法[记得要取地址]
{
    //1.c++:lst *&l
    //2.c:lst** l
    lst* s;
    s = (lst*)malloc(sizeof(lst));
    s->val = x;
    s->next = l;
    l = s;
}

insert1函数实现了头插法,将一个新节点插入到链表的头部。这里lst* &l表示传递的是头指针的引用,这样在函数内部对l的修改会直接影响到函数外部的头指针。首先为新节点分配内存,设置新节点的值,然后将新节点的next指针指向原来的头节点,最后更新头指针l,使其指向新插入的节点。需要注意的是这里要用地址,方便更新头结点

(二)多个节点头插初始化

void initinsert1(lst* l, int n) {
    //头插法の初始化
    lst* s;
    puts("请输入");
    for (int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        s = (lst*)malloc(sizeof(lst));
        s->val = x;
        s->next = l->next;
        l->next = s;
        //l = s;
    }
    puts("完成插入");
}

六、求链表长度

//求长度
int get_len(lst* l)
{
    lst* p = l->next;
    int len = 0;
    while (p!= NULL)
    {
        p = p->next;
        len++;
    }
    return len;
}

get_len函数用于计算链表的长度(不包括头节点)。通过一个临时指针p从链表头节点的下一个节点开始遍历,每遍历一个节点,长度计数器len就加1,直到遍历完整个链表(pNULL),最后返回链表的长度。

七、查找操作

(一)按值查找位置

//查找
void find_idex(lst* l, int x)  //找x的位置
{
    lst* p = l->next;
    int id = 1;
    while (p!= NULL && p->val!= x)
    {
        p = p->next;
        id++;
    }
    if (p!= NULL) cout << "位置=" << id << endl;
}

find_idex函数用于查找值为x的节点在链表中的位置(从1开始计数)。从链表的第一个数据节点开始遍历,同时记录当前节点的位置id,如果找到值为x的节点,就输出其位置;如果遍历完链表都没有找到,则不输出任何信息。

(二)按位置查找值

void find_val(lst* l, int x)  //找x的位置上的值
{
    lst* p = l->next;
    int j = 1;
    while (p!= NULL && j < x)
    {
        p = p->next;
        j++;
    }
    if (x == j) cout << "值=" << p->val << endl;
}

find_val函数用于查找链表中第x个位置的节点的值。同样从链表的第一个数据节点开始遍历,用j记录当前遍历到的节点位置,当遍历到第x个节点时,输出该节点的值。

八、插入操作

//插入
void insert(lst* l, int i, int x)  //找i插入x
{
    lst* p, * s;
    int j = 0;
    while (p->next!= NULL && j < i - 1)
    {
        p = p->next;
        j++;
    }
    if (p!= NULL)
    {
        s = (lst*)malloc(sizeof(lst));
        s->val = x;
        s->next = p->next;
        p->next = s;
    }
}

insert函数用于在链表的第i个位置插入值为x的节点。首先找到第i - 1个节点(通过遍历并计数j),然后为新节点分配内存,设置新节点的值和next指针,使其插入到第i - 1个节点和第i个节点之间。需要注意的是,这里代码存在问题,while (p->next!= NULL && j < i - 1)p未初始化,应在循环前将p初始化为l

九、删除操作

//删除
void insert(lst* l, int i)
{
    lst* p, * s;
    int j = 0;
    while (p->next!= NULL && j < i - 1)
    {
        p = p->next;
        j++;
    }
    if (p!= NULL&&j == i - 1)  //删除的位置要在i之前
    {
        s = p->next;  //这个是要删的地址
        p->next = s->next;
        free(s);  //释放空间
    }
}

这里insert函数名错误,应该改为deleteNode之类的名字。此函数用于删除链表中第i个节点。先找到第i - 1个节点,然后将第i个节点从链表中移除(通过修改指针),并释放第i个节点的内存空间。同样,这里也存在p未初始化的问题,应在循环前将p初始化为l

十、总结

通过以上对单链表各种操作的实现,我们可以看到单链表在数据存储和操作上的灵活性。虽然代码中存在一些小问题,如类型判断错误、指针未初始化等,但通过理解这些操作的原理和逻辑,我们可以更好地掌握单链表这一数据结构,并且能够根据实际需求进行优化和改进。在实际应用中,单链表常用于实现栈、队列等数据结构,以及在需要动态分配内存和频繁插入删除操作的场景中发挥重要作用。


http://www.kler.cn/a/528621.html

相关文章:

  • three.js+WebGL踩坑经验合集(6.2):负缩放,负定矩阵和行列式的关系(3D版本)
  • 浅色可视化大屏虽然经常被诟病,也有自己的用武之地呀
  • 数据结构的队列
  • 51单片机入门_01_单片机(MCU)概述(使用STC89C52芯片;使用到的硬件及课程安排)
  • java后端之登录认证
  • 快速提升网站收录:利用网站内链布局
  • LightM-UNet(2024 CVPR)
  • 面试之SolrElasticsearch
  • DRM系列五:注册DRM设备--drm_dev_register
  • C++11新特性之lambda表达式
  • 类和对象(中)---默认函数
  • Linux命令入门
  • Python 模块导入问题终极解决指南
  • 土地覆盖产品批量下载(GLC_FCS30 、Esri_GLC10、 ESA_GLC10 、FROM_GLC10)
  • 深度学习 DAY3:NLP发展史
  • 网络工程师 (11)软件生命周期与开发模型
  • vscode命令面板输入 CMake:build不执行提示输入
  • Mono里运行C#脚本39—mono_jit_runtime_invoke函数
  • mac 手工安装OpenSSL 3.4.0
  • Linux02——Linux的基本命令
  • 水瓶加水时的重心变化,MATLAB计算与可视化
  • Day24 洛谷普及2004(内涵前缀和与差分算法)
  • 【上篇】-分两篇步骤介绍-如何用topview生成和自定义数字人-关于AI的使用和应用-如何生成数字人-优雅草卓伊凡-如何生成AI数字人
  • MySQL 如何深度分页问题
  • 论文阅读(十):用可分解图模型模拟连锁不平衡
  • 第25节课:前端缓存策略—提升网页性能与用户体验