分享一篇由C语言实现《数据结构》无头无循环单链表

news2024/12/28 20:35:51

三月,你好,各位csdn uu们好
在这里插入图片描述

文章目录

  • 前言
  • 一、何为单链表
  • 二、单链表基本操作(增,删,查,改,销毁,遍历)
    • 1.查找与修改、销毁与遍历
    • 2.链表插入与删除操作
  • 三、单链表 VS 顺序表 且 全部 源码(SLNode.h ) (SLNode.c) ( test.c)


前言

由于上一篇博客顺序表的相关实现其基本操作中,它的查找效率很快,通过下标可以快速存取表中任意一个位置的元素,但是其插入和删除操作因为要移动大量的元素,造成效率极低,时间复杂度达到O(n),那么能用上面结构来存储可以使其效率提高呢?因此在这里引入单链表,单链表的基本操作插入和删除效率喝高很高时间复杂度为O(n),在以查找到节点的前提下.在这里插入图片描述


一、何为单链表

链表 是一种在物理上非连续、 非顺序的数据结构, 由若干 节点 所组成,一个节点连着一个节点。
单链表顾名思义是链表的每一个节点又包含两部分, 一部分是存放数据的变量data, 另一部分是指向下一个节点的指针next。一个节点连着一个节点,每一个节点之间可以想象成有一条绳子拴起来,其实是上一个节点的next指针指向下一个节点,然后下一个的next指针又指向下一个。

线性表的单链表存储结构

typedef int SLDataType;
typedef struct SLNode
{
	SLDataType data;
	struct SLNode* next;
}SLNode;

从这个结构中,可以知道节点由存放数据元素的数据域(data)和存放下一个节点地址的指针域(next)组成,假设phead是指向链表中第一个节点的指针,那么该节点的数据域可以用phead->data表示,该节点的指针域用phead->next表示,链表与顺序表按照下标来随机寻找元素不同,
对于链表的其中一个节点A, 只能根据节点A的next指针来找到该节点的下一个节点B, 再根据节点 B的next指针找到下一个节点C……
而链表中的第一个节点被称为头节点, 最后1个节点被称为尾节点, 尾节点的 next指针指向空。后文不用带哨兵位的头节点,哨兵位的头节点数据域里是没有元素。这里的头节点数据域里是有元素的,与哨兵位头节点不同,这里用phead这一结构体指针指向链表第一个节点,phead->next是指向头节点的下一个节点,其实是phead->next存着它下一个节点的地址,可以想象成第一个节点拿着一根绳子把第二个节点拴起来了。
单链表找下一个容易但是它找他的前一个十分困难,需要再回溯一遍,在数据结构中还有一中双向链表可以快速找到前一个元素,但是本文重点描述单链表实现

链表在内存中如何存储的?

接下来我们看一看链表的存储方式。 如果说顺在内存中的存储方式是顺序存储(一个挨着一个的存储), 而链表在内存中的存储 方式则是随机存储(链表在内存中每一个节点存储在不同内存块中,而通过节点next指针链接下一个节点地址,依靠next指针这样连使每一个节点存在联系,使这些零星的空间链接起来)红色为顺序表顺序存储,绿色为链表随机存储
在这里插入图片描述

链表物理结构和逻辑结构
物理结构是在内存中真是存在的内存块,结构体指针phead指向第一个节点,然后第一个节点的next指针指向下一个节点地址。其实就是前一个节点的next存着下一个节点地址,这样下去直到链表为空在这里插入图片描述
逻辑结构是想象中有一根线连着两个节点,箭头表示next指针
在这里插入图片描述
与顺序表一样,链表同样有增删查改基本操作,并且插入和删除同样有头、尾、任意位置操作。且还有销毁操作

二、单链表基本操作(增,删,查,改,销毁,遍历)

1.查找与修改、销毁与遍历

查找

在查找元素时, 链表不像数组那样可以通过下标快速进行定位, 只能从 头节点开始向后一个一个节点逐一查找,查找到就返回改节点,找不到则返回空,如pos为3,从以phead指针指向第一个节点,第一个节点数据域不为3,通过第一个节点next指针指向第二个,然后第三个,找到为止。链表中的数据只能按顺序进行访问, 最坏的时间复杂度是O(n) 。
在这里插入图片描述

//查找
SLNode* SLNodeFind(SLNode* phead, SLDataType x)
{
	SLNode* cur = phead;
	while (cur != NULL)
	{
		if (cur->data != x)
		{
			cur = cur->next;
		}

		else
		{
			return cur;
		}
	}

	return NULL;
}

不考虑查找节点的过程, 链表的更新过程会像数组那样简单, 直接 把旧数据替换成新数据即可。修改就是将找到的这一节点的数据域修改即可

代码如下

if (ret)
	{
		//修改ret节点的值
		ret->data *= 2;
	}

链表销毁

销毁时注意释放上一个节点时要找到下一个节点地址,同时将该节点置空
代码实现如下


//销毁
void SLNodeDestory(SLNode** pphead)
{
	assert(*pphead);
	SLNode* cur = *pphead;
	SLNode* next = NULL;

	while (cur->next != NULL)
	{
		next = cur->next;
		free(cur);
		cur = next;

	}
		free(cur);
		cur = NULL;
		/*free(next);
		next = NULL;*/
		*pphead = NULL;

}

链表遍历
直接上代码

//遍历
void SLNodeprint(SLNode* phead)
{
	SLNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

2.链表插入与删除操作

插入三种情况

1、尾部插入 2、头部插入 3、中间插入

在插入之前先创建一个要插入的节点,开创新节点之后返回该节点地址,新节点数据域为x,指针域为空
代码如下

//创建一个新节点
SLNode* BuyNewnode(SLDataType x)
{
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;//1
	newnode->next = NULL;

	return newnode;
}

尾插

尾部插入, 是最简单的情况, 把最后一个节点的next指针指向新插入的 节点即可。
在这里插入图片描述
代码如下

//尾插
void SLNodePushBack(SLNode** pphead, SLDataType x)
{

	assert(pphead);
	SLNode* newnode = BuyNewnode(x);

	if (*pphead == NULL)
	{
		*pphead = newnode;//1
	}

	else
	{
		SLNode* tail = *pphead;
		while (tail->next != NULL)//如果将判断改为cur != NULL,那么到最后一个节点时将NULL给cur,后面插入的时候就不能找到下一个节点,直接为空了
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

头插
头部插入, 可以分成两步。

1、 把新节点的next指针指向原先的头节点。
2 、把新节点变为链表的头节点。
在这里插入图片描述
代码如下

//头插
void SLNodePushFront(SLNode** pphead, SLDataType x)
{
	assert(pphead);
	SLNode* newnode = BuyNewnode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

任意位置(pos)插入
任意位置插入, 同样分为两步。

1、 新节点的next指针, 指向插入位置的节点。
2、 插入位置前置节点的next指针, 指向新节点。
在这里插入图片描述
代码 如下

//pos前面插入
void SLNodeInsert(SLNode** pphead, SLNode*pos,SLDataType x)
{
	assert(pphead);
	SLNode* newnode = BuyNewnode(x);
	SLNode* prev = *pphead;

	if (pos == *pphead)
	{
		SLNodePushFront(pphead, x);//复用头插
	}

	else
	{
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		prev->next = newnode;
		newnode->next = pos;
	}

}

链表插入只要内存空间允许, 能够插入链表的元素是无穷无尽的, 不需要像数组 那样考虑扩容的问题。

链表删除操作同样分为尾删、头删、任意位置(pos)删除
尾删

尾部删除, 是最简单的情况, 把倒数第2个节点的next指针指向空。
在这里插入图片描述
代码如下

//尾删
void SLNodePopBack(SLNode** pphead)
{
	assert(pphead);
	assert(*pphead);

	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}

	else
	{
		SLNode* tail = *pphead;
		SLNode* prev = NULL;

		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}

		free(tail);
		tail = NULL;
		prev->next = NULL;
	}
}

头删

头部删除, 把链表的头节点设为原先头节点的next指针即 可。
在这里插入图片描述
代码如下

//头删
void SLNodePopFront(SLNode** pphead)
{

	assert(pphead);
	assert(*pphead);
	
	SLNode* first = *pphead;
	*pphead = first->next;
	free(first);
	first = NULL;
}

任意位置删除
任意位置删除,把要删除节点的前置节点的next指针 指向要 删除元素的下一个节点即可。在这里插入图片描述

代码如下

//删除pos位置节点
void SLNodeErase(SLNode** pphead, SLNode* pos)
{
	assert(pphead);
	if (*pphead == pos)
	{
		*pphead = pos->next;
		free(pos);
		pos = NULL;//这里置空无意义,形参改变不会影响实参,在外面调用它的那个函数置空
	}

	else
	{
		SLNode* prev = *pphead;
		while (prev->next!=pos)
		{
			prev = prev->next;
		}

		prev->next = pos->next;
		free(pos);
	}
}

删除注意两点

1、链表删空
2、被删除的节点需要释放且置空

链表的插入和删除时间复杂度是多少嘞?
如果不考虑插入、 删除操作之前 查找元素的过程, 只考虑纯粹的插入和删除操作, 时间复杂度都 是O(1)


三、单链表 VS 顺序表 且 全部 源码(SLNode.h ) (SLNode.c) ( test.c)

那么思考一下顺序表和 链表都属于线性的数据结构, 用哪一个更好呢? 顺序表和链表没有绝对的好与坏,顺序和 链表各有千秋。
对比一下顺序表和链表相关操作的性能。
在这里插入图片描述
从表格可以看出, 顺序表的优势在于能
够快速定位元素,就这一优势就特别如在有序且数据个数范围特别大时,用二分效率高。而链表的优势在于能够灵活地进行插入和删除操作。

SLNode.h

#include<stdlib.h>

#include<assert.h>
#include<stdio.h>
typedef int SLDataType;
typedef struct SLNode
{
	SLDataType data;
	struct SLNode* next;
}SLNode;



//遍历打印
void SLNodeprint(SLNode* phead);
//尾插
void SLNodePushBack(SLNode** pphead, SLDataType x);
//尾删
void SLNodePopBack(SLNode** pphead);
//头插
void SLNodePushFront(SLNode** pphead, SLDataType x);
//头删
void SLNodePopFront(SLNode** pphead);
//销毁
void SLNodeDestory(SLNode** pphead);
//查找
SLNode* SLNodeFind(SLNode* phead, SLDataType x);
//在pos位置前插入
void SLNodeInsert(SLNode** pphead, SLNode* pos, SLDataType x);
//在pos位置后插入
void SLNodeInsertAfter(SLNode** pphead, SLNode* pos, SLDataType x);
//删除pos节点
void SLNodeErase(SLNode** pphead, SLNode* pos);
//节点pos后一个节点
void SLNodePopAfter(SLNode** pphead, SLNode* pos);

SLNode.c

#include"SLNode.h"


//遍历
void SLNodeprint(SLNode* phead)
{
	SLNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

//创建一个新节点
SLNode* BuyNewnode(SLDataType x)
{
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;//1
	newnode->next = NULL;

	return newnode;
}

//尾插
void SLNodePushBack(SLNode** pphead, SLDataType x)
{

	assert(pphead);
	SLNode* newnode = BuyNewnode(x);

	if (*pphead == NULL)
	{
		*pphead = newnode;//1
	}

	else
	{
		SLNode* tail = *pphead;
		while (tail->next != NULL)//如果将判断改为cur != NULL,那么到最后一个节点时将NULL给cur,后面插入的时候就不能找到下一个节点,直接为空了
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

//尾删
void SLNodePopBack(SLNode** pphead)
{
	assert(pphead);
	assert(*pphead);

	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}

	else
	{
		SLNode* tail = *pphead;
		SLNode* prev = NULL;

		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}

		free(tail);
		tail = NULL;
		prev->next = NULL;
	}
}

//头插
void SLNodePushFront(SLNode** pphead, SLDataType x)
{
	assert(pphead);
	SLNode* newnode = BuyNewnode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

//头删
void SLNodePopFront(SLNode** pphead)
{

	assert(pphead);
	assert(*pphead);
	
	SLNode* first = *pphead;
	*pphead = first->next;
	free(first);
	first = NULL;
}

//销毁
void SLNodeDestory(SLNode** pphead)
{
	assert(*pphead);
	SLNode* cur = *pphead;
	SLNode* next = NULL;

	while (cur->next != NULL)
	{
		next = cur->next;
		free(cur);
		cur = next;

	}
		free(cur);
		cur = NULL;
		/*free(next);
		next = NULL;*/
		*pphead = NULL;

}

//查找
SLNode* SLNodeFind(SLNode* phead, SLDataType x)
{
	SLNode* cur = phead;
	while (cur != NULL)
	{
		if (cur->data != x)
		{
			cur = cur->next;
		}

		else
		{
			return cur;
		}
	}

	return NULL;
}

//pos前面插入
void SLNodeInsert(SLNode** pphead, SLNode*pos,SLDataType x)
{
	assert(pphead);
	SLNode* newnode = BuyNewnode(x);
	SLNode* prev = *pphead;

	if (pos == *pphead)
	{
		SLNodePushFront(pphead, x);
	}

	else
	{
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		prev->next = newnode;
		newnode->next = pos;
	}

}

//在pos位置后面插入x
void SLNodeInsertAfter(SLNode** pphead, SLNode* pos, SLDataType x)
{
	SLNode* newnode = BuyNewnode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

//删除pos位置节点
void SLNodeErase(SLNode** pphead, SLNode* pos)
{
	assert(pphead);
	if (*pphead == pos)
	{
		*pphead = pos->next;
		free(pos);
		pos = NULL;//这里置空无意义,形参改变不会影响实参,在外面调用它的那个函数置空
	}

	else
	{
		SLNode* prev = *pphead;
		while (prev->next!=pos)
		{
			prev = prev->next;
		}

		prev->next = pos->next;
		free(pos);
	}
}

//在pos位置之后删除
void SLNodePopAfter(SLNode** pphead, SLNode* pos)
{
	assert(pos->next);
	SLNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}

test.c

#include"SLNode.h"

void test()
{
	SLNode* plist = NULL;
	//尾插
	SLNodePushBack(&plist, 1);
	SLNodePushBack(&plist, 2);
	SLNodePushBack(&plist, 3);
	SLNodePushBack(&plist, 4);
	SLNodeprint(plist);
	//头插
	SLNodePushFront(&plist, 10);
	SLNodeprint(plist);
	SLNodeDestory(&plist);
}

void test1()
{
	SLNode* plist = NULL;

	SLNodePushBack(&plist, 1);
	SLNodePushBack(&plist, 2);
	SLNodePushBack(&plist, 3);
	SLNodePushBack(&plist, 4);
	SLNodePushFront(&plist, 10);
	SLNodeprint(plist);
	//尾删
	SLNodePopBack(&plist);
	SLNodeprint(plist);
	SLNodePopBack(&plist);
	SLNodePopBack(&plist);
	/*SLNodePopBack(&plist);*/
	//SLNodePopBack(&plist);
	SLNodeprint(plist);

	//头删
	SLNodePopFront(&plist);
	SLNodeprint(plist);
	SLNodePopFront(&plist);
	SLNodeprint(plist);
	SLNodePopFront(&plist);
	SLNodeprint(plist);
	SLNodeDestory(&plist);
}

void test2()
{
	SLNode* plist = NULL;

	SLNodePushBack(&plist, 1);
	SLNodePushBack(&plist, 2);
	SLNodePushBack(&plist, 3);
	SLNodePushBack(&plist, 4);
	SLNodePushFront(&plist, 10);
	SLNodeprint(plist);
	//查找
	SLNode* ret = SLNodeFind(plist, 4);
	if (ret)
	{
		//在ret位置前插入30
		SLNodeInsert(&plist, ret, 30);

	}
	SLNodeprint(plist);

	if (ret)
	{
		//修改ret节点的值
		ret->data *= 2;
	}
	SLNodeprint(plist);

	if (ret)
	{
		//在ret节点后面插入一个值为节点
		SLNodeInsertAfter(&plist, ret, 999);
	}
	SLNodeprint(plist);

	//if (ret)
	//{
	// //删除ret节点
	//	SLNodeErase(&plist, ret);
	//	//ret = NULL;
	//}
	//SLNodeprint(plist);

	if (ret)
	{
		//删除ret节点后面的那个节点
		SLNodePopAfter(&plist, ret);
	}
	SLNodeprint(plist);
}
int main()
{
	//test();
	//test1();
	test2();
	return 0;
}

注意:这篇文章是由c语言实现的,而从上文很多接口实现时可以发现,有**pphead,*phead,*plist为结构体指针初始化为空,后面在向链表中插入节点时,由于要改变链表内容,那么就需要将链表的地址传过去,在插入接口以二级指针指向链表地址,通过解引用就可以改变链表。这里插入一点传参问题,值传递:改变形参不可改变实参,地址传递:,改变形参可以改变实参。而断言assert(pphead)则是因为在插入时需要有链表才可以对链表进行操作,如果在SLNodePushBack()这个接口函数test()在传参时,传的是空地址,该接口就无法对链表进行操作。而在删除时同样如此,还需要断言链表内容是否为空,链表中是否有节点assert(*pphead),链表为空就不能再删除了。如果不用断言判断,用if判断链表是否为空的话,如果代码太多,一时间看不出哪里出的问题,还需要通过调试才能发现,而用断言则能给你报一些基础的错误,这时会发觉断言真好用。

迎来了三月

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

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

相关文章

python如何快速采集美~女视频?无反爬

人生苦短 我用python~ 这次康康能给大家整点好看的不~ 环境使用: Python 3.8 Pycharm mou歌浏览器 mou歌驱动 —> 驱动版本要和浏览器版本最相近 <大版本一样, 小版本最相近> 模块使用: requests >>> pip install requests selenium >>> pip …

暑期实习准备——Verilog手撕代码(持续更新中。。。

暑期实习准备——手撕代码牛客刷题笔记Verilog快速入门VL4 移位运算与乘法VL5 位拆分与运算VL6 多功能数据处理器VL8 使用generate…for语句简化代码VL9 使用子模块实现三输入数的大小比较VL11 4位数值比较器电路VL12 4bit超前进位加法器电路VL13 优先编码器电路①Verilog进阶挑…

Scala-模式匹配

基本语法 模式守卫 匹配类型 匹配对象 Scala 中的模式匹配类似于 Java 中的 switch 语法 基本语法 模式匹配语法中&#xff0c;采用 match 关键字声明&#xff0c;每个分支采用 case 关键字进行声明&#xff0c;当需 要匹配时&#xff0c;会从第一个 case 分支开始&#…

C++学习笔记-内存空间

考虑这样一种情况&#xff0c;当我们使用相同的名称&#xff0c;叫Zara的两个人在同一个班级。我们需要明确区分它们将不得不使用一些额外的信息&#xff0c;如他们的名字&#xff0c;如他们生活在不同的区域或母亲或父亲的名字等等。 同样的情况也出现在C应用程序中。例如&am…

Ubuntu 18.04.6 LTS 服务版安装参考

文章目录安装环境安装过程等待安装进行设置语言选择更新设置键盘样式设置IP地址配置代理配置 Ubuntu 存档镜像自定义存储配置设置账号配置 SSH安装软件包等待安装完成重启后验证安装后的基础优化重置 root 密码放开 root 远程登陆网卡信息变更修改ubuntu-18.04.6-live-server-a…

怎么用白光干涉仪的拼接测量功能

白光干涉仪的基本原理是通过不同的光学元件形成参考光路和探测光路&#xff0c;是利用干涉原理测量光程差&#xff0c;从而确定相关物理量的光学仪器。结合精密Z向扫描模块、3D 建模算法等&#xff0c;可以对器件表面进行非接触式扫描并建立表面3D图像&#xff0c;然后通过一体…

普通指针扫盲

一、什么是指针 C语言里&#xff0c;变量存放在内存中&#xff0c;而内存其实就是一组有序字节组成的数组&#xff0c;每个字节有唯一的内存地址。 CPU 通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位。这里&#xff0c;数据对象是指存储在内存 中的一个指定数据…

【项目精选】病历管理系统设计与实现(源码+视频)

点击下载源码 企业财务管理系统主要用于电子病历来提高医院各项工作的效率和质量&#xff0c;促进医学科研、教学&#xff1b;减轻各类事务性工作的劳动强度&#xff0c;使他们腾出更多的精力和时间来服务于病人。本系统结构如下&#xff1a; 电子病例系统&#xff1a; 病人登…

通过知识库深度了解用户的心理

自助服务知识库的价值是毋庸置疑的&#xff0c;如果执行得当&#xff0c;可以帮助减少客户服务团队的工作量&#xff0c;仅仅编写内容和发布是不够的&#xff0c;需要知道知识库对客户来说是否有用&#xff0c;需要了解客户获得的反馈&#xff0c;如果你正确的使用知识库软件&a…

哪款蓝牙耳机打电话好用?打电话音质好的蓝牙耳机

现在几乎是人人离不开耳机的时代。在快节奏的生活和充满嘈杂声音的世界中&#xff0c;戴着耳机听歌&#xff0c;是每个人生活中最不可或缺的一段自由、放松的时光&#xff0c;下面小编就来分享几款通话音质好的蓝牙耳机。 一、南卡小音舱蓝牙耳机 动圈单元&#xff1a;13.3mm…

伯恩光学再成被执行人:多次因劳动纠纷被起诉,曾冲刺港交所上市

近日&#xff0c;贝多财经从天眼查APP了解到&#xff0c;伯恩光学&#xff08;深圳&#xff09;有限公司&#xff08;下称“伯恩光学”&#xff09;因《伯恩光学&#xff08;深圳&#xff09;有限公司与温*燕劳动合同纠纷的案件》一事&#xff0c;被广东省深圳市龙岗区人民法院…

服务端开发之Java备战秋招面试篇6-Java各种并发锁

努力了那么多年,回头一望,几乎全是漫长的挫折和煎熬。对于大多数人的一生来说,顺风顺水只是偶尔,挫折、不堪、焦虑和迷茫才是主旋律。我们登上并非我们所选择的舞台,演出并非我们所选择的剧本。继续加油吧&#xff01; 目录 1、Java中主流锁分类体系介绍 2、乐观锁CAS原理刨…

wordpress 占用内容过高的解决方案

WordPress占用内存过高可能由多种因素引起&#xff0c;以下是一些可能的原因和解决方法&#xff1a; 主题和插件&#xff1a; WordPress的主题和插件是最常见的内存占用原因。确保使用的主题和插件是最新版本&#xff0c;并且只使用必需的插件。禁用不需要的插件&#xff0c;并…

【Linux】文件时间-ACM

文章目录文件时间-acmAccessChangeModify文件时间-acm 我们可以使用stat 文件名的方式查看对应的文件的时间信息 Access 表示文件最近一次被访问的时间 文件的访问 实际也就是文件的读取 实际操作中,文件的Access时间可能没有变化,这是因为在新的Linux内核中,Access时间不…

JavaScript 闭包的理解

1、概念 一个函数对周围状态的引用捆绑在一起&#xff0c;内层函数中访问到其外层函数的作用域 2、简单理解 闭包 内层函数 引用的外层函数变量 简单代码示例&#xff1a; function outer(){let i 1; // 被内层函数引用的变量function fn(){ // 内层函数console.log(i…

用javascript分类刷leetcode5.二分查找(图文视频讲解)

二分搜索 时间复杂度O(logn) 步骤&#xff1a; 从数组中间的元素开始&#xff0c;如果中间的元素正好是目标值&#xff0c;搜索结束如果目标值大于或小于中间的元素&#xff0c;则在大于或小于中间的元素的那一半继续搜索 代码模版 //二分查找伪代码模版 while (left <…

3.1 模拟栈+表达式求值+模拟队列+单调栈

模拟栈 题目链接 栈的数组模拟非常简单&#xff0c;不详细描述 设置一个指针指向栈顶第一个元素即可 STL中stack实现已经更新在STL_Stack #include<iostream> #include<string>using namespace std;const int N1e51; int m; string s; int stack[N]; int p;//指针…

Cartesi 2023 年 2 月回顾

2023年2月28日&#xff0c;通过ETH Denver和Cartesi的在线全球黑客马拉松一起开启黑客马拉松赛季!ETH Denver 正在热火朝天的进行着&#xff0c;我们正在为3月25日开始的首个全球在线黑客马拉松做准备。但这并不是本月发生的所有事情。我们在继续扩展和发展在全世界各地的社区&…

【软件测试】接口测试和接口性能测试,资深测试老鸟的总结......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 只会独立使用工具&a…

程序员必备!最值得收藏的宝藏网站大盘点

做为程序员&#xff0c;没有收藏点宝藏网站都说不过去。 除了常见的大家熟知的知乎、谷歌、b站、CSDN、掘金等&#xff0c;今天将介绍一些其他更加实用的宝藏网站&#xff0c;程序员小伙伴们可以按需收藏哦&#xff5e; 1.菜鸟教程&#xff1a;https://www.runoob.com/ 国内…