单链表相关操作(插入,删除,查找)

news2024/11/22 22:02:17

通过上一节我们知道顺序表的优点:

可随机存储(O(1)):查找速度快

存储密度高:每个结点只存放数据元素,而单链表除了存放数据元素之外,还需存储指向下一个节点的指针

http://t.csdn.cn/p7OQf

但是顺序表也有明显的缺点,就是需要大片的连续空间,改变容量不方便,所以出现了单链表

目录

一.初始化单链表

二.插入结点

1.带头结点的插入

2.不带头结点的插入:不存在第’0‘个结点,因此i=1时,需要特殊处理

补充:带头结点

指针结点的后插操作

指针的前插操作

三.删除结点

1.带头节点的删除

2.不带头节点的删除

3.删除指定节点

四.单链表的查找

1.按位查找:查找第i个节点的值

2.按值查找:查找链表中是否有元素e

补充:求表的长度


一.初始化单链表

typedef struct LNode{
    ElemType data;    //每个节点存放一个数据元素
    struct LNode *next;//指针指向下一个节点
}LNode,*LinkList;
//这里的LinkList==》typedef struct LNode *LinkList,定义一个指向结构体的指针
//在这里
//LNode *L与LinkList L;//都是表示指向单链表第一个节点的指针
//LNode *L强调的是一个节点
//LinkList L强调的是一个单链表

/*不带头节点的单链表
bool InitList(LinkList &L)
{
    L=NULL;//防止空间中存在脏数据
    return true;

}

bool Empty(LinkList L)
{
    return (L=NULL);
}

void test()
{
    LinkList L;
    InitList(L);

}
*/

//带头结点的单链表
LinkList InitList(LinkList &L)
{
    L=(LNode *)malloc(sizeof(LNode));//分配一个头结点
    if(L==NULL)
        return NULL;
    L->next=NULL;//声明一个指向单链表的指针
    return L;

}

bool Empty(LinkList L)
{
    if(L->next==NULL)
    {
        return true;
    }    
    else
        return false;

}
//我们可以把头结点看作第0个结点,这样写代码更加方便

二.插入结点

1.带头结点的插入

bool ListInsert(LinkList &L,int i,ElemType e)//i表示插入的位置,e表示插入的元素
{
    if(i<1)
    {
        return false;
    }
    LNode *p;//指针p指向当前扫描到的结点
    int j=0;//当前p指向的是第几个结点
    p=L;    //指向头节点,头节点是第0个节点
    while(p!=NULL && j<i-1)//循环找到第i-1个结点
    {
        p=p->next;
        j++;
    }
    if(p==NULL)
    {
        return false;
    }
    LNode *s=(LNode *)malloc(sizeof(LNode));
    s->data=e;
    s->next=p->next;
    p->next=s;
    return true;

}

若先p->next=s,再s->next=p->next,即

 带头结点的插入

最好情况:插在表头O(1)

最坏情况:插在表尾O(n)

平均情况:O(n)

2.不带头结点的插入:不存在第’0‘个结点,因此i=1时,需要特殊处理

bool ListInsert(LinkList &L,int i,ElemType e)
{
    if(i<1)
        return false;
    if(i==1)//插入第1个结点的操作与其他节点操作不同    
    {
        LNode *s=(LNode *)malloc(sizeof(LNode));
        s->data=e;
        s->next=L;
        L=s;//头指针指向新结点
        return true;
    }
    LNoden *p;
    int j=1;//除了这里j=1和带头结点的指针不同,其他是相同的
    p=L;
    while(p!=NULL && j<i-1)
    {
        p=p->next;
        j++;
    }

    if(p==NULL)
    {
        return false;
    }
    LNode *s=(LNode *)malloc(sizeof(LNode));
    s->data=e;
    s->next=p->next;
    p->next=s;
    return true;
}

删除第一个元素时,需要更改头指针L 

补充:带头结点

指针结点的后插操作

//在p结点之后,插入元素e
bool InsertNextNode(LNode *p,ElemType e)
{
    if(p==NULL)
        return false;
    LNode *s=(LNode *)malloc(sizeof(LNode));
    if(s=NULL)//如果内存分配失败,如内存不足等
        return false;
    s->data=e;
    s->next=p->next;    
    p->next=s;
    return true;

}

 指针的前插操作

1.第一种方法:通过前驱节点完成前插操作

需要传入头节点才能找到p的前驱结点,即

bool InsertPriorNode(LinkList L,LNode *p,ElemType e)

bool InsertPriorNode(LinkList L,LNode *p,ElemType e)
{
    if(p==NULL)
        return false;
    LNode *s=(LNode*)malloc(sizeof(LNode));
    if(s==NULL)
        return false;
    LNode *current=L;
    while(current->next!=p)
        current=current->next;
    s->data=e;
    s->next=current->next;
    current->next=s;
    return true;
}

 在这里时间复杂度为O(n),如何减小时间复杂度,可以用第二种方法

2.第二种方法:通过结点的数据交换完成前插操作

bool InsertPriorNode(LNode *p,ElemType e)
{
    if(p==NULL)
        return false;
    LNode *s=(LNode *)malloc(sizeof(LNode));
    if(s==NULL)
        return false;
    s->next=p->next;
    p->next=s;    //新节点s连到p之后
    s->data=p->data;//将p中元素复制到s中
    p->data=e;//p中元素覆盖为e
        return true;

}

 关键在于s->data=p->data;p->data=e;这样实现了两节点的数据交换,实现了在p结点前插入e元素,同时这里的时间复杂度是O(1)

所以对于插入节点可以观察到如下两个规律

1.先后再前

s->next=p->next;

p->next=s;

2.先小后大

在第一个节点插入

s->next=L;

L=s        //头指针指向新插入的节点s 

三.删除结点

1.带头节点的删除

bool ListDelete(LinkList &L,int i,ElemType &e)
{
    if(i<1)
    {
        return false;
    }
    LNode *p=L;
    int j=0;
    while(p!=NULL && j<i-1)//这里是寻找要删除结点的前驱结点
    {
        p=p->next;
        j++;
    }    
    if(p==NULL)
        return false;
    if(p->next==NULL)//如果要删除结点的前驱结点为NULL,那么删除就无意义了
        return false;
    LNode *q=p->next;//令q指向被删除结点
    e=q->data;//用e返回元素的值
    p->next=q->next;//将*q结点从链表中断开
    free(q);//释放q
    return true;
}

最好时间复杂度为O(1):删除第一个节点

最坏和平均时间复杂度为O(n)

2.不带头节点的删除

bool ListDelete(LinkList& L, int i, ElemType& e) {
    if (i < 1)
        return false;
    if (i == 1) {    
        LNode* p = L;
        L = L->next;
        free(p);
        return true;
    }
    LNode* p = L;
    int j = 1;
    while (p != NULL && j < i - 1) {//寻找删除节点的前驱节点
        p = p->next;
        j++;
    }

    if (p == NULL || p->next == NULL)
        return false;
    LNode* q = p->next;
    e = q->data;
    p->next = q->next;
    free(q);
    return true;
}

 3.删除指定节点

//删除指定节点p
bool DeleteNode(LNode *p)
{
    if(p==NULL)
        return false;
    LNode *q=p->next;    //    令q指向*p的后继节点
    q->data=p->next->data;
    p->next=q->next;//将*q节点从链中断开
    free(q);//释放后继节点的存储空间
    return true;

}
//如果p的后继节点刚好为NULL,那么p->next->data指向空所以这段代码对此情况不适用
//针对此情况还是要找到其前驱节点,然后进行删除

找前驱节点进行删除

bool DeleteNode(LinkList L, LNode* p, ElemType& e) {
    if (p == NULL)
        return false;
    LNode* q = L;
    while (q->next != p)
        q = q->next;
    q->next = NULL;
    e = p->data;
    free(p);
    return true;
}

四.单链表的查找

1.按位查找:查找第i个节点的值

LNode *GetElem(LinkList L,int i)
{
    if(i<0)
        return false;
    LNode *p=L;
    int j=0;
    while(p!=NULL && j<i)//循环找到第i个节点
    {
        p=p->next;
        j++;
    }
    return p;

}

平均时间复杂度O(n)

2.按值查找:查找链表中是否有元素e

LNode *LocateElem(LinkList L,ElemType e)
{
   LNode *p=L->next;
   //从第一个节点开始查找数据域为e的节点
   while(p!=NULL && p->data!=e)
        p=p->next;
    return p;//找到后返回该节点的指针,否则为NULL
}

平均时间复杂度O(n)

补充:求表的长度

int length(LinkList L)
{
    int len=0;
    LNode *p=L;
    while(p!=NULL){
        p=p->next;
        len++;
    }
    return len;

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/879989.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机组成原理之浮点运算

1、浮点运算步骤 ①0操作数的检查&#xff0c;看有无简化操作的可能。 ②比较阶码大小并完成对阶&#xff08;小阶向大阶对齐&#xff09;。 ③尾数进行加或减运算。 &#xff08;在进行尾数加减前&#xff0c;浮点运算器会自动把原码形式的尾数转换成补码形式。&#xff09; ④…

像素相关知识

物理像素 指的是物理设备上真实的小方块个数&#xff0c;就是拿放大镜看屏幕时看到的像素点&#xff0c; 每个物理像素具体的大小是不固定的&#xff0c;不同设备不相同&#xff0c;由厂家设置 逻辑像素 指的就是我们css用到的px这个单位的像素 像素比&#xff08;DPR&…

GB/GMP法规内容对沉降​菌、浮游菌监测​点设置相关规定及要求详解

微生物鉴定一直是生物、食品、医药等领域非常重要的监控指标&#xff0c;可以帮助预测药品生产过程以及无菌生产环境的风险。对于许多企业而言&#xff0c;建立厂房环境微生物菌种库对于微生物污染控制来说非常关键&#xff0c;环境菌种库的建立可以帮助制定洁净区消毒灭菌程序…

Leetcode40 组合之和2

给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用 一次 。 注意&#xff1a;解集不能包含重复的组合。 解题思路&#xff1a;回溯剪枝 代码&#xff…

LeetCode--HOT100题(31)

目录 题目描述&#xff1a;25. K 个一组翻转链表&#xff08;困难&#xff09;题目接口解题思路代码 PS: 题目描述&#xff1a;25. K 个一组翻转链表&#xff08;困难&#xff09; 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表…

麦肯锡重磅发布2023年15项技术趋势,生成式AI首次入选,选对了就是风口

两位朋友在不同群里分享了同一份深度报告。 一位是LH美女&#xff0c;她在“AIGC时代”群里上传了这份文档&#xff0c;响应寥寥&#xff0c;可能是因为这些报告没有像八卦文那样容易带来冲击。 你看韩彬的这篇《金融妲己&#xff1a;基金公司女销售的瓜&#xff0c;一个比一个…

Ingress企业实战:金丝雀与蓝绿发布篇

背景 现如今&#xff0c;越来越多的应用采用了微服务架构&#xff0c;这也导致了应用数量相比传统模式更多&#xff0c;管理更加复杂&#xff0c;发布更加频繁&#xff0c;如果直接将新版本上线发布给全部用户。一旦遇到线上事故&#xff08;或BUG&#xff09;&#xff0c;对用…

哪些客户适合打Cold Call?

都知道Cold Call是外贸营销中一种重要的销售方式&#xff0c;相比邮件跟进&#xff0c;它可以可实时交流&#xff0c;获取需求&#xff0c;解决问题&#xff0c;展示人情味&#xff0c;提升好感度&#xff0c;提高转化率。但由于陌生性&#xff0c;当下如果没有需求的话&#x…

“万恶”之源的KieServices,获取代码就一行,表面代码越少里面东西就越多,本以为就是个简单的工厂方法,没想到里面弯弯绕绕这么多东西

Drools用户手册看了得有一段时间了&#xff0c;现在开始看源码了&#xff0c;因为每次使用drools都会看见这么一段代码&#xff1a; 代码段1 起手代码 KieServices ks KieServices.Factory.get(); 那我就从这段代码出发开始研究drools的源码吧&#xff0c;这么一小段代码起初…

文件操作/IO

文件 文件是一种在硬盘上存储数据的方式&#xff0c;操作系统帮我们把硬盘的一些细节都封装起来了&#xff0c;程序员只需要了解文件相关的接口即可&#xff0c;相当于操作文件就是间接的操作硬盘了 硬盘用来存储数据&#xff0c;和内存相比硬盘的存储空间更大&#xff0c;访问…

代码运行出现:No module named ‘torch_geometric‘

这是没有torch_geometric库导致的&#xff0c;但是不能像一般库安装‘pip install 库名’或者‘conda install 库名’进行安装&#xff0c;经常会报错!!! 需要先安装四个小部件再安装torch_geometric&#xff0c;具体安装步骤如下&#xff1a; step 1&#xff1a;查看自己环境…

简约时尚的健康手表,智能守护每一刻,dido Y60上手

智能手表是现在很流行的一种智能设备&#xff0c;很多品牌都推出了各种各样的产品&#xff0c;但是大部分都更侧重功能和运动的方面&#xff0c;健康监测往往只是配角&#xff0c;而随着人们对自己的健康越来越重视&#xff0c;有些朋友只是单纯的需要一块专业的健康监测手表。…

SIFT 算法 | 如何在 Python 中使用 SIFT 进行图像匹配

介绍 人类通过记忆和理解来识别物体、人和图像。你看到某件事的次数越多,你就越容易记住它。此外,每当一个图像在你的脑海中弹出时,它就会将该项目或图像与一堆相关的图像或事物联系起来。如果我告诉你我们可以使用一种称为 SIFT 算法的技术来教机器做同样的事情呢? 尽管…

部署私有知识库项目FastGPT

FastGPT 是一个基于 LLM 大语言模型的知识库问答系统&#xff0c;提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排&#xff0c;从而实现复杂的问答场景。 项目源码&#xff1a; GitHub - c121914yu/FastGPT: A platform that uses the Open…

【推荐】7个可以改变我们在3dMax中工作方式的插件

​以下给大家介绍的这些插件&#xff0c;将改变我们在3d Max中的工作方式&#xff0c;让生活更加轻松。 Glue Utility&#xff08;粘合实用程序&#xff09; 这些年来&#xff0c;这个工具改变了我的游戏规则。它使我能够执行许多任务&#xff0c;否则这些任务要么是不可能…

在矩池云使用 ChatGLM2-6B ptuning

本文参考 ChatGLM2-6B 官方文档&#xff0c;在矩池云复现了对于 ChatGLM2-6B 模型基于 P-Tuning v2 的微调。P-Tuning v2 将需要微调的参数量减少到原来的 0.1%&#xff0c;再通过模型量化、Gradient Checkpoint 等方法&#xff0c;最低只需要 7GB 显存即可运行。 官方文档地址…

一百五十六、Kettle——Linux上安装的Kettle9.3连接ClickHouse数据库(亲测,附流程截图)

一、目标 kettle9.3在Linux上安装好后&#xff0c;需要与ClickHouse数据库建立连接 二、前提准备 &#xff08;一&#xff09;在Linux已经安装好kettle并可以启动kettle &#xff08;二&#xff09;已知kettle和ClickHouse版本 1、kettle版本是9.3 2、ClickHouse版本是21…

深度学习优化器

1、什么是优化器 优化器用来寻找模型的最优解。 2、常见优化器 2.1. 批量梯度下降法BGD(Batch Gradient Descent) 2.1.1、BGD表示 BGD 采用整个训练集的数据来计算 cost function 对参数的梯度&#xff1a; 假设要学习训练的模型参数为W&#xff0c;代价函数为J(W)&#xff0c;…

项目管理工具探析:详细介绍四种常用选择

市场上的项目管理工具&#xff0c;主要是解决项目计划制定、任务协作、文档协作这几方面的问题&#xff0c; 下面简单聊聊一些自己用过的工具&#xff1a; 1、Excel/在线协作表格 如果项目简单&#xff0c;任务数少&#xff0c;没什么依赖&#xff0c;那么就可以用Excel来做项目…

静电放电发生器的操控模式和释放模式有哪些方式

静电放电是一种自然现象 &#xff0c;当两种不同介电强度的材料相互摩擦时&#xff0c;就会产生静电电荷&#xff0c;当其中一种材料上的静电荷积累到一定程度&#xff0c;在与另外一个物体接触时&#xff0c;就会通过这个物体到大地的阻抗而进行放电。静电放电及影响是电子设备…