【链表】无头单向非循环链表

news2024/10/5 14:19:28
  • 本节知识所需代码已同步到gitee --》单链表
  • 关注作者,持续阅读作者的文章,学习更多知识!
    https://blog.csdn.net/weixin_53306029?spm=1001.2014.3001.5343

单链表

      • 顺序表的问题及思考
      • 链表
        • 链表的概念及结构
        • 链表的分类
      • 无头单向非循环链表
        • 初始化链表
        • 打印单链表
        • 增加结点
        • 插入操作(==增==)
          • 单链表的尾插
          • 单链表的头插
          • 在给定位置之后插入
          • 在给定位置之前插入
        • 删除操作(==删==)
          • 单链表的头删
          • 单链表的尾删
          • 删除给定位置的结点
          • 删除给定位置之后的结点
        • 查找数据(==查==)
        • 修改数据(==改==)
        • 查看链表元素个数
        • 判空
        • 销毁所有节点
      • 测试案例

顺序表的问题及思考

我们之前了解了线性表中的顺序表,下面针对顺序表有一些问题思考:

问题:

  1. 中间/头部的插入删除,时间复杂度为O(N)
  2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

思考:如何解决以上问题呢?下面给出了链表的结构来看看。

链表

链表的概念及结构

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

它不按照线性的顺序存储数据,而是由若干个同一结构类型的“结点”依次串联而成的,即每一个结点里保存着下一个结点的地址。
在这里插入图片描述
注意:

  • 从上图看出,链式结构在逻辑上是连续的,但是在物理上不一定连续。
  • 现实中的节点一班都是从堆上申请出来的。
  • 从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续。

链表的分类

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

  1. 单向或者双向
  2. 带头或者不带头
  3. 循环或者非循环

虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:

  • 无头单项非循环链表

无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

  • 带头双向循环链表

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

今天我们来讲常用的无头单向非循环链表

无头单向非循环链表

初始化链表

我们知道链表是由多个结点组成,所以要想创建一个链表,首先要创建一个结点。一个结点存储的内容可以分为两部分:数据域,指针域。

  • 数据域:用于存储数据。
  • 指针域:用于存储下一个结点的地址,使链表“连起来”。

这里我们以存储整型(int)的数据为例创建结点。

typedef int SLTDataType;

typedef struct SListNode
{
	SLTDataType data; 数据域:用于存储该结点的数据
	struct SListNode* next;//指针域:用于存放下一个结点的地址
}SLTNode;

功能接口:

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include<stdbool.h>
// 要改变传过来的指向第一个节点的指针就传二级
// 不改变传过来的指向第一个节点的指针就传一级

//读写修改的函数传二级指针

void SListPushBack(SLTNode** pphead, SLTDataType x);//尾插

void SListPushFront(SLTNode** pphead, SLTDataType x);//头插

void SListPopBack(SLTNode** pphead);//尾删

void SListPopFront(SLTNode** pphead);//头删

//只读的函数接口传一级指针
SLTNode* BuySListNode(SLTDataType x);//创建新节点

void SListPrint(SLTNode* phead);//打印

int SListSize(SLTNode* phead);//查看数据元素个数

bool SListEmpty(SLTNode* phead);//判空  

SLTNode* SListFind(SLTNode* phead, SLTDataType x);//查找元素

// 在pos位置之前去插入x
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);//效率不高,得找插入前面的结点
//在pos位置后面去插入x
void SListInsertAfter(SLTNode* pos, SLTDataType x);


// 删除pos位置的值
void SListErase(SLTNode** pphead, SLTNode* pos);//删除pos位置
void SListEraseAfter(SLTNode* pos);//删除pos后面位置


void SListDestory(SLTNode** pphead);//销毁结点

打印单链表

打印链表时,我们需要从头指针指向的位置开始,依次向后打印,直到指针指向NULL时,结束打印。

void SListPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

增加结点

每当我们需要增加一个结点之前,我们必定要先申请一个新结点,然后再插入到相应位置

SLTNode* BuySListNode(SLTDataType x)//创建新节点
{
	SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));
	if (node == NULL)
	{
		printf("malloc fall!\n");
		exit(-1);
	}
	node->data = x;
	node->next = NULL;
	return node;//返回新结点地址
}

插入操作(

单链表的尾插

尾插的时候我们需要先判断链表是否为空,若为空,则直接让头指针指向新结点即可;若不为空,我们首先需要利用循环找到链表的最后一个结点,然后让最后一个结点的指针域指向新结点。

void SListPushBack(SLTNode** pphead, SLTDataType x)//尾插
{
	assert(pphead);

	if (*pphead == NULL)//判断是否为空表
	{
		SLTNode* newnode = BuySListNode(x);
		*pphead = newnode;
	}
	
	else
	{
		//找尾
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		SLTNode* newnode = BuySListNode(x);
		tail->next = newnode;
	}
}

:增加节点(BuySListNode())的函数本身就已经将新节点指针域置空,所以尾插时不需要再将新结点的指针域置空。

单链表的头插

头插时,我们只需要先让新结点的指针域指向即原来的第一个结点的位置,然后把新结点的地址给头指针变量即可。

void SListPushFront(SLTNode** pphead, SLTDataType x)//头插
{
		SLTNode* newnode = BuySListNode(x);
		newnode->next = *pphead;
		*pphead = newnode;
}

:上面两步操作的顺序不能颠倒,若先让头指针指向新结点(即先让新节点作为头指针),那么就无法找到原来第一个结点的位置了(即原来第一个结点的位置没有保存)。

在给定位置之后插入

在给定位置后插入结点也只需要两步:先让新结点的指针域指向该位置(pos)的下一个结点,然后再让该位置(pos)的结点指向新结点即可。

//在pos位置后面去插入x
void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = BuySListNode(x);
	//下面两行代码的顺序不能换,防止该位置(pos)的下一个结点地址因为没保存而找不到
	newnode->next = pos->next;
	pos->next = newnode;
}
在给定位置之前插入

要想在给定位置的前面插入一个新结点,我们首先还是要找到该位置之前的一个结点,然后让新结点的指针域指向位置为pos的结点,让前一个结点的指针域指向新结点即可。需要注意的是,当给定位置为头指针指向的位置时,相当于头插。

void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)// 在pos位置之前去插入x
{
	assert(pphead);
	assert(pos);

	// 1、头插
	if (*pphead == pos)
	{
		SListPushFront(pphead, x);
	}
	// 2、后面插入
	else
	{
		SLTNode* prev = *pphead;//接收头指针
		while (prev->next != pos)
		{
			prev = prev->next;
		}
	    // 找到pos位置的前一个节点,prev存的就是它的地址
		SLTNode* newnode = BuySListNode(x);
		newnode->next = pos;//让新结点的指针域指向位置为pos的结点
		prev->next = newnode;//让pos位置的前一个结点指向新结点
	}
}

**

注意:单链表不适合在pos的位置之前插入元素,因为需要找前一个位置

**

删除操作(

单链表的头删

头删较为简单,若为空表,则不必做处理;若不为空表,则直接让头指针指向第二个结点,然后释放第一个结点的内存空间即可。

void SListPopFront(SLTNode** pphead)//头删
{
	
	//链表为空,断言检测
	assert(*pphead != NULL);

	SLTNode* newnode = (*pphead)->next;
	free(*pphead);
	(*pphead) = newnode;
}
单链表的尾删

我们需要考虑三种不同的情况:

1、当链表为空时,不做处理。
2、当链表中只有一个结点时,直接释放该结点,然后将头指针置空。
3、当链表中有多个结点时,我们需要先找到最后一个结点的前一个结点,然后将最后一个结点释放,将前一个结点的指针域置空,使其成为新的尾结点。

void SListPopBack(SLTNode** pphead)//尾删
{
	assert(pphead);

	//链表为空,断言
	assert(*pphead != NULL);
	//1.一个结点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	//2.多个结点
	else
	{
		SLTNode* prev = NULL;
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		tail=NULL;
		prev->next = NULL;
	}	
}
删除给定位置的结点

要删除给定位置的结点,我们首先要判断该结点是否为第一个结点,若是,则操作与头删相同;若不是,我们就需要先找到待删除结点的前一个结点,然后让其指向待删除结点的后一个结点,最后才能释放待删除的结点。

void SListErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);
	//1.如果待删除的结点为第一个结点,则调用头删即可
	if (pos== *pphead)
	{
		SListPopFront(pphead);
	}
    //2.删除非头节点
	else
	{
		//找pos的前一个结点
		SLTNode* prev =* pphead;
		while (prev->next!=pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}
删除给定位置之后的结点

要删除给定位置之后的值,我们首先判断传入地址是否为最后一个结点的地址,若是,则不做处理,因为最后一个结点后面没有结点可删除。若不是最后一个结点,我们首先让地址为pos的结点指向待删除结点的后一个结点,然后将待删除结点释放即可。

void SListEraseAfter(SLTNode* pos)
{
	assert(pos);//确保传入地址不为空
	if (pos->next != NULL)
	{
		SLTNode* nwenode = pos->next->next;

		free(pos->next);
		pos->next = NULL;
		pos->next = nwenode;	
	}
	else
	{
		printf("后面没有元素可删!\n");
	}
}

查找数据(

查找数据相对于前面的来说就非常简单了,我们只需要遍历一遍链表,在遍历的过程中,若找到了目标结点,则返回结点的地址;若遍历结束也没有找到目标结点,则直接返回空指针。

SLTNode* SListFind(SLTNode* phead, SLTDataType x)//查找元素
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
		
	}
	return NULL;//没有找到数据为x的结点
}

修改数据(

//修改数据
void SListModify(SLTNode* pos, SLTDataType x)
{
	pos->data = x;//将结点的数据改为目标数据
}

查看链表元素个数

int SListSize(SLTNode* phead)//查看数据元素个数
{
	int size = 0;
	SLTNode* cur = phead;
	while (cur)
	{
		size++;
		cur = cur->next;
	}
	return size;
}

判空

//bool 只有真和假
bool SListEmpty(SLTNode* phead)//判空
{
	//法一
	//return phead == NULL;//空为真,非空为假,用(0,1)表示
	//法二
	return phead == NULL ? true : false;//空为真,非空为假,用(0,1)表示
}

销毁所有节点

void SListDestory(SLTNode** pphead)//销毁结点
{
	assert(pphead);

	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);

		cur = next;
	}
	*pphead = NULL;
}

测试案例

test1:手动创建链表,并输出

void TestSList1()//手动创建链表
{
	SLTNode* n1 = (SLTNode*)malloc(sizeof(SLTNode));
	n1->data = 1;

	SLTNode* n2 = (SLTNode*)malloc(sizeof(SLTNode));
	n2->data = 2;

	SLTNode* n3 = (SLTNode*)malloc(sizeof(SLTNode));
	n3->data = 3;

	SLTNode* n4 = (SLTNode*)malloc(sizeof(SLTNode));
	n4->data = 4;

	n1->next = n2;
	n2->next = n3;
	n3->next = n4;
	n4->next = NULL;

	SLTNode* plist = n1;
	SListPrint(plist);
}
int main()
{
	TestSList1();
	return 0;
}

在这里插入图片描述


test2:插入删除测试

void TestSList2()
	{
	SLTNode* plist = NULL;
	SListPushFront(&plist, -1);//头插
	SListPrint(plist);

	SListPushBack(&plist, 1);//尾插
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);  

	SListPrint(plist);

	SListPushFront(&plist, 0);//头插
	SListPrint(plist);

	SListPopBack(&plist);//尾删
	SListPopBack(&plist);//尾删
	SListPopBack(&plist);//尾删
	SListPopBack(&plist);//尾删
	SListPrint(plist);

	SListPopFront(&plist);//头删
	SListPrint(plist);

	printf("SListSize:%d\n", SListSize(plist));//查看当前链表元素个数
	printf("SListEmpty:%d\n", SListEmpty(plist));//判空操作

	}
int main()
{
	TestSList2();
	return 0;
}

在这里插入图片描述


test3:查找元素并修改元素

TestSList3()
{
	SLTNode* plist = NULL;
	SListPushBack(&plist, 1);//尾插
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPushBack(&plist, 5);
	SListPrint(plist);
	int n = 0;
	printf("请输入需要查找的元素:");
	scanf("%d", &n);
	SLTNode* pos = SListFind(plist,n);//查找元素
	if (pos)
	{
		printf("找到了!\n");
		//用SListFind找到元素返回的是结点指针,,就可以修改此元素
		int m = 0;
		printf("请修改元素为:");
		scanf("%d", &m);

		SListModify(pos, 20);

		SListPrint(plist);
	}
	else
	{
		printf("没找到!\n");
	}
}
int main()
{
	TestSList3();
	return 0;
}

在这里插入图片描述


test4:指定位置插入元素

TestSList4()
{
	SLTNode* plist = NULL;
	SListPushBack(&plist, 1);//尾插
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPushBack(&plist, 5);
	SListPrint(plist);
	int n = 0;
	printf("请输入需要查找的元素:");
	scanf("%d", &n);
	SLTNode* pos = SListFind(plist, n);//查找元素
	if (pos)
	{
		printf("找到了!\n");
		int m = 0;
		printf("请输入插入元素:");
		scanf("%d", &m);
		SListInsert(&plist,pos, m);// 在pos位置之前去插入元素
		SListInsertAfter(pos, -1);//在pos位置后面去插入元素
		SListPrint(plist);
	}
}
int main()
{
	TestSList4();
	return 0;
}

在这里插入图片描述


test5:删除指定位置元素

TestSList5()
{
	SLTNode* plist = NULL;
	SListPushBack(&plist, 1);//尾插
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPushBack(&plist, 5);
	SListPrint(plist);
	int n = 0;
	printf("请输入需要查找的元素:");
	scanf("%d", &n);
	SLTNode* pos = SListFind(plist, n);//查找元素
	if (pos)
	{
		printf("找到了,并且会依次删除pos位置之后的元素以及pos位置的元素~\n");
		SListEraseAfter(pos);
		SListPrint(plist);

		SListErase(&plist, pos);
		SListPrint(plist);
	}
	else
	{
		printf("没有此元素!\n");
	}
	SListDestory(&plist);//销毁结点
	SListPrint(plist);
}
int main()
{
	TestSList5();
	return 0;
}

在这里插入图片描述


the end

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

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

相关文章

windows默认文件(桌面、下载、文档等)设置为C盘根路径后怎么修改回去

桌面、下载、文档等设置为C盘根路径后怎么修改回去1.问题2.解决办法2.1.按Win R调出运行窗口&#xff0c;输入regedit并按回车。2.2.在弹出的注册表窗口里&#xff0c;打开下面路径计算机\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell F…

1.15 SPI数码管实验

一.SPI总线 SPI是全双工三/四总线制串行总线&#xff0c;支持多主机多从机模式&#xff0c;常用单主机多从机模式 数据传输可以先传输高位也可以先传输低位。 四线&#xff08;单主机多从机&#xff09;&#xff1a; MOSI&#xff1a;主机输出&#xff0c;从机输入 …

9. 垃圾收集器与内存分配策略

整体思路 先考虑3个问题 哪些内存需要收集 堆和方法区需要收集&#xff1b;程序计数器、虚拟机栈、本地方法栈都不需要做垃圾回收&#xff08;按照其功能很容易理解&#xff09; 什么时候收集 对象已死。引申出另一个问题&#xff0c;怎么判断对象已死呢&#xff1f;当程序内…

外部链接<a>

创建外部链接 HTML 通过使用 标签在 HTML 中创建链接。 注意&#xff1a;移除 target“_blank” 属性避免点击链接会打开新的元素页。 使用a元素实现页面内跳转 a元素也可以用在网页内不同区域的跳转。 设置a元素的 href 属性值为井号#加上想跳转区域对应的id属性值&…

ASP.NET Core 3.1系列(28)——ASP.NET Core中使用Autofac替换内置IoC容器

1、前言 前面的博客主要介绍了一些Autofac的使用方法&#xff0c;示例代码都是基于控制台程序。本文就来介绍一下如何在ASP.NET Core中使用Autofac代替内置的IoC容器。 2、创建接口和类 这里搭建了一个简易的项目&#xff0c;如下图所示&#xff1a; Service层代码如下&…

长安汽车推动新伙伴变革重塑供应链模式发布长安智电iDD技术

1月12日&#xff0c;以“携手勇进一路有你”为主题的2023长安汽车全球伙伴大会在重庆大剧院举行。此次大会&#xff0c;是长安汽车总结过往生产经营良好态势&#xff0c;研判行业未来发展趋势&#xff0c;发布最新企业发展战略&#xff0c;与全球合作伙伴共谋新未来&#xff0c…

【JavaEE】网络编程基础之Socket套接字

✨哈喽&#xff0c;进来的小伙伴们&#xff0c;你们好耶&#xff01;✨ &#x1f6f0;️&#x1f6f0;️系列专栏:【JavaEE】 ✈️✈️本篇内容:网络编程基础之Socket套接字。 &#x1f680;&#x1f680;代码存放仓库gitee&#xff1a;JavaEE初阶代码存放&#xff01; ⛵⛵作者…

机器学习:公式推导与代码实现-概率模型

最大信息熵模型 根据最大信息熵原理,信息熵最大时得到的模型是最优模型,即最大信息熵模型。 最大信息熵原理 信息论的开创者香农将信息的不确定程度称为熵,为了与热力学中熵的概念区分,这种信息的不确定程度又称信息熵。 最大信息熵原理认为在所有可能的概率模型中,熵…

【Python从入门到进阶】4、pycharm的安装及使用

接上篇《3、运行python代码》 上一篇我们学习了如何使用终端和执行文件运行python代码&#xff0c;本篇我们来学习python编程工具pycharm的安装及基本使用。 一、IDE的概念 上一篇我们介绍了使用命令行指令执行和文件编译的方法进行python代码的解释执行&#xff0c;但是仍然…

总之2022,我的研发、直播、软文触达13W+人的成果打包拿走,展望2023一起加油

导读 | 2022年勇哥算是正是进入写作圈&#xff0c;在小伙伴们的支持下&#xff0c;勇哥也是每日每夜的肝&#xff0c;真心和小伙伴们分享技术前沿路上的系列故事&#xff0c;大家相互鼓励与支持&#xff0c;勇哥也是收获满满&#xff01;现在勇哥通过这边文章整理一下本年度&am…

重装系统win11服务器未响应怎么修复操作

最近网友问小编win11服务器未响应怎么修复?最近有用户询问这个问题&#xff0c;在使用电脑的时候遇到了服务器无响应的情况&#xff0c;今天小编来教大家win11服务器未响应怎么修复操作&#xff0c;希望能帮到各位。 工具/原料&#xff1a; 系统版本&#xff1a;Windows11 品…

Koa 真解

1. 前言 昨天花费了比较多的时间将Koa的源码阅读了一遍&#xff0c;主要是项目中用到了Koa&#xff0c;为了做的更加得心应手所以先将源码看一下&#xff0c;总体上源码还是非常简单的&#xff0c;没啥难度。一方面为了总结另一方面也是为了不太看懂源码的同学们&#xff0c;今…

代码审计-7 ThinkPHP框架代码审计

ThinkPHP框架目录 applocation&#xff1a;此目录为应用目录&#xff0c;网站主要的文件控制器都放在applocation目录下 view&#xff1a;此目录在applocation下&#xff0c;为视图层 extend&#xff1a;为扩展类库目录 public&#xff1a;为网站对外访问目录&#xff0c;也就…

汽车路径尽头放一个点图像验证

文章目录前言一.图片二.大致思路2.1 小车位置识别2.2 采用轮廓算法得到路径的坐标2.3 采用断点续连的方法&#xff0c;将轮廓算法得到点组成直线&#xff0c;并寻找到最后的坐标三 缺陷四.如果大佬有其他的好的方法欢迎大佬们留言交流前言 提示&#xff1a;文章写完后&#xf…

坑多路难走,学数据分析转行前要知道培训机构不会说的事情

想要转行做数据分析师&#xff1f;那就要做好迎接坑多路难走的准备。虽然培训机构可以教你如何使用工具和算法&#xff0c;但它们很少会告诉你真正的行业现状。在这个竞争激烈的领域中&#xff0c;需要知道的不仅仅是如何处理数据&#xff0c;还有如何在企业中应用它。 跟着我…

CMMI之配置管理

配置管理&#xff08;Configuration Management, CM&#xff09;的目的是通过执行版本控制、变更控制等规程&#xff0c;以及使用配置管理软件&#xff0c;来保证所有配置项的完整性和可跟踪性。配置管理是对工作成果的一种有效保护。配置管理过程域是SPP模型的重要组成部分。本…

42. 【农产品溯源项目前后端Demo】后端-区块链连接服务

本节介绍后端代码是如何与区块链网络连接的。 1.在后端代码里fabric包 负责与区块链网络连接,并发送交易。 2.fabric.Const文件 定义 区块链网络拓扑结构,请查看注释。 public final class Const {//区块链网络中organizations的配置目录,从配置文件读取证书目录public stat…

【JavaEE】单例模式如何保证在多线程环境下线程安全高可用?

文章目录1 单例模式回顾2 饿汉式单例模式的实现3 懒汉式单例模式的实现4 单例模式的线程安全问题分析5 线程安全的懒汉式实现6 总结1 单例模式回顾 单例模式是设计模式的一种。而设计模式就是针对我们实际开发中写代码所遇到的不同场景所设立的解决方案。在笔者JavaSE阶段的文章…

Vue组件化编程需要注意的命名规则

在Vue组件化编程过程中&#xff0c;开始接触的不太注意命名规则&#xff0c;比如对于组件的内部参数命名以及在父组件中使用命名感到模糊&#xff0c;犯一些错误&#xff0c;就感觉在踩坑。 其实&#xff0c;这是对Vue组件化编程中的命名规则没有留意&#xff0c;稍加学习就可以…

java伪随机数生成器

关于随机数的基本概念 1、对随机数性质分类&#xff1a; 随机性&#xff1a;符合该性质的叫弱伪随机数。这种随机数仅可以用于一般应用&#xff0c;无法用在密码学&#xff0c;例如java中的java.util.Random类不可预测性&#xff1a;符合该性质的叫强伪随机数。在密码学中&am…