链表基础知识(二、双向链表头插、尾插、头删、尾删、查找、删除、插入)

目录

一、双向链表的概念

二、 双向链表的优缺点分析​与对比

 2.1双向链表特点:

2.2双链表的优劣:

2.3循环链表的优劣

2.4 顺序表和双向链表的优缺点分析​

三、带头双向循环链表增删改查实现

3.1SList.c

3.2创建一个新节点、头节点

3.3头插

3.4尾插

3.5头删

3.6尾删

3.7查找

3.8删除

3.9插入

3.10查找

3.11打印链表

3.12销毁链表

四、简化链表,用插入和删除代替其他插入删除

五、SList.h

六、Test.c


书接上文:

链表基础知识(一、单链表、头插、尾插、头删、尾删、查找、删除、插入)-CSDN博客

一、双向链表的概念

双向链表,即一个节点中有两个指针域,一个存放当前节点前一个节点的地址,另一个存放当前节点后一个节点的地址。

(STL中list就是这个结构)

双链表的节点:

二、 双向链表的优缺点分析​与对比

 2.1双向链表特点:

       1.每次在插入或删除某个节点时, 需要处理四个节点的引用, 而不是两个. 实现起来要困难一些
  2.相对于单向链表, 必然占用内存空间更大一些.
  3.既可以从头遍历到尾, 又可以从尾遍历到头

2.2双链表的优劣:

2.3循环链表的优劣

循环表的好处主要包括以下几点:

  • 灵活性:循环链表可以从任何一个节点开始遍历,而且任何节点都可以作为头节点。这种灵活性使得循环链表在处理某些问题时比其他类型的链表更有优势。

  • 简化操作:在循环链表中,插入和删除操作相对简便,不需要对头尾节点

  • 行特殊处理。这种简便性可以提高算法的效率,降低编程的复杂性。

  • 适用于循环问题:循环链表可以实现循环访问,因此特别适用于解决涉及到循环问题的场景。

  • 便于实现队列数据结构:使用循环链表来实现队列数据结构可以简化操作,只需要维护一个尾节点指针即可,因为尾节点的后向节点就是队头节点。

  • 方便操作系统管理和应用程序运行:循环链表在操作系统和应用程序中也有广泛的应用。例如,操作系统可以利用循环链表来管理多个应用程序,通过循环遍历来给每个应用程序分配执行时间。

  • 可用于实现高级数据结构:循环双向链表可以用于实现高级数据结构,例如斐波那契堆(Fibonacci Heap),从而扩展了其在复杂算法和数据结构中的应用范围。

循环链表的缺点主要包括:

  • 内存占用更多:相比单链表,循环链表需要更多的内存空间来存储链接信息。这是因为循环链表中的每个节点都需要指向下一个节点,形成一个闭环,从而增加了内存开销。

  • 代码复杂度提高:循环链表的代码实现相比单链表要复杂一些。在插入、删除节点或遍历链表时,需要特别注意处理边界条件和循环结构,这可能会增加开发和调试的难度。

  • 潜在的死循环风险:如果循环链表中的链接关系出现错误,可能会导致死循环的情况发生。例如,在插入或删除节点时,如果没有正确更新链接关系,就可能导致链表无法正确遍历或陷入无限循环中。

2.4 顺序表和双向链表的优缺点分析​

三、带头双向循环链表增删改查实现

结构复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。

3.1SList.c

#pragma once
#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>typedef int LTDataType;
//带哨兵位的头节点,第一个节点不存储有效数据,不能存储链表的长度
//typedef char LTDataType;
//typedef double LTDataType;无法存储链表长度//Slist  双向链表
//DList  单链表
typedef struct ListNode
{struct ListNode* next;struct ListNode* prev;LTDataType data;
}ListNode;ListNode* ListInit();
void ListDestory(ListNode* phead);
void ListPrint(ListNode* phead);void ListPushBack(ListNode* phead, LTDataType x);
void ListPushFront(ListNode* phead, LTDataType x);
void ListPopFront(ListNode* phead);
void ListPopBack(ListNode* phead);ListNode* ListFind(ListNode* phead, LTDataType x);
// pos位置之前插入x
void ListInsert(ListNode* pos, LTDataType x);
// 删除pos位置的值
void ListErase(ListNode* pos);//bool ListEmpty(ListNode* phead);
//int ListSize(ListNode* phead);//链表的长度

3.2创建一个新节点、头节点

ListNode* BuyListNode(LTDataType x)
//创建一个新节点
{ListNode* newnode = malloc(sizeof(ListNode));//malloc分配大小,是一个ListNode结构体的大小newnode->data = x;//将新节点的data字段设置为参数x的值newnode->next = NULL;//给新节点的next存储的地址置空newnode->prev = NULL;//给新节点的prev存储的地址置空return newnode;
}
ListNode* ListInit()
//创建头节点
{ListNode* phead = BuyListNode(0);//phead指向了这个新创建的节点phead->next = phead; // 将phead的 next 指向其自身phead->prev = phead; // 将phead的 prev 指向其自身return phead;//返回phead,即头节点
}

3.3头插

在头节点后插入

void ListPushFront(ListNode* phead, LTDataType x)
{assert(phead);//此节点不为空//顺序不能换ListNode* newnode = BuyListNode(x);newnode->next = phead->next;//存储phead的next,即下一个节点phead->next->prev = newnode;//下一个节点的prev指向newnodephead->next = newnode;//下一个节点的next 指向 newnodenewnode->prev = phead;//newnode的prev 指向 phead
}

3.4尾插

在头节点前插入

void ListPushBack(ListNode* phead, LTDataType x)
//x = 0,尾插
{assert(phead);//phead不为空ListNode* tail = phead->prev;//tail存储phead的prev中的地址,即最后一个节点ListNode* newnode = BuyListNode(x);//创建一个新节点tail->next = newnode;//上一个节点的next指向新节点newnode->prev = tail;//新节点的prev指向pheadnewnode->next = phead;//新节点的next指向pheadphead->prev = newnode;//上一个节点的prev指向新节点
}

3.5头删

在头结点后删除

void ListPopFront(ListNode* phead)
{assert(phead);//phead不为空assert(phead->next != phead);ListNode* first = phead->next;//先first存储phead的next中的地址,即phead的下一个节点ListNode* second = first->next;//再second存储下一个的节点的next中的地址,即下下个节点phead->next = second;//phead的next指向下下一个节点second->prev = phead;//下下一个节点的prev指向pheadfree(first);//下一个节点的空间被释放first = NULL;//first置空
}

3.6尾删

在头节点前删除

void ListPopBack(ListNode* phead)
{assert(phead);//此节点不为空assert(phead->next != phead);ListNode* tail = phead->prev;//先tail存储phead的prev中的地址,即上一个节点ListNode* prev = tail->prev;//prev存储上一个节点的prev中的地址,即上上一个节点prev->next = phead;//上上个节点的next指向pheadphead->prev = prev;//phead的prev指向上上个节点free(tail);//释放tail存储的空间,即上个节点的空间tail = NULL;//tail置空}

3.7查找

ListNode* ListFind(ListNode* phead, LTDataType x)
{assert(phead);//此节点不为空ListNode* cur = phead->next;//cur存储phead的next中的地址,即下一个节点while (cur != phead)//不是头节点{if (cur->data == x)//如果是要查找的地址{return cur;//找到了返回这个地址}cur = cur->next;//指向下一个节点}return NULL;//没有找到,返回空
}

3.8删除

// 删除pos位置的值
void ListErase(ListNode* pos)
{assert(pos);//此节点不为空ListNode* prev = pos->prev;//prev存储pos的prev中的地址,即上一个节点ListNode* next = pos->next;//next存储pos的next中的地址,即下一个节点prev->next = next;//上一个节点的next指向pos的next,即下一个节点next->prev = prev;//下一个节点的prev指向pos的prev,即上一个节点free(pos);//释放pos所在的空间
}

3.9插入

// pos位置之前插入x
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);//此节点不为空ListNode* prev = pos->prev;//prev存储pos的prev中的地址,即pos上一个节点ListNode* newnode = BuyListNode(x);//创建一个新节点// prev newnode posprev->next = newnode;//上一个节点的next指向newnodenewnode->prev = prev;//新节点的prev指向上一个节点newnode->next = pos;//新节点的next指向pospos->prev = newnode;//pos的prev指向newnode
}

3.10查找

ListNode* ListFind(ListNode* phead, LTDataType x)
{assert(phead);//此节点不为空ListNode* cur = phead->next;//cur存储phead的next中的地址,即下一个节点while (cur != phead)//不是头节点{if (cur->data == x)//如果是要查找的地址{return cur;//找到了返回这个地址}cur = cur->next;//指向下一个节点}return NULL;//没有找到,返回空
}

3.11打印链表

void ListPrint(ListNode* phead)
{assert(phead);//此节点不为空ListNode* cur = phead->next;//cur存储phead的next中的地址,即下一个节点while (cur != phead)//cur不是指向的自己(头节点){								printf("%d ", cur->data);//打印cur存储的数据,即下一个地址的数据(因为有头节点)cur = cur->next;//指向下一个节点}printf("\n");
}

3.12销毁链表

void ListDestory(ListNode* phead)
{assert(phead);//phead不为空ListNode* cur = phead->next;//cur存储phead的next中的地址,即下一个节点while (cur != phead)//cur指向的不是自己{ListNode* next = cur->next;//next指向下一个节点free(cur);//释放cur所在的空间cur = next;//cur指向下一个节点}free(phead);//释放头节点所在的空间phead = NULL;//头节点置空}

四、简化链表,用插入和删除代替其他插入删除

void ListPushBack(ListNode* phead, LTDataType x)
{assert(phead);//phead不为空ListInsert(phead, x);//可以用插入来表示尾插
}void ListPushFront(ListNode* phead, LTDataType x)
{assert(phead);//此节点不为空ListInsert(phead->next, x);//可以用插入来表示头插
}void ListPopFront(ListNode* phead)
{assert(phead);//phead不为空ListErase(phead->next);//可以用删除表示头删
}void ListPopBack(ListNode* phead)
{assert(phead);//此节点不为空ListErase(phead->prev);//可以用删除表示尾删
}// pos位置之前插入x
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);//此节点不为空ListNode* prev = pos->prev;//prev存储pos的prev中的地址,即pos上一个节点ListNode* newnode = BuyListNode(x);//创建一个新节点// prev newnode posprev->next = newnode;//上一个节点的next指向newnodenewnode->prev = prev;//新节点的prev指向上一个节点newnode->next = pos;//新节点的next指向pospos->prev = newnode;//pos的prev指向newnode
}// 删除pos位置的值
void ListErase(ListNode* pos)
{assert(pos);//此节点不为空ListNode* prev = pos->prev;//prev存储pos的prev中的地址,即上一个节点ListNode* next = pos->next;//next存储pos的next中的地址,即下一个节点prev->next = next;//上一个节点的next指向pos的next,即下一个节点next->prev = prev;//下一个节点的prev指向pos的prev,即上一个节点free(pos);//释放pos所在的空间
}

五、SList.h

#include"List.h"ListNode* BuyListNode(LTDataType x)
//创建一个新节点
{ListNode* newnode = malloc(sizeof(ListNode));//malloc分配大小,是一个ListNode结构体的大小newnode->data = x;//将新节点的data字段设置为参数x的值newnode->next = NULL;//给新节点的next存储的地址置空newnode->prev = NULL;//给新节点的prev存储的地址置空return newnode;
}//void ListInit(ListNode* phead)
初始化了链表的新节点
//{
//	phead = BuyListNode(0);//phead指向了这个新创建的节点
//	phead->next = phead;//将phead的 next 指向其自身
//	phead->prev = phead;//将phead的 prev 指向其自身
//}ListNode* ListInit()
//创建头节点
{ListNode* phead = BuyListNode(0);//phead指向了这个新创建的节点phead->next = phead; // 将phead的 next 指向其自身phead->prev = phead; // 将phead的 prev 指向其自身return phead;//返回phead,即头节点
}void ListDestory(ListNode* phead)
{assert(phead);//phead不为空ListNode* cur = phead->next;//cur存储phead的next中的地址,即下一个节点while (cur != phead)//cur指向的不是自己{ListNode* next = cur->next;//next指向下一个节点free(cur);//释放cur所在的空间cur = next;//cur指向下一个节点}free(phead);//释放头节点所在的空间phead = NULL;//头节点置空}void ListPrint(ListNode* phead)
{assert(phead);//此节点不为空ListNode* cur = phead->next;//cur存储phead的next中的地址,即下一个节点while (cur != phead)//cur不是指向的自己(头节点){								printf("%d ", cur->data);//打印cur存储的数据,即下一个地址的数据(因为有头节点)cur = cur->next;//指向下一个节点}printf("\n");
}void ListPushBack(ListNode* phead, LTDataType x)
//x = 0,尾插
{//assert(phead);//phead不为空//ListNode* tail = phead->prev;//tail存储phead的prev中的地址,即最后一个节点//ListNode* newnode = BuyListNode(x);//创建一个新节点////tail->next = newnode;//上一个节点的next指向新节点//newnode->prev = tail;//新节点的prev指向phead//newnode->next = phead;//新节点的next指向phead//phead->prev = newnode;//上一个节点的prev指向新节点ListInsert(phead, x);//可以用插入来表示尾插
}//void ListPushFront(ListNode* phead, LTDataType x)
x = 0,头插
//{
//	assert(phead);//此节点不为空
//
//	ListNode* first = phead->next;//first存储phead的next中的地址,即下一个节点
//	ListNode* newnode = BuyListNode(x);//在x处创建一个新节点
//
//	// phead newnode first
//	phead->next = newnode;//下一个节点的next指向新节点
//	newnode->prev = phead;//新节点的prev指向此节点
//	newnode->next = first;//新节点的next指向此节点
//	first->prev = newnode;//下一个节点的prev指向新节点
//}void ListPushFront(ListNode* phead, LTDataType x)
{assert(phead);//此节点不为空//顺序不能换ListNode* newnode = BuyListNode(x);newnode->next = phead->next;//存储phead的next,即下一个节点phead->next->prev = newnode;//下一个节点的prev指向newnodephead->next = newnode;//下一个节点的next 指向 newnodenewnode->prev = phead;//newnode的prev 指向 phead//ListInsert(phead->next, x);//可以用插入来表示头插
}void ListPopFront(ListNode* phead)
{assert(phead);//phead不为空assert(phead->next != phead);ListNode* first = phead->next;//先first存储phead的next中的地址,即phead的下一个节点ListNode* second = first->next;//再second存储下一个的节点的next中的地址,即下下个节点phead->next = second;//phead的next指向下下一个节点second->prev = phead;//下下一个节点的prev指向pheadfree(first);//下一个节点的空间被释放first = NULL;//first置空//ListErase(phead->next);//可以用删除表示头删
}void ListPopBack(ListNode* phead)
{assert(phead);//此节点不为空/*assert(phead->next != phead);ListNode* tail = phead->prev;//先tail存储phead的prev中的地址,即上一个节点ListNode* prev = tail->prev;//prev存储上一个节点的prev中的地址,即上上一个节点prev->next = phead;//上上个节点的next指向pheadphead->prev = prev;//phead的prev指向上上个节点free(tail);//释放tail存储的空间,即上个节点的空间tail = NULL;//tail置空*/ListErase(phead->prev);//可以用删除表示尾删
}ListNode* ListFind(ListNode* phead, LTDataType x)
{assert(phead);//此节点不为空ListNode* cur = phead->next;//cur存储phead的next中的地址,即下一个节点while (cur != phead)//不是头节点{if (cur->data == x)//如果是要查找的地址{return cur;//找到了返回这个地址}cur = cur->next;//指向下一个节点}return NULL;//没有找到,返回空
}// pos位置之前插入x
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);//此节点不为空ListNode* prev = pos->prev;//prev存储pos的prev中的地址,即pos上一个节点ListNode* newnode = BuyListNode(x);//创建一个新节点// prev newnode posprev->next = newnode;//上一个节点的next指向newnodenewnode->prev = prev;//新节点的prev指向上一个节点newnode->next = pos;//新节点的next指向pospos->prev = newnode;//pos的prev指向newnode
}// 删除pos位置的值
void ListErase(ListNode* pos)
{assert(pos);//此节点不为空ListNode* prev = pos->prev;//prev存储pos的prev中的地址,即上一个节点ListNode* next = pos->next;//next存储pos的next中的地址,即下一个节点prev->next = next;//上一个节点的next指向pos的next,即下一个节点next->prev = prev;//下一个节点的prev指向pos的prev,即上一个节点free(pos);//释放pos所在的空间
}

六、Test.c

#include"List.h"void TestList1()
{ListNode* plist = ListInit();//plist为头节点ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);ListPrint(plist);ListPushFront(plist, 0);ListPushBack(plist, -1);ListPrint(plist);/*ListPopFront(plist);ListPopFront(plist);ListPopFront(plist);ListPrint(plist);ListPopBack(plist);ListPrint(plist);ListDestory(plist);*/}int main()
{TestList1();return 0;
}

今天就先到这了!!!

看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注!

你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。

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

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

相关文章

C# WPF上位机开发(知识产权ip保护)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 上位机软件如果是和硬件模块搭配开发&#xff0c;这个时候大部分上位机基本上都是白送的&#xff0c;不会收取相关的费用。但是&#xff0c;如果上…

chrome升级后,调试vue在控制台输出总是显示cjs.js

当前chrome版本120.0.6099.72 在vue中使用console.log输出时&#xff0c;总是显示cjs.js多少多少行&#xff0c;不能显示源文件名及行数 【解决方案】 打开控制台的设置 左侧找到“Ignore List”&#xff0c;取消勾选"enable Lgnore Listing"&#xff0c;并重启chr…

【Jmeter】Jmeter基础6-Jmeter元件介绍之前置处理器

前置处理器主要用于处理请求前的准备工作&#xff0c;如&#xff1a;参数、环境变量的设置等。 2.6.1、JSR223预处理程序 作用&#xff1a;请求前的准备工作。 参数说明&#xff1a; 语言&#xff1a;开发脚本所使用的语言&#xff0c;可通过下拉列表选择。参数&#xff1a;传…

Linux实操——安装Mysql

安装Mysql 一、检查是否已经安装了mariadb数据库,并卸载二、下载mysql包&#xff0c;并通过ftp上传到服务器三、解压安装包四、创建数据存储文件夹五、创建执行mysqld命令的用户&#xff0c;并初始化mysql六、启用传输安全七、启动mysql&#xff0c;验证是否安装成功 总结 博主…

【Hive】——DDL(DATABASE)

1 概述 2 创建数据库 create database if not exists test_database comment "this is my first db" with dbproperties (createdByAllen);3 描述数据库信息 describe 可以简写为desc extended 可以展示更多信息 describe database test_database; describe databa…

技术分享 | 接口测试价值与体系

如果把测试简单分为两类&#xff0c;那么就是客户端测试和服务端测试。移动端的测试包括 UI 测试&#xff0c;兼容性测试等&#xff0c;服务端测试包括接口测试。接口测试检查数据的交换、传递和控制管理过程。它绕过了客户端&#xff0c;直接对服务端进行测试。 接口测试的价值…

链表之带头双向循环链表(C语言版)

我们之前已经介绍过链表的知识了&#xff0c;这里我们直接开始实现带头双向循环链表 数据结构之单链表&#xff08;不带头单向非循环链表&#xff09;-CSDN博客 第一步&#xff1a;定义结构体 //定义结构体 typedef int SLTDateType; typedef struct Listnode {SLTDateType d…

字符设备驱动框架的编写

一. 简介 我们在学习裸机或者 STM32 的时候关于驱动的开发就是初始化相应的外设寄存器&#xff0c;在 Linux 驱动开发中&#xff0c;肯定也是要初始化相应的外设寄存器。 只是在 Linux 驱动开发中&#xff0c; 我们需要按照其规定的框架来编写驱动&#xff0c;所以说学 …

【HTML5、CSS3】新增特性总结!

文章目录 23 HTML5 新增特性23.1 语义化标签23.2 多媒体标签23.2.1 视频<video>标签23.2.2 音频<audio>标签 23.3 input属性值23.4 表单属性 24 CSS3 新增特性24.1 属性选择器24.2 结构伪类选择器24.2.1 选择第n个元素24.2.2 常用的6个结构伪类选择器 24.3 伪元素选…

如何用Python向图像中加入噪声

我们在做机器视觉项目的过程中&#xff0c;有的时候需要向图像中加入噪声。Pytorch本身不支持类似的功能&#xff0c;如果自己写的话&#xff0c;不但麻烦&#xff0c;而且容易出错。好在skimage支持这个功能。代码如下&#xff1a; import skimage import matplotlib.pyplot …

抚琴成一快-布鲁斯

布鲁斯 0.理论1.音阶1.大调布鲁斯音阶2.小调布鲁斯音阶 1.基础1.shuffle节奏制音2.十二小节3.和弦4.小调五声音阶 2.演奏手法1.Lamp and Lamp1.基础和声进行2.进阶和声进行1.quick change2. call and respond:3.回旋句 2.Box1.基础和声进行2.进阶和声进行 3.Boogie1.基础节奏2.…

【教学类-06-19】20231217 (按“列”正序题)X-Y之间“加法题+题”(1页最多0-13。填满115空格)

作品展示&#xff1a;按列排序&#xff0c;从小到大正序&#xff08;没有大量空格&#xff09; 1.会有空格做分割线&#xff0c;上面部分是所有的小到大正序加法&#xff0c;下面的部分就是正序题目的不重复随机抽取题目&#xff08;乱序题&#xff09; 2、包含分割空格&…

实验记录:深度学习模型收敛速度慢有哪些原因

深度学习模型收敛速度慢有哪些原因&#xff1f; 学习率设置不当&#xff1a; 学习率是算法中一个重要的超参数&#xff0c;它控制模型参数在每次迭代中的更新幅度。如果学习率过大&#xff0c;可能会导致模型在训练过程中的振荡&#xff0c;进而影响到收敛速度&#xff1b;如果…

【Windows】windows11右键默认显示更多选项的办法

Windows11系统的右键菜单显示&#xff0c;需要多点一次“显示更多选项”才能看到所有菜单内容&#xff0c;按下面步骤简单设置一下就能恢复成Windows经典的右键菜单显示。 1. 2.输入命令【reg.exe add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a…

Redis - RDB与AOF持久化技术

Redis 持久化技术 RDB 是默认持久化方式&#xff0c;但 Redis 允许 RDB 与 AOF 两种持久化技术同时 开启&#xff0c;此时系统会使用 AOF 方式做持久化&#xff0c;即 AOF 持久化技术的优先级要更高。同样的道 理&#xff0c;两种技术同时开启状态下&#xff0c;系…

css的元素显示模式(有单行文字垂直居中,侧边栏等案例)

目录 1. 什么是元素的显示模式 2. 元素显示模式的类型 块元素 行内元素 行内块元素 3. 元素显示模式的转换 4.文字垂直居中 5.具体实现案例 1. 什么是元素的显示模式 定义&#xff1a;元素显示模式就是元素&#xff08;标签&#xff09;以什么方式进行显示&#xff0c;…

HarmonyOS鸿蒙应用开发——数据持久化Preferences封装

文章目录 数据持久化简述基本使用与封装测试用例参考 数据持久化简述 数据持久化就是将内存数据通过文件或者数据库的方式保存到设备中。HarmonyOS提供两两种持久化方案&#xff1a; Preferences&#xff1a;主要用于保存一些配置信息&#xff0c;是通过文本的形式存储的&…

TCP/IP详解——FTP 协议,Telnet协议

文章目录 1. FTP 协议1.1 FTP的应用1.2 FTP传输文件的过程1.3 FTP传输模式1.4 主动模式&#xff08;Active Mode&#xff09;1.5 Active Mode 抓包分析1.6 被动模式&#xff08;Passive Mode&#xff09;1.7 Passive Mode 抓包分析 2. Telnet 协议2.1 Telnet 概念2.2 Telnet 协…

【网络安全】网络防护之旅 - Java安全机制探秘与数字证书引爆网络防线

&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《网络安全之道 | 数字征程》⏰墨香寄清辞&#xff1a;千里传信如电光&#xff0c;密码奥妙似仙方。 挑战黑暗剑拔弩张&#xff0c;网络战场誓守长。 目录 &#x1f608;1. 初识网络安…

Web前端-CSS(文本样式)

文章目录 1.font字体1.1 font-size:大小1.2 font-family:字体1.3 font-weight:字体粗细1.4 font-style:字体风格1.5 font总结 2. css外观属性2.1 color:文本颜色2.2 text-align:文本水平对齐方式2.3 line-height:行间距2.4 text-indent:首行缩进2.5 text-decoration 文本的装饰…