单链表的实现(单链表的增删查改)

在顺序表中实现数据的增删的操作时,都要把操作位置之后的数据全部移动一遍,操作效率低下。其次是容量固定(静态顺序表),虽然在动态顺序表中容量可变,但也会造成空间上的浪费。

单链表就完美解决了上述缺点。

这里要说明一点,单链表与顺序表没有哪个比哪个更好,只有哪个更适应场合。

介绍完上述后,现在开始单链表的实现,实现单链表需要用到结构体指针。

链表中的节点都是动态开辟的空间,是在堆上的,并不会出了作用域就销毁,如果在链表中找到值为1的节点,那就把这个节点的地址返回(将体现在指定位置插入删除的操作,解决了为什么没有传址而传值的疑惑)

创建结构体:

typedef int SLTDataType;
typedef struct SListNode SLTNode;
typedef struct SListNode
{
    SLTDataType data;
    SLTNode* next;
}SLTNode;

结构体指针SLTNode*储存下一个节点,可以理解为一条链条或套娃。直到最后一个节点的next指向空(NULL)时

需要注意的是这里SLTNode我进行了前置声明因此可以直接用转换后的语句。

如果没有前置声明,STLNode不会被识别出来,这里需要说一下,操作系统在找SLTNode时只会向上找不会向向下找,这也就是为什么有许多代码中会有前置声明的函数或者像当先操作的转换一样。

能直观的看到前置声明的重要性。

创建一个头文件SListNode.h用来放需要实现的函数(声明)

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLTDataType;
typedef struct SListNode SLTNode;
typedef struct SListNode
{SLTDataType data;SLTNode* next;
}SLTNode;void SLTPrint(SLTNode* phead);//头部插入删除/尾部插入删除
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);
//销毁链表
void SListDesTroy(SLTNode** pphead);

创建SListNode.c文件用来实现头文件的函数

首先是尾插

//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
    SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    newnode->data = x;
    newnode->next = NULL;
    //不能直接这样用*pphead->next = NULL;加括号(*pphead)->next,  优先级问题
    //链表为空
    if (*pphead == NULL)
    {
        *pphead = newnode;
    }
    //链表不为空,找尾节点
    SLTNode* node = *pphead;
    while (node->next)
    {
        node = node->next;
    }
    node->next = newnode;
}

使用SLTNode** pphead接收,传递的phead是SLTNode*类型,而我们需要改变phead的内容,因此要传址而非传值,故用二级指针接收。

在这里我想用*pphead->next可是会报错,这是为什么呢,因为优先级,->的优先级比*的优先级高,pphead先与->结合,因此报错。

首先创建 节点(结构体指针newnode)保存需要插入的值,然后要判断两种情况,一种是*pphead为空时,直接让*pphead等于newnode。第二种*pphead不为空,创建一个node等于*pphead,循环向后找,找到node->next为空时,node->next = newnode。

头插的实现

//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
    SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    newnode->data = x;
    if (*pphead == NULL)
    {
        *pphead = newnode;
        newnode->next = NULL;
    }
    else
    {
        SLTNode* ret = *pphead;
        newnode->next = ret;
        *pphead = newnode;
    }
}

头插跟尾插一样需要判断两种情况,当*pphead为空和不为空的两种情况。为空直接等于,不为空创建临时的ret保存*pphead,然后让*pphead(头节点)等于newnode。

尾删的实现

//尾删
void SLTPopBack(SLTNode** pphead)
{
    assert(*pphead);
    assert(pphead);
    if ((*pphead)->next == NULL)
    {
        free(*pphead);
        *pphead = NULL;
    }
    else
    {
        SLTNode* node = *pphead;
        SLTNode* ret = node->next;

        while (ret->next)
        {
            node = node->next;
            ret = ret->next;
        }
        free(ret);
        ret = NULL;
        node->next = NULL;
    }
}

删除涉及到空间的释放。因此要判断*ppead和ppead是否为空,不能传递一个空值来删除释放吧,所以要用assert断言。

也是要判断两种情况,一种是只有一个节点时,另一种是多个节点时。当为一个节点时,直接释放后置空即可,当为多个节点时需要遍历到最后一个节点后进行删除。

头删的实现

//头删
void SLTPopFront(SLTNode** pphead)
{
    assert(*pphead);
    assert(pphead);
    if ((*pphead)->next == NULL)
    {
        free(*pphead);
        *pphead = NULL;
    }
    else
    {
        SLTNode* node = (*pphead)->next;
        free(*pphead);
        *pphead = node;
    }
}

头删和尾删还是差不多,需要判断两种情况,是否是一个节点或多个节点。一个节点直接删除即可,多个节点,将头节点指向第二个节点。

查找的实现

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
    assert(phead);
    SLTNode* node = phead;
    while (node)
    {
        if (node->data == x)
        {
            return node;
        }
        node = node->next;
    }
    return NULL;
}

void SLTfind(SLTNode* phead, SLTDataType x)
{
    SLTNode* ret = SLTFind(phead, x);
    if (ret)
    {
        printf("找到了%d\n", ret->data);
    }
    else
    {
        printf("没找到%d\n", x);
    }
}

先创建SLTFind函数返回需要查找的节点(在指定位置操作时需要用到,因此将查找节点的操作分割出来),后用SLTfind判断。

在指定位置之前插入的实现

//指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
    assert(*pphead);
    assert(pphead);
    assert(pos);
    SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    newnode->data = x;
    newnode->next = NULL;
    if (pos == *pphead)
    {
        SLTNode* node = (*pphead);
        *pphead = newnode;
        (*pphead)->next = node;
        return;
    }

    SLTNode* prev = *pphead;
    while (prev->next != pos)
    {
        prev = prev->next;
    }
    prev->next = newnode;
    newnode->next = pos;
}

链表中的节点都是动态开辟的空间,是在堆上的,并不会出了作用域就销毁,如果在链表中找到值为1的节点,那就把这个节点的地址返回

pos节点用SLTFind函数寻找,需要断言 pos是否为空,要判断*ppead和ppead是否为空。

判断两种情况,当插入节点为头节点时和不为头节点时。当插入节点为头节点时将头节点指向newnode。当不为头节点时创建prev使用while循环,当 prev->next=pos时退出,让prev的下个节点指向newnode,newnode的下个节点指向pos。

pos节点删除的实现

//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
    assert(*pphead);
    assert(pphead);
    assert(pos);

    if (pos == *pphead)
    {
        SLTNode* ret = *pphead;
        *pphead = (*pphead)->next;
        free(ret);
        ret = NULL;
        return;
    }

    SLTNode* prev = *pphead;
    while (prev->next != pos)
    {
        prev = prev->next;
    }
    SLTNode* node = pos->next;
    prev->next = node;
    free(pos);
    pos = NULL;
}

SLTFind函数寻找pos节点,判断两种情况,pos是否为头节点,然后进行删除操作。

在指定位置之后插入的实现

//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
    assert(pos);
    SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    newnode->data = x;
    newnode->next = NULL;

    SLTNode* prve = pos->next;
    pos->next = newnode;
    newnode->next = prve;
}

这个更简单。

删除pos之后的节点的实现

//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos)
{

    assert(pos);
    SLTNode* prve = pos->next;
    SLTNode* node = prve;
    while (node)
    {
        node = prve->next;
        free(prve);
        prve = node;
    }
    pos->next = NULL;
}

找到pos后循环释放。

销毁链表的实现

void SListDesTroy(SLTNode** pphead)
{
    assert(*pphead);
    assert(pphead);
    SLTNode* prve = (*pphead);
    while(prve)
    {
        prve = prve->next;
        free(*pphead);
        *pphead = NULL;
        *pphead = prve;
    }
}

一样的循环释放。

下面是SListNode.c代码

#include "SListNode.h"//打印
void SLTPrint(SLTNode* phead)
{while (phead){printf("%d->", phead->data);phead = phead->next;}printf("NULL\n");
}//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));newnode->data = x;newnode->next = NULL;//不能直接这样用*pphead->next = NULL;加括号(*pphead)->next,  优先级问题//链表为空if (*pphead == NULL){*pphead = newnode;}//链表不为空,找尾节点SLTNode* node = *pphead;while (node->next){node = node->next;}node->next = newnode;
}//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));newnode->data = x;if (*pphead == NULL){*pphead = newnode;newnode->next = NULL;}else{SLTNode* ret = *pphead;newnode->next = ret;*pphead = newnode;}
}//尾删
void SLTPopBack(SLTNode** pphead)
{assert(*pphead);assert(pphead);if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{SLTNode* node = *pphead;SLTNode* ret = node->next;while (ret->next){node = node->next;ret = ret->next;}free(ret);ret = NULL;node->next = NULL;}
}//头删
void SLTPopFront(SLTNode** pphead)
{assert(*pphead);assert(pphead);if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{SLTNode* node = (*pphead)->next;free(*pphead);*pphead = node;}
}//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{assert(phead);SLTNode* node = phead;while (node){if (node->data == x){return node;}node = node->next;}return NULL;
}void SLTfind(SLTNode* phead, SLTDataType x)
{SLTNode* ret = SLTFind(phead, x);if (ret){printf("找到了%d\n", ret->data);}else{printf("没找到%d\n", x);}
}//指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(*pphead);assert(pphead);assert(pos);SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));newnode->data = x;newnode->next = NULL;if (pos == *pphead){SLTNode* node = (*pphead);*pphead = newnode;(*pphead)->next = node;return;}SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;
}//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(*pphead);assert(pphead);assert(pos);if (pos == *pphead){SLTNode* ret = *pphead;*pphead = (*pphead)->next;free(ret);ret = NULL;return;}SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}SLTNode* node = pos->next;prev->next = node;free(pos);pos = NULL;
}//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));newnode->data = x;newnode->next = NULL;SLTNode* prve = pos->next;pos->next = newnode;newnode->next = prve;
}//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos)
{assert(pos);SLTNode* prve = pos->next;SLTNode* node = prve;while (node){node = prve->next;free(prve);prve = node;}pos->next = NULL;
}//销毁链表
void SListDesTroy(SLTNode** pphead)
{assert(*pphead);assert(pphead);SLTNode* prve = (*pphead);while(prve){prve = prve->next;free(*pphead);*pphead = NULL;*pphead = prve;}
}

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

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

相关文章

为什么很多人说考研数学不要用张宇?你要警惕的是老学长!

先看看说的是不是老学长&#xff0c;他们不了解24考情。 25考研er&#xff0c;都是用脚投票&#xff01; 一、最新数据 1. 中等基础&#xff08; “答案都懂&#xff0c;题型一变就不会做了”&#xff09; 2024年&#xff0c;67%选择武忠祥&#xff0c;23%选择张宇&#xff…

150个 HTML5 成体系的网站模版 量大慢选 持续更新中

目录 HTML5 网站模版 No.1HTML5 网站模版 No.2HTML5 网站模版 No.3HTML5 网站模版 No.4HTML5 网站模版 No.5 HTML5 网站模版 No.1 HTML5 网站模版 No.1 HTML5 网站模版 No.2 HTML5 网站模版 No.2 HTML5 网站模版 No.3 HTML5 成体系网站模版 No.3 HTML5 网站模版…

SpringCloud(一)

微服务框架 一、分布式架构 分布式架构︰根据业务功能对系统进行拆分&#xff0c;每个业务模块作为独立项目开发&#xff0c;称为一个服务。 优点: 降低服务耦合有利于服务升级拓展 微服务是一种经过良好架构设计的分布式架构方案&#xff0c;微服务架构特征: 单一职责:微…

OWASP发布大语言模型网络安全与治理清单

当前人工智能技术面临的最大风险是大语言模型&#xff08;LLM&#xff09;和生成式人工智能技术的发展和应用速度已经远远超过了安全和治理的速度。 OpenAI、Anthropic、谷歌和微软等公司的生成式人工智能和大语言模型产品的使用正呈指数级增长。与此同时&#xff0c;开源大语…

js微博发布案例

思路&#xff1a; 需求1&#xff1a;检测用户输入的字数 注册input事件 将输入文本长度赋值给对应的数值 需求2&#xff1a;输入不能为空 点击按钮之后判断 如果输入为空&#xff0c;则提示不能输入为空&#xff0c;并直接return 为了防止无意义的一些输入&#xff0c;利用字符…

Redis从入门到精通(二十)Redis最佳实践(一)优雅的Key结构、拒绝BigKey

文章目录 第7章 Redis最佳实践7.1 Redis键值设计7.1.1 优雅的Key结构7.1.2 拒绝BigKey7.1.2.1 何为BigKey7.1.2.2 BigKey的危害7.1.2.3 如何发现BigKey7.1.2.4 如何删除BigKey 7.1.3 恰当的数据类型7.1.3.1 存储Java对象7.1.3.2 存储hash数据 7.1.4 小结 第7章 Redis最佳实践 …

用html写一个窗口风景动画

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>窗边风景动画</title><link rel"stylesheet" href"./style.css"> </head> <body><!-- 窗户 -->&l…

【OpenHarmony】XTS环境配置

零、参考 1、xts测试环境配置&#xff1a;https://www.yuque.com/u25440504/ehvzki/ik2fso 2、Windows安装Python、pip、easy_install的方法&#xff1a;https://pythonjishu.com/bmxqeisbkzgrpnn/ 3、Python中easy_install 和 pip 的安装及使用&#xff1a; https://blog.c…

Vision Pro 零基础教程:1.机器视觉概述

文章目录 机器视觉简介机器视觉的发展历史机器视觉的结构组成机器视觉的应用工业相机分类1. 按传感器类型分类&#xff1a;2. 按分辨率分类&#xff1a;3. 按扫描方式分类&#xff1a;4. 按输出信号类型分类&#xff1a;5. 按应用领域分类&#xff1a;6. 按接口类型分类&#x…

springSecurity-记住我(Remember me)

一.记住我概述 Remember me(记住我)记住我&#xff0c;当用户发起登录勾选了记住我&#xff0c;在一定的时间内再次登录就不用输入用户名和密码了&#xff0c;即使浏览器退出重新打开也是如此。 二.流程分析 在SpringSecurity中提供RememberMeAuthenticationFilter过滤器来实…

day10 | 栈与队列 part-2 (Go) | 20 有效的括号、1047 删除字符串中的所有相邻重复项、150 逆波兰表达式求值

今日任务 20 有效的括号 (题目: . - 力扣&#xff08;LeetCode&#xff09;)1047 删除字符串中的所有相邻重复项 (题目: . - 力扣&#xff08;LeetCode&#xff09;)150 逆波兰表达式求值 (题目: . - 力扣&#xff08;LeetCode&#xff09;) 20 有效的括号 题目: . - 力扣&…

Redis入门到通过之解决Redis缓存击穿、缓存穿透、缓存雪崩

文章目录 ☃️缓存击穿❄️❄️解决方案一、使用锁来解决&#xff1a;❄️❄️解决方案二、逻辑过期方案❄️❄️解决方案三、永不过期 主动更新❄️❄️解决方案四、接口限流❄️❄️实战❄️❄️❄️利用互斥锁解决缓存击穿问题❄️❄️❄️利用逻辑过期解决缓存击穿问题 ☃️…

Python-VBA函数之旅-float函数

目录 1、float函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、相关文章&#xff1a; 个人主页&#xff1a;https://blog.csdn.net/ygb_1024?spm1010.2135.3001.5421 float函数在 Python 中的实际应用场景非常广泛&#xff0c;几乎涉及到任何需要处理…

人才测评的方法有哪些?

人才测评是企业在筛选人才的时候必然会使用的策略&#xff0c;为了节省企业HR在招聘时的成本&#xff0c;又极大提高了人才和岗位的匹配度&#xff0c;从企业发展和员工个人发展来看&#xff0c;起到了双赢的作用&#xff0c;在线人才测评是现代企业招聘&#xff0c;人才选拔&a…

递归、搜索与回溯算法——二叉树的深搜

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章与大家分享分治递归、搜索与回溯算法中关于二叉树的深搜的专题 如果有不足的或者错误的请您指出! 目录 1.计算布尔值的二叉树1.1解析1.2题解 2.求根节点到叶子节点数字之和2.1解析2.2题解…

【漏洞复现】泛微E-Office jx2_config 存在信息泄露漏洞

0x01 阅读须知 “如棠安全的技术文章仅供参考&#xff0c;此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等&#xff08;包括但不限于&#xff09;进行检测或维护参考&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供…

超声波洗眼镜机有用吗?哪些洗眼镜值得入?超声波洗眼镜好不好用

在日常生活中&#xff0c;眼镜不仅是视力不佳者的重要辅助工具&#xff0c;更是时尚搭配的一部分。然而&#xff0c;长时间佩戴眼镜会不可避免地积累各种污垢和细菌&#xff0c;从油脂、指纹到灰尘等&#xff0c;这些不仅影响视觉效果&#xff0c;更有可能对眼部健康造成潜在威…

云服务器安装Mysql、MariaDB、Redis、tomcat、nginx

前置工作 进入根目录 cd / 都在/usr/local/src文件夹&#xff09; 上传压缩包 rz 压缩包 Mysql 1.下载并安装MySQL官方的 Yum Repository wget http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm rpm -ivh mysql-community-release-el7-5.noarch.rpm yum…

黑马程序员——mysql——day05——反射、注解、动态代理

目录&#xff1a; 类的加载 目标讲解 类的加载过程类的加载机制小结类加载器 目标讲解 类加载器的作用类加载器的分类&#xff1a;获取类加载器的方式小结双亲委派机制 目标讲解 3种类加载器的关系双亲委派机制小结反射:概述 目标讲解 反射反射技术的应用案例&#xff1a;反射…

Python实现exe小工具

1、实例代码 import tkinter as tk from tkinter import messagebox from tkinter import ttk import requestsdef submit():input_text entry.get()if len(input_text) 0:messagebox.showinfo("提示", "请输入您所要提问的问题&#xff01;")returnsel…