C语言实现单链表(超多配图,这下不得不学会单链表了)
目录
一:什么是链表?
二:创建源文件和头文件
(1)头文件
(2)源文件
三:实参和形参
四:一步步实现单向链表
(1)建立一个头指针并置空
(2)打印链表,便于观察测试
(3)创建一个新的结点
(4)尾部插入数据
(5)头部插入
(6)尾部删除
(7)头部删除
(8)查找
(9)指定位置插入
(10)指定删除
(11)清空链表
(12)最终代码
SingleLinkedList.h
SingleLinkedList.c
text.c
五:小结
一:什么是链表?
我们先看下面这个结构体。
这个结构体存储数据的同时保存了一个结构体指针。
链表其实就是一个个结构体(后文把这样的一个结构体称为结点)通过保存地址的方式找到下一个结构体,最后一个结构体保存的地址为空。
链表的两种实现方式
(1)带头结点
(2)不带头结点
区别:带头结点有一个哨兵结点,这个节点作为第一个节点,它的数据域不存储数据。
两者各有利弊:我们进行结点删除时需要用到待删除结点的前一个结点。对于没有哨兵的单链表,当链表中只存在一个节点,需要进行单独处理。从而代码的复杂性增加。但如果设计了哨兵结点,则第一个结点的处理与其他结点一致。但处理链表数据时这个哨兵结点属于无效数据,我们需要规避这个数据,也需要进行处理。
本文选择的是无哨兵链表。
二:创建源文件和头文件
(1)头文件
头文件SingleLinkedList.h用来包含一些必要的头文件,声明函数以及定义结构体。
(2)源文件
源文件SingleLinkedList.c用来实现链表的具体功能。
源文件text.c用来对各个功能进行测试。
三:实参和形参
在实现链表之前,我们需要先深入的认识一下实参和形参的关系。
我们看下面这个代码:
我们可以看到a的值并没有发生变化,那我们如果传入a的地址进行解引用呢?我们看下面这个代码。
我们可以看到a成功被修改为了5,但这是为什么呢?
答:其实在传入参数的时候系统临时开辟了一块空间用来接收数据,函数调用结束时这一块空间就会被释放,这意味着如果我们直接传入a的值,我们只是在对这一块临时开辟的空间内的数据进行修改,无论如何都不会影响到a,但如果我们传入的是a的地址,对a进行解引用就能直接找到并修改a。
图解:
这是否意味着只要我们传入的是地址就一定能改变实参的值呢?我们看下面这个代码。
我们发现虽然传入的是地址,但p依旧指向a[0],并没有改变。这是为什么呢?
答:与前面的原理一致,我们传入p的时候也临时开辟了一片空间来保存p的值,无论我们怎么改变p值,在函数调用结束后这片空间会被释放,所以p实际上还是指向a[0]的。
图解:
那如果我们传入p的地址,是不是就能改变p了呢?我们看代码。
图解:
![]()
四:一步步实现单向链表
(1)建立一个头指针并置空
struct SListNode* head = NULL;
(2)打印链表,便于观察测试
我们用头指针的地址是否为空为循环条件。
我们可以分成两种情况讨论,如果链表为空,我们不进行遍历,直接打印NULL。
如果链表中有元素,从头指针(第一个结点)开始,我们打印结点数据,并让头指针指向下一个结点,一直到NULL。代码:
图解(以有三个结点为例子):
(3)创建一个新的结点
只要插入新结点,我们就一定要生成新的结点,我们可以把生成新结点的功能单独封装成函数BuyListNode()。
代码:
(4)尾部插入数据
进行数据插入,我们要改变实参的值(即改变指针的指向),必须传入头指针的地址(二级指针)。
基础思路:【1】在进行数据插入之前,我们要先生成一个新的结点。
【2】要进行尾部插入,我们需要找到链表的最后一个结点,并将它存储的指针指向新生成的结构体。
【3】我们设计一个指针tail来找尾部结点,如果tail->next为NULL,我们就找到了尾部结点,结束循环。
图解:
现阶段代码:
我们对代码进行测试:
可以发现程序崩溃了,这是为什么呢?
答:这是因为我们没有考虑链表为空的情况,如果链表为空,我们会直接对空指针进行解引用,导致程序崩溃。
为了解决这个问题,我们可以对这种情况进行单独处理。
代码:
再次测试,观察结果。
我们发现数据成功插入了。
(5)头部插入
进行数据插入,我们要改变实参的值(即改变指针的指向),必须传入头指针的地址(二级指针)。
思路:头部插入我们只需要让头指针指向新结点,让新结点的指针域指向原来的头结点。
代码:
前面进行尾部插入的时候需要考虑链表为空的情况,那头部插入需不需要单独进行这个临界条件的处理呢?
图解:
我们可以发现最后结点的指针域会指向空,所以不需要考虑这个临界情况。
(6)尾部删除
进行数据插入,我们要改变实参的值(即改变指针的指向),必须传入头指针的地址(二级指针)。
思路:和尾部插入一样我们需要使用一个tail指针找到尾部结点(方法与前面一致),然后释放这个结点。
我们看代码和运行结果:
我们发现程序打印的是随机数,这是为什么呢?
答:因为我们释放最后一个结点的时候上一个结点的指针域没有指空,但空间已经被系统回收了,此时我们进行指针的引用是非法的,也就是我们常说的野指针。
解决方案:我们可以设计一个指针prev来记录倒数第二个结点,在释放尾结点后让倒数第二个结点的指针域指向空。
但此时程序依然存在不足。
如果链表为空,我们就会对空指针进行解引用,所以我们需要单独处理这种情况,这里提供两种解决方案
第一,我们可以直接返回空。
第二,我们可以使用断言让程序直接报错。这里使用第一种方法。
代码和测试结果:
(7)头部删除
进行数据插入,我们要改变实参的值(即改变指针的指向),必须传入头指针的地址(二级指针)。
思路:进行头部删除,可以将第一个结点释放,然后让头指针指向第二个结点。
代码和运行结果:
(8)查找
查找有两种实现方式,一种返回结点地址,一种返回数据在第几个结点。
思路:遍历整个链表,一直到找到要查找的数据或最后一个结点为止。
如果没有查找到数据,返回NULL或者0。第一种(我用的):
第二种:
上述函数我们只能找到第一个数,后面相同的找不到,如果我们需要查找链表中所有该数的位置 ,我们可以设计一个pos指针并进行循环,循环结束条件为pos为空,这样就可以实现多次查找,我们看代码。(这个方法只适用于第一种)
(9)指定位置插入
这里给出两种插入方式,一种是指定在那个结点前插入,一种是指定在那个结点后插入
第一种前插入:
第一种后插入:
在进行插入前我们需要找到要插入的那个结点的前面(后面),可以先使用查找函数找到位置,在进行插入。
测试结果:
(10)指定删除
思路:删除的思路同尾删类似,我们需要找到待删除的结点并保存待删除的结点的指针域。
代码:
在进行删除前我们需要找到要删除的那个结点的前一个结点(目的是让前面的结点指针域指向下下个结点),可以先使用查找函数找到位置,再进行删除。
测试:
(11)清空链表
思路:从第一个结点开始,设计一个pos指针,每次循环把头指针指向的结点地址赋给pos,让头指针指向下一个结点地址,调用free()释放pos指向的结点。
图解(以三个结点为例子):
代码和测试结果:
(12)最终代码
SingleLinkedList.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
//#include <assert.h> 要使用断言的话注意包含头文件//结构体数据类型重定义,方便我们更改要存储的元素类型
typedef int SLTDataType;struct SListNode
{SLTDataType data; //要存储的数据(数据域)struct SListNode* next; //用来存储下一个结构体的地址(指针域)
};//打印
void SListPrint(struct SListNode* phead);
//创建一个新节点
struct SListNode* BuyListNode(SLTDataType x);
//尾部插入
void SListPushBack(struct SListNode** pphead, SLTDataType x);
//头部插入
void SListPushFront(struct SListNode** pphead, SLTDataType x);
//尾部删除
void SListPopBack(struct SListNode** pphead);
//头部删除
void SListPopFront(struct SListNode** pphead);
//查找,返回对应结点地址
//int SListFind(struct SListNode* phead, SLTDataType x);
struct SListNode* SListFind(struct SListNode* phead, SLTDataType x);
//指定插入(还有一种按输入位置来插入)(在前面插入)
void SListInsertF(struct SListNode** pphead, struct SListNode* pos, SLTDataType x);
void SListInsertB(struct SListNode** pphead, struct SListNode* pos, SLTDataType x);//指定删除
void SListEarse(struct SListNode** pphead, struct SListNode* pos);
//销毁链表
void SListDestory(struct SListNode** pphead);
SingleLinkedList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "SingleLinkedList.h"//打印链表
void SListPrint(struct SListNode* phead)
{//一直循环,直到找到最后一个结点while (phead){printf("%d-> ", phead->data); //依次打印结点存储的数据phead = phead->next; //让phead指向下一个结点}printf("NULL\n");
}//生成新节点
struct SListNode* BuyListNode(SLTDataType x)
{//调用maoolc()函数生成一个结点struct SListNode* newNode = (struct SListNode*)malloc(sizeof(struct SListNode));//如果申请失败,打印错误并结束程序if (newNode == NULL){printf("malloc error\n");exit(-1);}//将要插入的数据赋给新结点newNode->data = x;//新节点的next置空newNode->next = NULL;//返回生成的结点的地址return newNode;
}//尾部插入
void SListPushBack(struct SListNode** pphead, SLTDataType x)
{//生成一个新的结点struct SListNode* newnode = BuyListNode(x);//如果链表为空,直接把新结点地址赋给*ppheadif (*pphead == NULL){*pphead = newnode;}else{ //设置一个指针tail用来找到尾部结点struct SListNode* tail = *pphead;//不断循环,直到找到尾部结点while (tail->next){tail = tail->next;//指向下一个结点}//让原本置空的指针指向新生成的结点tail->next = newnode;}
}//头部插入
void SListPushFront(struct SListNode** pphead, SLTDataType x)
{//生成新结点struct SListNode* newnode = BuyListNode(x);//保存原来第一个结点的地址struct SListNode* prev = *pphead;//让头指向新结点*pphead = newnode;newnode->next = prev;
}//尾部删除
void SListPopBack(struct SListNode** pphead)
{//如果链表为空,就直接返回空,也可以使用assert(*pphead!=NULL)if (*pphead == NULL){ return;}//如果只有一个结点if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{//找尾部struct SListNode* tail = *pphead;//记录尾部的前一个结点的地址struct SListNode* prev = NULL;//找尾部结点,并保存尾部结点的前一个结点的地址while (tail->next){prev = tail;tail = tail->next;}//找到尾部结点,释放free(tail);//置空tail = NULL;//把尾部的前一个结点保存的地址置空prev->next = NULL;}
}//头部删除
void SListPopFront(struct SListNode** pphead)
{//如果链表为空,返回空,也可以使用assert(*pphead!=NULL)if (*pphead == NULL){return;}else{//找到下一个结点的地址struct SListNode* prev = (*pphead)->next;//释放第一个结点free(*pphead);//头指针指向第二个结点*pphead = prev;}
}//查找
struct SListNode* SListFind(struct SListNode* phead, SLTDataType x)
{struct SListNode* cur = phead;//循环查找while (cur){//找到返回该结点地址if (cur->data == x){return cur;}//没找到指向下一个结点else{cur = cur->next;}}//如果没找到,返回NULLreturn NULL;
}
//第二种
//int SListFind(struct SListNode* phead, SLTDataType x)
//{
// //记录第几个结点
// int i = 1;
// struct SListNode* cur = phead;
// //循环查找
// while (cur)
// {
// //找到返回该结点地址
// if (cur->data == x)
// {
// return i;
// }
// //没找到指向下一个结点,i加1
// else
// {
// i = i + 1;
// cur = cur->next;
// }
// }
// //如果没找到,返回0
// return 0;
//}//指定结点前插入
void SListInsertF(struct SListNode**pphead,struct SListNode* pos,SLTDataType x)
{//生成一个新的结点struct SListNode* newnode = BuyListNode(x);//只有一个结点或者链表为空,进行头插if (*pphead == pos){newnode->next = *pphead;*pphead= newnode;}else{//设计一个结构体指针来找pos的前一个结点struct SListNode* posprev = *pphead;while (posprev->next != pos){posprev = posprev->next;}posprev->next = newnode;newnode->next = pos; }
}
//指定结点后插入
void SListInsertB(struct SListNode**pphead,struct SListNode* pos,SLTDataType x)
{//生成一个新的结点struct SListNode* newnode = BuyListNode(x);//新结点指针域指向该结点的后一个newnode->next = pos->next;//结点的指针域指向新结点pos->next = newnode;
}//指定位置删除
void SListEarse(struct SListNode** pphead, struct SListNode* pos)
{//如果链表为空,返回空,也可以使用assert(*pphead!=NULL)if (*pphead == NULL){return;}//要删除的结点是第一个结点if (pos == *pphead){//找到下一个结点的地址struct SListNode* prev = (*pphead)->next;//释放第一个结点free(*pphead);//头指针指向第二个结点*pphead= prev;}else{//要找到pos结点的前一个结点位置struct SListNode* posprev = *pphead;while (posprev->next != pos){posprev = posprev->next;}//让posprev的指针域指向下下个结点posprev->next = pos->next;//释放结点pos的空间free(pos);pos= NULL;}
}//清空链表
void SListDestory(struct SListNode** pphead)
{struct SListNode* prev = *pphead;while ((*pphead)!= NULL){//找到头指针指向的结点prev = *pphead;//让头指针指向下一个结点*pphead = (*pphead)->next;//释放前面的结点free(prev);}
}
text.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "SingleLinkedList.h"void text1()
{struct SListNode* head = NULL;SListPushFront(&head, 5);SListPushFront(&head, 50);SListPushFront(&head, 50);SListPushFront(&head, 5);struct SListNode* pos = SListFind(head, 50);int i = 1;//查找多个相同的值while (pos){printf("第%d个pos节点:%p->%d\n", i++, pos, pos->data);//pos指向目标结点的下一个结点pos = SListFind(pos->next, 50);}SListPrint(head);//修改pos = SListFind(head, 50);if (pos){pos->data = 30;}SListPrint(head);}void text2()
{struct SListNode* head = NULL;//插入SListPushBack(&head, 2);SListPushBack(&head, 5);SListPushBack(&head, 15);//查找14位置struct SListNode* pos = SListFind(head, 14);//判断是否有14if (pos == NULL){printf("没有该数据\n");}else{//删除14SListEarse(&head, pos);}SListPrint(head);//清空SListDestory(&head);//插入SListPushBack(&head, 2);SListPushBack(&head, 5);SListPrint(head);
}int main()
{/*text1();*/text2();return 0;
}
五:小结
相较于顺序表,链表能够更好的利用零散的空间,并且插入数据不需要大量移动数据,但是单链表在物理层上不是连续存储的,我们只能前找后却无法后找前,而且一旦指针域的数据丢失我们就没法找到后续结点,后续的双向链表可以很好的解决这个问题。
顺序表讲解链接:http://t.csdn.cn/V96aI
相关文章:

C语言实现单链表(超多配图,这下不得不学会单链表了)
目录 一:什么是链表? 二:创建源文件和头文件 (1)头文件 (2)源文件 三:实参和形参 四:一步步实现单向链表 (1)建立一个头指针并置空 (2)打印链表,便于…...

SQL编写优化技巧
一、底层原理 sql慢是因为没有走索引,因此需要添加索引然它走索引联合索引需要匹配最左匹配原则(索引回表)如果查询列超出索引的key, 会导致回表,回表数量多,则会走全表扫描 索引是分聚集索引、非聚集索引…...

【基础算法】单链表的OJ练习(6) # 复制带随机指针的链表 #
文章目录🍇前言🍎复制带随机指针的链表🍑写在最后🍇前言 本章的链表OJ练习,是最后的也是最难的。对于本题,我们不仅要学会解题的思路,还要能够通过这个思路正确的写出代码,也就是思路…...

Activity生命周期完成EvenetLog回调
Activity 生命周期 系统EvenetLog回调 EventLog路径: Android13/frameworks/base/core/java/android/app/EventLogTags.logtags wm_on_create_called wm_on_restart_called wm_on_start_called wm_on_resume_called wm_on_top_resumed_gained_called wm_on_top_resumed_lost_c…...

西安石油大学C语言期末真题实战
很简单的一道程序阅读题,pa’默认为a【0】,接下来会进行3次循环 0 1 2 输出结果即可 前3题就是一些基础定义,在此不多赘述 要注意不同的数据类型的字节数不同 a<<2 b>>1(b>>1;就是说b自身右位移一位(…...

【Shell】Shell变量
Shell变量系统预定义变量自定义变量基本语法定义变量撤销变量命名规则使用变量只读变量删除变量变量类型系统预定义变量 $HOME、$PWD、$SHELL、$SUSER等 实例 yysubuntu:~$ echo $HOME #查看系统变量的值 /home/yys yysubuntu:~$ set #显示当前shell中所有变量自定义变量…...

你是真的“C”——结构体中鲜有人知的“秘密”
你是真的“C”——结构体中的精髓剖析【内存对齐】 【位段】 😎前言🙌结构体内存对齐:😊结构体内存对齐存在的意思是什么?😘内存对齐例子详细剖析:😘结构体中的位段:&…...

2023年“网络安全”赛项江苏省淮安市赛题解析(超详细)
2023年中职组江苏省淮安市“网络空间安全”赛项 ①.2023年中职组江苏省淮安市任务书②.2023年中职组江苏省淮安市解析③.需要环境或者不懂的可以私信博主!①.2023年中职组江苏省淮安市任务书 任务一:服务器内部信息获取 任务环境说明: 服务器场景:Server210510(关闭链接…...

【二分查找】
二分查找704. 二分查找35. 搜索插入位置34. 在排序数组中查找元素的第一个和最后一个位置结语704. 二分查找 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在…...

Vue学习 -- 如何用Axios发送请求(get post)Promise对象 跨域请求问题
什么是Axios Vue本身是不支持发送axios请求,需要使用第三方插件,这里推荐使用Axios,Axios是基于promise的HTTP库;它会从浏览器中创建XMLHttpRequset对象。 安装Axios npm install axios -S下载后把axios.js文件复制进项目目录 …...

TVS和稳压管的相同点和不同点
大家好,我是记得诚。 文章目录 介绍相同点不同点介绍 TVS和稳压管都是电路中很常用的电子元器件,都是二极管的一个种类。 TVS二极管全称是Transient voltage suppression diode,也叫瞬态电压抑制二极管。 稳压二极管英文名字Zener diode,又叫齐纳二极管。 关于稳压二极…...

微信小程序项目实例——扫雷
今日推荐💁♂️ 2023许嵩演唱会即将到来🎤🎤🎤大家一起冲冲冲🏃♂️🏃♂️🏃♂️ 🔮🔮🔮🔮🔮往期优质项目实例🔮…...

2022-2023年度广东省职业院校学生专业技能大赛 中职组网络安全赛项竞赛规程
2022-2023年度广东省职业院校学生专业技能大赛 中职组网络安全赛项竞赛规程 一、赛项名称 赛项编号:Z27 赛项名称:网络安全赛项组别:中职 赛项归属:信息技术类 二、竞赛目的 为检验中职学校网络信息安全人才培养成效,促…...

超详细的堆排序,进来看看吧。
1.堆的基本概念1.1什么是堆堆是一种叫做完全二叉树的数据结构,1.2大堆和小堆大堆:每个节点的值都大于或者等于他的左右孩子节点的值小根堆:每个结点的值都小于或等于其左孩子和右孩子结点的值1.3完全二叉树节点之间的关系leftchild parent*2 1rightchild parent*…...

线性回归 特征扩展的原理与python代码的实现
文章目录1 多项式扩展的作用2 多项式扩展的函数2.1 接收参数2.2 多项式扩展示例3 多项式扩展的完整实例1 多项式扩展的作用 在线性回归中,多项式扩展是种比较常见的技术,可以通过增加特征的数量和多项式项的次数来提高模型的拟合能力。 举个例子&#…...

订阅关系一致
订阅关系一致指的是同一个消费者Group ID下所有Consumer实例所订阅的Topic、Tag必须完全一致。如果订阅关系不一致,消息消费的逻辑就会混乱,甚至导致消息丢失。本文提供订阅关系一致的正确示例代码以及订阅关系不一致的可能原因,帮助您顺畅地订阅消息。 背景信息 消息队列Ro…...

测试老鸟都在用的接口抓包常用工具以及接口测试工具都有哪些?
目录 接口 接口测试的重要性 常用抓包工具 常用接口测试工具 接口 接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换,传递和控制管理过程,以及系统间…...

Delphi 一个函数实现腾讯云最新版(API3.0)短信发送
目录 一、腾讯云短信基本知识 1. 需要在腾讯云后台注册账号 2. 需要在腾讯云中开通短信功能 3. 腾讯云短信版本说明 4. 短信内容的组成 特定规范 二、短信发送函数 三、下载源代码(收费) 一、腾讯云短信基本知识 如今我们随时都收到短信验证码,注册码等等。这是…...

2023年Android现代开发
2023年现代Android开发 下面与大家分享如何构建具有2023年最新趋势的Android应用程序。 Android是什么? Android 是一种基于 Linux 内核并由 Google 开发的开源操作系统。它用于各种设备,包括智能手机、平板电脑、电视和智能手表。 目前,…...

自然语言处理(NLP)在医疗领域的应用
自然语言处理(Natural Language Processing,NLP)是计算机科学领域与人工智能领域中的一个重要方向。它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。在各个领域都有其应用。 其在生物医学领域迅速发展,已经…...

计算机中的浮点数运算
计算机中的浮点数 计算机中以固定长度存储浮点数的方式,造成了浮点数运算过程容易产生上溢和下溢。以float32为例, 其标记位占1bit,指数位占8bit,小数部分占23bit 经典下溢场景 不满足精度导致截断误差 #include <iostream> #include <iomanip> usin…...

看了字节跳动月薪20K+测试岗面试题,让我这个工作3年的测试工程师,冷汗直流....
朋友入职已经两周了,整体工作环境还是非常满意的!所以这次特意抽空给我写出了这份面试题,而我把它分享给伙伴们,面试&入职的经验! 大概是在2月中的时候他告诉我投递了字节跳动并且简历已通过,2月23经过…...
这两天最好的ChatGPT应用;使用Notion AI提升效率的经验(13);AI编程与程序员的生存 | ShowMeAI日报
👀日报合辑 | 🎡生产力工具与行业应用大全 | 🧡 点赞关注评论拜托啦! 🤖 硅谷银行风波中,OpenAI 创始人大方帮助硅谷初创公司:钱先拿着用,有了再还 OpenAI 创始人 Sam Altman 的弟弟…...

Linux 内核likely与unlikey
内核源码的时候经常可以看到likely()和unlikely()函数,这两个函数的作用是什么?-- 先得学一学GCC提供的内建函数!! likely和unlikely内核中的定义 # define likely(x) __builtin_expect(!!(x), 1) # define unlikely(x) __built…...

成功解决主从同步异常之Slave_IO_Running显示为No的问题
前言 MySQL主从同步在做的过程中很容易出问题, 尤其是双主配置,参数多,需要在两台服务器中反复操作,容易搞错导致失败,这里汇总的是主从同步异常之Slave_IO_Running显示为No的解决方案。 文章目录 前言一. 问题重现二. 排查过程2.1 查看UUID是否相同,并修改2.2 修改完UU…...

面试阿里测开岗失败后,被面试官在朋友圈吐槽了......
前一阵子有个徒弟向我诉苦,说自己在参加某大厂测试面试的时候被面试官怼得哑口无言,场面让他一度十分尴尬印象最深的就是下面几个问题:根据你以前的工作经验和学习到的测试技术,说说你对质量保证的理解?非关系型数据库…...

蓝桥杯嵌入式--字符串比较在串口通信中的应用
前言今天做了个模拟题,大致意思是接收上位机发的字符串,然后执行相应操作。思路很明确,就是把接收到的内容进行比较,但是从前我只学过比较数字的方式,即直接用“”进行比较,但是字符串不能使用这个方法&…...

考研408每周一题(2019 41)
2019年(单链表) 41.(13分)设线性表L(a1,a2,a3,...,a(n-2),a(n-1),an)采用带头结点的单链表保存,链表中的结点定义如下: typedef struct node {int data;struct node *next; } NODE; 请设计一个空间复杂度为O(1)且时间上尽可能高效的算法&…...

Angular学习笔记(一)
以下内容基于Angular 文档中文版的学习 目录 使用Angular CLI 工具创建项目 HTML标签中{{}}插入值,[]绑定属性,()绑定事件,[(ngModel)]双向绑定 绑定属性 类和样式绑定 事件绑定 双向绑定 循环 IF 定义输入属性 定义输出事件 特殊符号 模板引用变量 页面跳转(路由…...

Linux用户和权限 —— 操作演示
Linux用户和权限——操作演示认知root用户用户、用户组管理查看权限控制修改权限控制- chmod修改权限控制- chownLinux系列: Linux基本命令 —— 操作演示 认知root用户 root用户(超级管理员) 无论是Windows、MacOS、Linux均采用多用户的管理模式进行权限管理。…...