数据结构线性表——单链表

news2024/11/29 7:43:39

前言:小伙伴们又见面啦,这篇文章我们来一起学习线性表的第二模块——单链表。

单链表的学习就要开始上强度啦,小伙伴们一定要努力,坚持!


目录

一.什么是单链表

二.单链表与顺序表的区别

三.单链表的实现

1.单链表的定义 

2.单链表的初始化

3.单链表节点的创建

 四.单链表的操作

1.单链表的打印

 2.单链表的尾插

3.单链表的头插

4.单链表的尾删

5.单链表的头删

6.单链表的查找

7.单链表在指定位置前插

8.单链表在指定位置后插

9.单链表在指定位置删除

10.单链表的销毁

五.完整代码展示

1.SLinkList.h

2.SLIinkList.c

test.c

六.总结


一.什么是单链表

通过物理模型我们可以这样理解,单链表就是每个数据之间都用一个链条来连接,而单的含义就是两个数据之间只有一条链子连接


二.单链表与顺序表的区别

我们已经知道,顺序表是用一个数组来存储和管理数据,因为数组的大小难以改变就算是我们能够通过动态内存管理来实现数组大小的不断扩容,但还是可能会出现内存浪费的情况

而单链表就可以完美的解决这个问题。

单链表可以实现存一个数据就开辟一块空间的完美内存控制。

如下图,我们每需要存入一个数据,就开辟一个空间,而每个空间之间,我们用指针来相连前一个空间在存储数据的同时,存储下一块空间的地址,这样一来,我们就可以杜绝空间浪费。而这样一小块一小块的空间,我们称之为节点

带着这个思路,我们就开始来真正的实现单链表。


三.单链表的实现

1.单链表的定义 

通过上边的思路,不难得出,我们需要用结构体来管理每块空间:

typedef int SLLDataType;
//定义单链表
typedef struct SLinkList
{
	SLLDataType data;//数据
	struct SLinkList* next;//链条
}SLLNode;

因为指针要指向下一个数据空间,而空间是结构体类型,所以该指针的类型就要是结构体

而指针的名字,规定为next,对等“下一个”的意思。


2.单链表的初始化

因为单链表创建之后是没有数据的,内存为空,所以单链表的初始化,也就是我们要使用单链表的时候,创建一个单链表类型的指针变量

    SLLNode* ssl = NULL;

注意当我们创建完一个指针变量之后,如果不对其进行任何赋值操作,就要置空,否则就会变成野指针


3.单链表节点的创建

初始时刻,单链表是没有任何数据的,那么想要存入数据,就要创建新的节点

一个节点同时包含该节点的数据和要指向下一个节点地址的指针

SLLNode* CreatNode(SLLDataType x)
{
	SLLNode* newnode = (SLLNode*)malloc(sizeof(SLLNode));
		if (newnode == NULL)
		{
			perror("CreatNode->malloc");
			exit(-1);
		}
		newnode->data = x;	
		newnode->next = NULL;
		return newnode;
}

开辟节点的空间,我们用到malloc函数。

当我们新创建了一个节点之后,要将他的指针置空,因为我们并不需要在该函数中使用它的指针

至此,单链表的基础外壳就已经全部实现了,下面我们就要进行单链表的各种操作。 


 四.单链表的操作

1.单链表的打印

基于前边顺序表学习的经验,我们实现每一种操作之后,都要及时测试是否无误,所以我们要先有打印函数,那么单链表该如何打印呢???

有小伙伴说:这简单啊,不是每个节点都有指针指向下一个节点的地址吗,直接用指针遍历打印不就好了。

但是小伙伴们是否发现,虽然我们说每个节点都有一个指针来指向下一个节点,但是第一个节点的地址该怎么存放???,依此疑惑,我们引出头结点的定义。

头结点,我们用phead表示,让头结点去指向我们第一个节点的地址。

事实上,头结点还是我们在调用各种单链表操作函数时的形参

当我们新创建了单链表类型的变量,这个变量就是一个头节点,要用它去实现各种功能。

了解过头结点之后,我们就能够来实现打印函数了: 

void SLListPrint(SLLNode* phead)
{
	SLLNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
}

 为了防止phead发生改变,我们用cur来作为临时变量进行操作

那么该如何理解while循环的判断条件(cur != NULL)呢???

SLLNode* ssl = NULL;

这是我们的头结点,也是实参,把它传给函数,并用phead接收,再赋值给cur

如果cur一开始就为空,那么就证明当前单链表没有节点,无需打印如果不为空,就证明单链表有节点,开始循环打印,每打印一次,就让cur指向下一个节点直到cur再次为空时,说明没有下一个节点了,循环便退出。


 2.单链表的尾插

先来分析一下,单链表该如何尾插呢???

如果单链表本来有数据,那么只需要找到它的最后一个节点,让它的指针指向新节点的地址

但如果本来就没有数据,那么尾插不就等于创建一个新节点。

//尾插
void SLListPushBack(SLLNode** phead, SLLDataType x)
{
   	assert(phead);
	SLLNode* newnode = CreatNode(x);
	if (*phead == NULL)
	{
		*phead = newnode;
	}
	else
	{
        //找尾
		SLLNode* tail = *phead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

首先进行一个assert(phead)的断言,因为如果phead为空的话,就说明单链表不存在

尾插要创建一个新的节点,所以我们先调用CreatNode函数,并创建newnode节点变量来接收

接着进行头结点的判断如果为空,说明当前没有数据,直接让头结点指向新的节点

如果不为空,就从头开始往后找到最后一个节点,然后让最后一个节点的指针指向新的节点

要注意这里while循环的判断条件如果tail是最后一个节点了,那么它的指针就为空,因为它没有节点可以指向,所以要置空防止变成野指针,也就是tail->next == NULL,随后就让该指针指向新节点即可。

下面请注意最重要的一点

不知道小伙伴们有没有注意到形式参数phead是一个二级指针类型呀?

好奇怪,为什么是二级指针呢???

这就又要扯到形参是实参的一份临时拷贝这个知识点上了。

注意,我们的实参ssl是一个一级结构体指针类型,那么想要改变它所关联的结构体的内部数据,就需要传它的地址

接收一个指针的地址,就需要二级指针了。

 这里经常有小伙伴会认为,只要我是指针,我就能改变值,这是一个很大的误区

下面我们就来进行测试:

void test()
{
	SLLNode* ssl = NULL;
	SLListPushBack(&ssl, 1);
	SLListPushBack(&ssl, 2);
	SLListPushBack(&ssl, 3);
	SLListPushBack(&ssl, 4);
	SLListPrint(ssl);
}

注意我们调用函数时,传的都是实参ssl的地址,而打印并不会改变什么,所以不需要传地址。

结果如下:


3.单链表的头插

头插相对于尾插就简单多了,我们只需要创建一个新节点让它的指针指向第一个节点的地址,再让头结点指向新节点的地址

//头插
void SLListPushFront(SLLNode** phead,SLLDataType x)
{
    assert(phead);
	SLLNode* newnode = CreatNode(x);
	newnode->next = *phead;
	*phead = newnode;
}

首先进行一个assert(phead)的断言,因为如果phead为空的话,就说明单链表不存在

第一个节点的地址存在*phead里边,所以直接将*phead赋值给新节点的指针,然后再让*phead指向新节点

这段代码同样也满足原单链表没有节点的情况,这样*phead就为空,赋值之后同样满足最后一个节点指向NULL

测试如下:

void test()
{
	SLLNode* ssl = NULL;
	SLListPushBack(&ssl, 1);
	SLListPushBack(&ssl, 2);
	SLListPushBack(&ssl, 3);
	SLListPrint(ssl);
	printf("\n");
	SLListPushFront(&ssl, 5);
	SLListPushFront(&ssl, 6);
	SLListPrint(ssl);
}

结果如下:


4.单链表的尾删

想要尾删,那就和尾插一样,要先找到尾节点,然后将指向尾结点的前一个节点的指针置空,因为是用malloc开辟的空间,所以需要再将尾节点free掉,就可以完成尾删的操作了。

但是尾删还需要关注没有节点只有一个节点有多个节点这三种情况。

那有小伙伴就会问,为什么一个节点和多个节点不能用同一个代码呢???

我们来分析一下,如果只有一个节点,我们是要直接对*phead进行free的,这就需要二级指针

但是如果有多个节点,我们就可以用一个临时变量指针来free空间,不需要二级指针

  • 没有节点好说,那就是*phead为空
  • 当只有一个节点时,我们只需要将*phead给free了再将*phead置空即可。
  • 当有多个节点时,我们就要找尾了,这时候又有一个问题,找到尾之后,该如何找到尾的前一个节点呢???
  • 有一个便捷的方法,既然tail->next是下一个节点,那么tail->next->next就是下下个节点当tail->next->next为尾节点时,此时的tail->next就为尾结点的前一个节点啦

实现如下: 

//尾删
void SLListPopBack(SLLNode** phead)
{
	//空
	assert(*phead);
	//一个节点
	if ((*phead)->next == NULL)
	{
		free(*phead);
		*phead = NULL;
	}
	else//多个节点
	{
		//找尾
		SLLNode* tail = *phead;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}

判断单链表是否为空,我们直接用assert函数,强制断言,防止过渡尾删出现错误

测试如下:

void test()
{
	SLLNode* ssl = NULL;
	SLListPushBack(&ssl, 1);
	SLListPushBack(&ssl, 2);
	SLListPushBack(&ssl, 3);
	SLListPushFront(&ssl, 5);
	SLListPrint(ssl);
	printf("\n");
	SLListPopBack(&ssl);
	SLListPopBack(&ssl);
	SLListPopBack(&ssl);
	SLListPrint(ssl);
}

结果如下:


5.单链表的头删

头删需要和尾删一样考虑没有节点,只有一个节点和多个节点这三种情况吗???

我们来分析一下,没有节点肯定是要用assert函数来断言的;紧接着,删除第一个节点,那就是要让头结点指向第一个节点的next如果只有一个节点,那么就是让*phead指向NULL,而此时第一个节点的next不就为NULL吗

所以一个节点和多个节点就不用分开考虑了。

//头删
void SLListPopFront(SLLNode** phead)
{
	//空
	assert(*phead);
	//非空
	SLLNode* tail = *phead;
	*phead = (*phead)->next;
	free(tail);
	tail = NULL;
}

临时变量tail来记录第一个节点,接着让*phead指向下一个节点,最后通过tail来释放第一个节点

测试如下:

void test()
{
	SLLNode* ssl = NULL;
	SLListPushBack(&ssl, 1);
	SLListPushBack(&ssl, 2);
	SLListPushBack(&ssl, 3);
	SLListPushBack(&ssl, 4);
	SLListPrint(ssl);
	printf("\n");
	SLListPopFront(&ssl);
	SLListPopFront(&ssl);
	SLListPopFront(&ssl);
	SLListPrint(ssl);
}

结果如下:


6.单链表的查找

我们规定单链表的查找为:通过输入数据来查找找到则返回该数据的地址,找不到则返回NULL

其实和打印也是一个差不多的类型,我们就直接来实现:

//查找
SLLNode* SLListFind(SLLNode* phead, SLLDataType x)
{
	SLLNode* cur = phead;
	while (cur != NULL)
	{
		if (cur->data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	return NULL;
}

7.单链表在指定位置前插

单链表不像顺序表一样,因为是数组,所以可以直接在某个位置进行插入。而单链表的指定位置插入则可以分为前插和后插

首先要知道,想在单链表中插入数据,就必须有指针,所以我们要先找到指定位置的地址,说到找地址,就要用到单链表的查找功能了,所以单链表在指定位置的插入就必须和查找挂钩使用

//pos前插
void SLListInsertFront(SLLNode** phead, SLLNode* pos, SLLDataType x)
{
	assert(phead && *phead && pos);
	if (*phead == pos)
	{
		//头插
		SLListPushFront(phead, x);
	}
	SLLNode* val = *phead;
	while (val->next != pos)
	{
		val = val->next;
	}
	SLLNode* newnode = CreatNode(x);
	val->next = newnode;
	newnode->next = pos;
}

注意,pos位置前插入数据,我们就不是仅仅要断言phead了,同时还要断言*phead和pos

  • 如果*phead为空,说明链表为空,空链表哪有什么指定位置的概念;
  • 如果pos为空,就说明你给了一个空位置,空位置怎么插入?

值得注意的是,前插要判断pos是否为第一个节点,如果是第一个节点,就直接调用头插函数

那有小伙伴们可能会问,为什么pos是第一个节点就要特殊处理呢???

我们来看while的循环条件如果pos是第一个节点,那么val就是pos,这样val->next永远都不可能是pos,最后为空,对空指针解引用操作,程序就崩了

如果是前插,就必须先找到pos位置前的节点让它去指向新的节点newnode,再让新节点指向pos位置的地址

测试如下:

void test()
{
	SLLNode* ssl = NULL;
	SLListPushBack(&ssl, 1);
	SLListPushBack(&ssl, 2);
	SLListPushBack(&ssl, 3);
	SLListPushBack(&ssl, 4);
	SLListPrint(ssl);
	printf("\n");
	SLLNode* pos = SLListFind(ssl, 3);
	SLListInsertFront(&ssl, pos, 5);
	SLListPrint(ssl);
}

比如我们要在数据3所处的位置的前边插入,就先用查找操作找出3节点的地址,并赋值给pos,接着进行前插,结果如下:


8.单链表在指定位置后插

指定位置后插入数据就比前插简单多了,因为我们并不用去找pos位置前的节点,只需要让pos节点的指针指向新节点,再让新节点去指向pos节点的后一个节点即可

//pos后插
void SLListInsertBack(SLLNode* pos, SLLDataType x)
{
	assert(pos);
	SLLNode* newnode = CreatNode(x);
	SLLNode* tmp = pos->next;
	newnode->next = tmp;
	pos->next = newnode;
}

同样先断言一下pos定义tmp去接收pos节点的下一个节点的地址,让newnode指向tmp,再让pos去指向newnode

测试如下:

void test()
{
	SLLNode* ssl = NULL;
	SLListPushBack(&ssl, 1);
	SLListPushBack(&ssl, 2);
	SLListPushBack(&ssl, 3);
	SLListPushBack(&ssl, 4);
	SLListPrint(ssl);
	printf("\n");
	SLLNode* pos = SLListFind(ssl, 3);
	SLListInsertBack(pos, 5);
	SLListPrint(ssl);
}

依然是在数据3的后边插入,结果如下:


9.单链表在指定位置删除

单链表在指定位置的删除也比较简单,找到pos节点的前一个,让它直接指向pos节点的后一个,再将pos节点释放即可

那么指定位置的删除是否要考虑节点数问题呢???

首先没有节点或是表不存在肯定是不可取的,所以要先断言。

其次,考虑单节点和多节点,如果只有一个节点,那么pos位置前就是头节点pheadpos节点后就是NULL让头节点直接指向NULL,同样合情合理,所以单节点和多节点可以一起处理。

此外,指定位置的删除也需要判断pos是否为第一个节点

//pos删
void SLListErase(SLLNode** phead, SLLNode* pos)
{
	assert(phead && *phead && pos);
	if (*phead == pos)
	{
        //头删
		SLListPopFront(phead);
	}
	SLLNode* val = *phead;
	while (val->next != pos)
	{
		val = val->next;
	}
	val->next = val->next->next;
	free(pos);
	pos = NULL;
}

测试如下:

void test()
{
	SLLNode* ssl = NULL;
	SLListPushBack(&ssl, 1);
	SLListPushBack(&ssl, 2);
	SLListPushBack(&ssl, 3);
	SLListPushBack(&ssl, 4);
	SLListPrint(ssl);
	printf("\n");
	SLLNode* pos = SLListFind(ssl, 3);
	SLListErase(&ssl, pos);
	SLListPrint(ssl);
}

删除数据3所在位置的节点,结果如下:


10.单链表的销毁

单链表的所有空间也是使用malloc函数开辟的,所以使用之后要及时销毁。

那么只需要从头到尾将单链表遍历一遍,一个一个free即可:

//销毁
void SLListDestroy(SLLNode** phead)
{
	while (*phead)
	{
		SLLNode* tmp = *phead;
		*phead = (*phead)->next;
		free(tmp);
	}
}

五.完整代码展示

1.SLinkList.h

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLLDataType;

//定义单链表
typedef struct SLinkList
{
	SLLDataType data;
	struct SLinkList* next;
}SLLNode;

//打印
void SLListPrint(SLLNode* phead);
//尾插
void SLListPushBack(SLLNode** phead, SLLDataType x);
//尾删
void SLListPopBack(SLLNode** phead);
//头插
void SLListPushFront(SLLNode** phead, SLLDataType x);
//头删
void SLListPopFront(SLLNode** phead);
//查找
SLLNode* SLListFind(SLLNode* phead, SLLDataType x);
//pos前插
void SLListInsertFront(SLLNode** phead, SLLNode* pos, SLLDataType x);
//pos后插
void SLListInsertBack(SLLNode* pos, SLLDataType x);
//任意删
void SLListErase(SLLNode** phead, SLLNode* pos);

2.SLIinkList.c

#include "SLinkList.h"
//打印
void SLListPrint(SLLNode* phead)
{
	SLLNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("NULL");
}
//创建新节点
SLLNode* CreatNode(SLLDataType x)
{
	SLLNode* newnode = (SLLNode*)malloc(sizeof(SLLNode));
	if (newnode == NULL)
	{
		perror("CreatNode->malloc");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
//尾插
void SLListPushBack(SLLNode** phead, SLLDataType x)
{
	assert(phead);
	SLLNode* newnode = CreatNode(x);
	if (*phead == NULL)
	{
		*phead = newnode;
	}
	else
	{
		//找尾
		SLLNode* tail = *phead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}
//头插
void SLListPushFront(SLLNode** phead, SLLDataType x)
{
	assert(phead);
	SLLNode* newnode = CreatNode(x);
	newnode->next = *phead;
	*phead = newnode;
}
//尾删
void SLListPopBack(SLLNode** phead)
{
	assert(phead);
	//空
	assert(*phead);
	//一个节点
	if ((*phead)->next == NULL)
	{
		free(*phead);
		*phead = NULL;
	}
	else//多个节点
	{
		//找尾
		SLLNode* tail = *phead;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}
//头删
void SLListPopFront(SLLNode** phead)
{
	assert(phead);
	//空
	assert(*phead);
	//非空
	SLLNode* tail = *phead;
	*phead = (*phead)->next;
	free(tail);
	tail = NULL;
}
//查找
SLLNode* SLListFind(SLLNode* phead, SLLDataType x)
{
	SLLNode* cur = phead;
	while (cur != NULL)
	{
		if (cur->data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	return NULL;
}
//pos前插
void SLListInsertFront(SLLNode** phead, SLLNode* pos, SLLDataType x)
{
	assert(phead && *phead && pos);
	SLLNode* val = *phead;
	while (val->next != pos)
	{
		val = val->next;
	}
	SLLNode* newnode = CreatNode(x);
	val->next = newnode;
	newnode->next = pos;
}
//pos后插
void SLListInsertBack(SLLNode* pos, SLLDataType x)
{
	assert(pos);
	SLLNode* newnode = CreatNode(x);
	SLLNode* tmp = pos->next;
	newnode->next = tmp;
	pos->next = newnode;
}
//pos删
void SLListErase(SLLNode** phead, SLLNode* pos)
{
	assert(phead && *phead && pos);
	SLLNode* val = *phead;
	while (val->next != pos)
	{
		val = val->next;
	}
	val->next = val->next->next;
	free(pos);
	pos = NULL;
}
//销毁
void SLListDestroy(SLLNode** phead)
{
	while (*phead)
	{
		SLLNode* tmp = *phead;
		*phead = (*phead)->next;
		free(tmp);
	}
}

test.c

#include "SLinkList.h"
void test()
{
	SLLNode* ssl = NULL;
	SLListPushBack(&ssl, 1);
	SLListPushBack(&ssl, 2);
	SLListPushBack(&ssl, 3);
	SLListPushBack(&ssl, 4);
	SLListPrint(ssl);
	printf("\n");
	SLLNode* pos = SLListFind(ssl, 3);
	SLListErase(&ssl, pos);
	SLListPrint(ssl);
}
int main()
{
	test();
	return 0;
}

测试代码仅为例子,小伙伴们可以根据自己的需求自由更改。


六.总结

单链表基本知识的分享到这里就结束啦,博主到最后才发现这篇博客的字数竟然破万了!!!

其实大部分都是代码所占哈哈哈。

最后小伙伴们不要忘记一键三连哦!!!

我们下期再见啦!

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

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

相关文章

如何使用Node.js快速创建HTTP服务器并实现公网访问本地Server

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation&#xff0…

11月起,33个省份纳入数电票开票试点范围内,发票无纸化已是大势所趋!

10月底&#xff0c;北京、贵州、山东&#xff08;不含青岛市&#xff09;、湖南、宁夏5个地区相继发布开展数电票试点工作的通知&#xff0c;至此&#xff0c;全国已有33个省份纳入数电票开票试点范围内。根据上述5地区发布的相关公告&#xff0c;11月1日将正式推行“数电票”开…

Java-认识类和对象

本章重点&#xff1a; 1. 掌握类的定义方式以及对象的实例化 2. 掌握类中的成员变量和成员方法的使用 3. 掌握对象的整个初始化过程 4. 掌握封装特性 5. 掌握代码块 6. 掌握内部类 1. 面向对象的初步认知 1.1 什么是面向对象 Java是一门纯面向对象的语言(Object Oriented Pro…

HarmonyOS ArkTS基础知识

概述 上一节&#xff0c;学习了TypeScript的基础语法&#xff0c;而在鸿蒙开发当中&#xff0c;有基于自己的编程语言&#xff0c;便是ArkTS。它是一种声明式UI的编程范式的语言&#xff0c;开发框架如下图所示&#xff1a; 根据框架图&#xff0c;分析&#xff0c;我将它大致…

2023_11_6 每日半小时 SQL 刷题

文章目录 1. 查询所有列题目描述SQL 语句编写 2. 查询多列题目描述SQL 语句编写 3. 查询结果去重题目描述SQL 语句编写 4. 查询结果限制返回行数题目描述SQL 语句编写 5. 将查询后的列重新命名题目描述SQL 语句编写 语法小总结 1. 查询所有列 题目链接&#xff1a;SQL1 查询所…

SAP-MM-批量扩充视图

MM50 可以通过这个程序批量维护或查看这个物料没有维护的视图&#xff0c;进行扩充。

weblogic弱口令漏洞复现

文章目录 一、漏洞特征1.可以直接获取passwd文件2.可以直接获取密文文件3.可以直接获取密钥文件4.解密密码5.登录后台 二、命令执行复现1.部署webshell2.Shell命令执行3.jsp一句话木马 一、漏洞特征 1.可以直接获取passwd文件 http://192.168.232.131:7001/hello/file.jsp?p…

找茬小游戏源码系统 无聊就来玩一玩 带完整的搭建教程

找茬小游戏是一种经典的休闲游戏&#xff0c;它以找出不同之处为核心玩法&#xff0c;锻炼玩家的观察力和反应能力。随着互联网技术的发展和普及&#xff0c;越来越多的游戏开发者开始尝试开发这种简单有趣的游戏。下面源码小编就来给大家介绍一下这款源码系统的搭建教程以及核…

哈夫曼编码与解码,基于Python实现

from itertools import count from collections import Counter from heapq import heapify, heappush, heappopdef huffman_tree(s):# 统计每个字符出现的次数s Counter(s) # 计算可迭代序列中元素的数量&#xff0c;返回字典类型数据c…

2023年09月 Python(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试&#xff08;1~6级&#xff09;全部真题・点这里 一、单选题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 第1题 有一组数据存在列表中,things[“桌子”,“椅子”,“茶几”,“沙发”,“西瓜”,“苹果”,“草莓”,“香蕉”]&#xff0…

Python如何实现查看WiFi密码?

由于最近公司加强对无线安全的管理&#xff0c;wifi密码从以前的12345678&#xff0c;变成复杂难记的一串&#xff0c;导致我时常忘记WIFI密码&#xff0c;而遭受同事的“毒打”。由于我不是那种喜欢打扰同事认真工作的人&#xff0c;于是&#xff0c;我决定用python来试试&…

【Spring】注解

文章目录 1. 前言2.注解开发的准备工作3. IOC相关注解DI相关注解 4. DI相关注解4.1 Value4.2 AutoWired4.3 Qualifier 5. xml配置文件相关注解5.1 Configuration5.2 ComponensScan 6.Bean注解7. PropertySource 1. 前言 Spring支持使用注解代替xml配置,注解开发可以简化配置. …

如何备战秋季招聘:从规划到面试的全方位攻略

如何备战秋季招聘&#xff1a;从规划到面试的全方位攻略 随着秋天的到来&#xff0c;秋季招聘也进入了高峰期。对于即将毕业的同学们来说&#xff0c;抓住这个黄金时期&#xff0c;找到一份满意的工作至关重要。本文将为你提供一份全面的秋季招聘攻略&#xff0c;从规划到面试&…

<微信小程序>《微信小程序开发笔记》(二)

《微信小程序开发笔记》&#xff08;二&#xff09; 1 程序开发1.1 原则&#xff08;自己感悟&#xff09;1.2 架构1.3 开发模式 2 建立项目3 微信代码构成3.1 JSON 配置文件3.2 WXML 模板文件3.3 WXSS 样式文件3.4 JS 脚本逻辑文件 1 程序开发 1.1 原则&#xff08;自己感悟&…

吴翰清《计算》重磅来袭,为了可计算的价值,写给所有人!

《计算》终于出版了 计算&#xff0c;为了可计算的价值 撰文 | 吴翰清 三年前在新冠疫情大爆发期间&#xff0c;当我决心动笔写下第一行文字时&#xff0c;从来没想到过写作《计算》的工程会如此之大。 它横跨了人类文明三千年的数学史和计算机科学史&#xff0c;从数字的起…

【JMeter】定时器分类以及场景介绍

1. 定时器分类 固定定时器 作用&#xff1a;请求之间设置等待时间应用场景&#xff1a;查询商品列表后&#xff0c;去查看列表商品详情页。针对商品列表数据量比较大的&#xff0c;响应时间会比较长&#xff0c;就需要设置等待时间然后去查看商详 2.定时器的作用域&#xff1…

《005.SpringBoot+vue之学生选课管理系统01》

《005.SpringBootvue之学生选课管理系统01》 项目简介 [1]本系统涉及到的技术主要如下&#xff1a; 推荐环境配置&#xff1a;DEA jdk1.8 Maven MySQL 前后端分离; 后台&#xff1a;SpringBootMybatis; 前台&#xff1a;vueElementUI; [2]功能模块展示&#xff1a; 管理端 1…

渗透测试学习day1

文章目录 学习平台Starting Point靶机&#xff1a;Meow连接vpn创建靶机 解题过程Task 1Task 2Task 3Task 4Task 5Task 6Task 7Task 8 总结 学习平台 hackthebox Starting Point 靶机&#xff1a;Meow 难度&#xff1a;very easy 连接vpn创建靶机 步骤如下 打开linux虚拟…

开发环境eclipse的安装与配置详细教程(包括UML插件 如何汉化 JDK 代码补全等)

Eclipse开发环境的安装与配置 1.Eclipse安装与配置 1.将JDK与Eclipse这两个软件安装包放在一个文件夹下&#xff0c;方便之后安装使用。 2.安装JDK 在D&#xff1a;LeStoreDownload\java文件夹下另外新建三个文件夹分别命名为java、jdk和eclipse&#xff08;分别用于Java、j…

NFTScan | 10.30~11.05 NFT 市场热点汇总

欢迎来到由 NFT 基础设施 NFTScan 出品的 NFT 生态热点事件每周汇总。 周期&#xff1a;2023.10.30~ 2023.11.05 NFT Hot News 01/ BAYC 系列 NFT 地板价回升至 30 ETH 10 月 30 日&#xff0c;据数据显示&#xff0c;Bored Ape Yacht Club&#xff08;BAYC&#xff09;系列 …