【数据结构】第三站:单链表

目录

一、顺序表的缺陷

二、链表

1.链表的概念以及结构

2.链表的分类

3.单链表的逻辑结构与物理结构

三、单链表的实现

1.单链表的定义

2.单链表的接口定义

3.单链表的接口实现

四、单链表的实现完整代码


一、顺序表的缺陷

在上一篇文章中,我们了解了顺序表的结构以及他的接口的实现。但同时我们也发现了他的一些缺陷

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

二、链表

1.链表的概念以及结构

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

2.链表的分类

链表一共有八种类型,他们可由是单向还是双向,是循环还是非循环,是带头结点还是不带头结点进行排列组合出八种结构

虽然有很多种结构,但是只有两种最为常用

无头单向非循环链表和带头双向循环链表

这里我们先只需要了解无头单向非循环链表,其他链表后续了解

3.单链表的逻辑结构与物理结构

如下图所示,是链表的实际的物理结构与逻辑结构。物理结构就是实实在在数据在内存中的变化,逻辑结构就是为了方便理解,形象化出来的

 

三、单链表的实现

1.单链表的定义

typedef int SLTDateType;

typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SListNode;

如上代码所示,单链表有数据域和指针域两部分组成,指针是用于指向下一个结点的指针

2.单链表的接口定义

//单链表的打印
void SListPrint(SListNode* plist);
//单链表的尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
//单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
//单链表的尾删
void SListPopBack(SListNode** pplist);
//单链表的头删
void SListPopFront(SListNode** pplist);
//单链表的查找
SListNode* SListFind(SListNode* plist,SLTDateType x);
//单链表在pos位置之后插入
void SListInsertAfter(SListNode* pos, SLTDateType x);
//单链表在pos位置之前插入
void SListInsertPrev(SListNode** pplist, SListNode* pos, SLTDateType x);
//单链表在pos之后删除
void SListEraseAfter(SListNode* pos);
//单链表在pos之前删除
void SListErasePrev(SListNode** pplist, SListNode* pos);
//单链表在pos位置删除
void SListErase(SListNode** pplist, SListNode* pos);
//单链表的销毁
void SListDestroy(SListNode** pplist);

如上代码所示,是我们的单链表需要实现的接口,对于链表和顺序表一样都是为了实现数据的管理,区别就是前者是离散的,后者是连续的。我们的目的还是增删查改

3.单链表的接口实现

1.单链表的打印

//单链表的打印
void SListPrint(SListNode* plist)
{
	SListNode* cur = plist;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

我们先来完成单链表的打印,对于单链表的打印,还是比较简单的,只需要先将头结点的指针给保存下来,然后依次去遍历单链表即可

2.单链表的尾插以及获取结点函数

//获取一个结点
SListNode* BuySListNode(SLTDateType x)
{
	SListNode* tmp = (SListNode*)malloc(sizeof(SListNode));
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
	tmp->next = NULL;
	tmp->data = x;
	return tmp;
}

//单链表的尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{
	assert(pplist);
	SListNode* newnode = BuySListNode(x);
	if (*pplist == NULL)
	{
		*pplist = newnode;
		return;
	}
	SListNode* tail = (*pplist);
	while (tail->next != NULL)
	{
		tail = tail->next;
	}
	tail->next = newnode;
}

对于单链表的尾插,我们要特别注意了。pplist是单链表头结点的地址,所以一定不为空

首先是获取结点,我们为了方便,先直接将其封装为一个函数。

有了结点了,那么我们还要思考如何尾插,那么我们先完成一般情况,假设已经有了一个很长的单链表了,我们还想要继续尾插一个值,那么只需要先找到原来的尾结点,然后将尾结点和新结点进行链接即可

当然还是存在一些特殊情况的,比如说原来的单链表压根就没有尾结点,也就是说链表是空的,那么上面的一般方法肯定行不通,这里其实就需要特殊处理一下,直接将新节点和链表头链接起来即可

3.单链表的头插

//单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{
	assert(pplist);
	SListNode* newnode = BuySListNode(x);
	SListNode* first = *pplist;
	*pplist = newnode;
	newnode->next = first;
}

对于单链表的头插,就比较简单了,我们直接创建一个新结点,然后记录下原来的第一个结点的地址,然后让链表头与新节点链接起来,然后新节点与原来的第一个结点链接起来,这里我们会发下,其实是不需要处理空链表的情况的,这里体现了单链表适合头插

4.单链表的尾删

//单链表的尾删
void SListPopBack(SListNode** pplist)
{
	assert(pplist);
	assert(*pplist);
	if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* tail = *pplist;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}

对于单链表的尾删,我们要想清楚了,首先是一般情况,当链表很长的时候,我们想要删除最后一个结点,那么得先找到前一个结点,然后释放最后一个结点,最后让前一个结点指向空。

我们还需要注意链表为空的状态,这个肯定是不可以删除的,所以我们直接断言掉。

然后是链表为一个结点的情况,如果链表只有一个结点,那么我们会发现,压根找不到前一个结点,所以我们也特殊处理,我们直接释放掉第一个结点,然后置空即可。

5.单链表的头删

//单链表的头删
void SListPopFront(SListNode** pplist)
{
	assert(pplist);
	assert(*pplist);

	SListNode* second = (*pplist)->next;
	free(*pplist);
	*pplist = second;
}

对于单链表的头删,我们同样断言掉链表为空的状态

然后我们需要做的就是记录第二个结点,然后释放原来的第一个结点,最后连接链表头和第二个结点。这样我们就实现了我们的目的。值得注意的是,我们发现链表的头删也是比较有优势的。

6.单链表的查找

//单链表的查找
SListNode* SListFind(SListNode* plist,SLTDateType x)
{
	SListNode* cur = plist;
	while (cur != NULL)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

对于单链表的查找,这个也很简单,他和单链表的打印思路是一样的,不同的是只需要返回结点的地址即可。

7.单链表在pos位置之后的插入

//单链表在pos位置之后插入
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
	assert(pos);
	SListNode* newnode = BuySListNode(x);
	SListNode* next = pos->next;
	pos->next = newnode;
	newnode->next = next;
}

单链表在pos位置之后的插入也是比较简单的,我们只需要先申请一个结点,然后记录pos位置的下一个结点,然后连接就可以了。值得注意的是,pos的位置不可能为空。因为他不是一个有效的结点地址

8.单链表在pos位置之前的插入

//单链表在pos位置之前插入
void SListInsertPrev(SListNode** pplist, SListNode* pos, SLTDateType x)
{
	assert(pplist);
	assert(pos);
	assert(*pplist);

	SListNode* newnode = BuySListNode(x);
	if (*pplist == pos)
	{
		*pplist = newnode;
		newnode->next = pos;
	}
	else
	{
		SListNode* prev = *pplist;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = newnode;
		newnode->next = pos;
	}
}

对于在pos位置之前的插入,确实是比较繁琐了。我们要思考,首先pos和pplist不可能为空,然后这个链表也是不可能为空链表的,至少也要有一个值。否则如果存在pos这个结点呢?

然后我们在来考虑一般情况,我们假设链表很长,在中间位置pos之前插入一个结点,那么毫无疑问的是,我们需要先申请一个结点,然后在通过遍历的方式要找到pos之前的那个结点。

有了这两个结点,那么我们就可以进行连接了。

然后是特殊情况,假如这个链表只有一个结点呢,这个结点正好就是pos,我们发现pos就没有前一个结点,其实这个就等效于头插。我们采用头插的方式即可

9.单链表在pos之后删除

//单链表在pos之后删除
void SListEraseAfter(SListNode* pos)
{
	assert(pos);
	assert(pos->next);
	SListNode* next = pos->next;
	pos->next = next->next;
	free(next);
	next = NULL;
}

单链表在pos之后删除的话,首先pos和pos的下一个结点不会为空,否则题目就矛盾了。所以我们得断言,然后我们直接记录pos 的下一个结点,然后连接pos和pos之后的结点。最后释放掉pos的下一个结点即可。

10.单链表在pos之前删除

//单链表在pos之前删除
void SListErasePrev(SListNode** pplist, SListNode* pos)
{
	assert(pos);
	assert(pplist);
	assert(pos != *pplist);
	assert(*pplist);

	if ((*pplist)->next == pos)
	{
		SListNode* del = *pplist;
		*pplist = pos;
		free(del);
		del = NULL;
	}
	else
	{
		SListNode* prev = *pplist;
		while (prev->next->next != pos)
		{
			prev = prev->next;
		}
		SListNode* del = prev->next;
		prev->next = pos;
		free(del);
		del = NULL;
	}
}

对于单链表在pos之前删除确实就比较复杂了,首先pos,pplist,*pplist肯定不可能为空,然后pos也绝不可以是头节点,所以pos!=*pplist

我们现在来思考,假设一般情况,链表很长,在中间位置是pos,删除pos的前一个结点,那么我们就需要找到pos 的前一个的前一个结点。然后记录pos的前一个结点。释放pos 的前一个结点,然后进行连接即可

对于特殊情况,也就是,pos在第二个结点上,这样我们无法找到pos的前一个的前一个结点,但是这个就是头删,我们直接采用类似的思路即可

11.单链表在pos位置的删除

//单链表在pos位置删除
void SListErase(SListNode** pplist, SListNode* pos)
{
	assert(pplist);
	assert(pos);
	assert(*pplist);
	
	if (*pplist == pos)
	{
		free(pos);
		*pplist = pos = NULL;
	}
	else
	{
		SListNode* prev = *pplist;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

这个与上一个接口是基本一致的思路。不同的是,pos可以在头结点了,如果是头节点就是头删。

如果不是,就是先找到前一个结点,然后进行删除连接即可

12.单链表的销毁

//单链表的销毁
void SListDestroy(SListNode** pplist)
{
	SListNode* cur = *pplist;
	while (cur != NULL)
	{
		SListNode* next = cur->next;
		free(cur);
		cur = next;
	}
}

对于单链表的销毁,这个也很简单,就直接遍历销毁即可,与打印和查找的思路是一致的

四、单链表的实现完整代码

SList.h文件

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<assert.h>

typedef int SLTDateType;

typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SListNode;

//单链表的打印
void SListPrint(SListNode* plist);
//单链表的尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
//单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
//单链表的尾删
void SListPopBack(SListNode** pplist);
//单链表的头删
void SListPopFront(SListNode** pplist);
//单链表的查找
SListNode* SListFind(SListNode* plist,SLTDateType x);
//单链表在pos位置之后插入
void SListInsertAfter(SListNode* pos, SLTDateType x);
//单链表在pos位置之前插入
void SListInsertPrev(SListNode** pplist, SListNode* pos, SLTDateType x);
//单链表在pos之后删除
void SListEraseAfter(SListNode* pos);
//单链表在pos之前删除
void SListErasePrev(SListNode** pplist, SListNode* pos);
//单链表在pos位置删除
void SListErase(SListNode** pplist, SListNode* pos);
//单链表的销毁
void SListDestroy(SListNode** pplist);

SList.c文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"

//单链表的打印
void SListPrint(SListNode* plist)
{
	SListNode* cur = plist;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

//获取一个结点
SListNode* BuySListNode(SLTDateType x)
{
	SListNode* tmp = (SListNode*)malloc(sizeof(SListNode));
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
	tmp->next = NULL;
	tmp->data = x;
	return tmp;
}

//单链表的尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{
	assert(pplist);
	SListNode* newnode = BuySListNode(x);
	if (*pplist == NULL)
	{
		*pplist = newnode;
		return;
	}
	SListNode* tail = (*pplist);
	while (tail->next != NULL)
	{
		tail = tail->next;
	}
	tail->next = newnode;
}

//单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{
	assert(pplist);
	SListNode* newnode = BuySListNode(x);
	SListNode* first = *pplist;
	*pplist = newnode;
	newnode->next = first;
}

//单链表的尾删
void SListPopBack(SListNode** pplist)
{
	assert(pplist);
	assert(*pplist);
	if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* tail = *pplist;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}

//单链表的头删
void SListPopFront(SListNode** pplist)
{
	assert(pplist);
	assert(*pplist);

	SListNode* second = (*pplist)->next;
	free(*pplist);
	*pplist = second;
}

//单链表的查找
SListNode* SListFind(SListNode* plist,SLTDateType x)
{
	SListNode* cur = plist;
	while (cur != NULL)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}
//单链表在pos位置之后插入
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
	assert(pos);
	SListNode* newnode = BuySListNode(x);
	SListNode* next = pos->next;
	pos->next = newnode;
	newnode->next = next;
}
//单链表在pos位置之前插入
void SListInsertPrev(SListNode** pplist, SListNode* pos, SLTDateType x)
{
	assert(pplist);
	assert(pos);
	assert(*pplist);

	SListNode* newnode = BuySListNode(x);
	if (*pplist == pos)
	{
		*pplist = newnode;
		newnode->next = pos;
	}
	else
	{
		SListNode* prev = *pplist;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = newnode;
		newnode->next = pos;
	}
}
//单链表在pos之后删除
void SListEraseAfter(SListNode* pos)
{
	assert(pos);
	assert(pos->next);
	SListNode* next = pos->next;
	pos->next = next->next;
	free(next);
	next = NULL;
}
//单链表在pos之前删除
void SListErasePrev(SListNode** pplist, SListNode* pos)
{
	assert(pos);
	assert(pplist);
	assert(pos != *pplist);
	assert(*pplist);

	if ((*pplist)->next == pos)
	{
		SListNode* del = *pplist;
		*pplist = pos;
		free(del);
		del = NULL;
	}
	else
	{
		SListNode* prev = *pplist;
		while (prev->next->next != pos)
		{
			prev = prev->next;
		}
		SListNode* del = prev->next;
		prev->next = pos;
		free(del);
		del = NULL;
	}
}
//单链表在pos位置删除
void SListErase(SListNode** pplist, SListNode* pos)
{
	assert(pplist);
	assert(pos);
	assert(*pplist);
	
	if (*pplist == pos)
	{
		free(pos);
		*pplist = pos = NULL;
	}
	else
	{
		SListNode* prev = *pplist;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}
//单链表的销毁
void SListDestroy(SListNode** pplist)
{
	SListNode* cur = *pplist;
	while (cur != NULL)
	{
		SListNode* next = cur->next;
		free(cur);
		cur = next;
	}
}

Test.c文件

#define _CRT_SECURE_NO_WARNINGS 1

#include"Slist.h"

void TestSList1()
{
	SListNode* phead = NULL;
	SListPushBack(&phead, 1);
	SListPushBack(&phead, 2);
	SListPushBack(&phead, 3);
	SListPushBack(&phead, 4);
	SListPushBack(&phead, 5);
	SListPrint(phead);

	SListPushFront(&phead, 6);
	SListPushFront(&phead, 7);
	SListPushFront(&phead, 8);
	SListPushFront(&phead, 9);
	SListPushFront(&phead, 10);
	SListPrint(phead);

	SListPopBack(&phead);
	SListPopBack(&phead);
	SListPopBack(&phead);
	SListPopBack(&phead);
	SListPrint(phead);
	SListPopBack(&phead);
	SListPrint(phead);

	

}
void TestSList2()
{
	SListNode* phead = NULL;
	SListPushBack(&phead, 1);
	SListPushBack(&phead, 2);
	SListPushBack(&phead, 3);
	SListPushBack(&phead, 4);
	SListPushBack(&phead, 5);
	SListPrint(phead);

	SListPopFront(&phead);
	SListPopFront(&phead);
	SListPopFront(&phead);
	SListPopFront(&phead);
	SListPrint(phead);
	SListPopFront(&phead);
	SListPrint(phead);
}
void TestSList3()
{
	SListNode* phead = NULL;
	SListPushBack(&phead, 1);
	SListPushBack(&phead, 2);
	SListPushBack(&phead, 3);
	SListPushBack(&phead, 4);
	SListPushBack(&phead, 5);
	SListPrint(phead);

	SListNode* pos = SListFind(phead, 3);
	pos->data = 100;
	SListPrint(phead);
	SListInsertAfter(pos, 200);
	SListInsertAfter(pos, 300);
	SListInsertAfter(pos, 400);
	SListInsertAfter(pos, 500);
	SListPrint(phead);

	SListNode* pos2 = SListFind(phead, 1);
	SListInsertPrev(&phead, pos2, 101);
	SListInsertPrev(&phead, pos2, 102);
	SListInsertPrev(&phead, pos2, 103);
	SListInsertPrev(&phead, pos2, 104);
	SListPrint(phead);

	SListEraseAfter(pos2);
	SListEraseAfter(pos2);
	SListEraseAfter(pos2);
	SListPrint(phead);
	

}


void TestSList4()
{
	SListNode* phead = NULL;
	SListPushBack(&phead, 1);
	SListPushBack(&phead, 2);
	SListPushBack(&phead, 3);
	SListPushBack(&phead, 4);
	SListPushBack(&phead, 5);
	SListPrint(phead);

	SListNode* pos = SListFind(phead, 5);

	SListErasePrev(&phead, pos);
	SListPrint(phead);
	SListErasePrev(&phead, pos);
	SListPrint(phead);


	SListErase(&phead, pos);
	SListPrint(phead);

	SListDestroy(&phead);
}

int main()
{
	//TestSList1();
	//TestSList2();
	//TestSList3();
	TestSList4();
	return 0;
}


本节内容到此位置,感谢您的阅读

如果对你有帮助的话,不要忘记点赞加收藏哦!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言实现单链表(超多配图,这下不得不学会单链表了)

目录 一&#xff1a;什么是链表&#xff1f; 二&#xff1a;创建源文件和头文件 (1)头文件 (2)源文件 三&#xff1a;实参和形参 四&#xff1a;一步步实现单向链表 &#xff08;1&#xff09;建立一个头指针并置空 &#xff08;2&#xff09;打印链表&#xff0c;便于…

人脸活体检测系统(Python+YOLOv5深度学习模型+清新界面)

摘要&#xff1a;人脸活体检测系统利用视觉方法检测人脸活体对象&#xff0c;区分常见虚假人脸&#xff0c;以便后续人脸识别&#xff0c;提供系统界面记录活体与虚假人脸检测结果。本文详细介绍基于YOLOv5深度学习技术的人脸活体检测系统&#xff0c;在介绍算法原理的同时&…

echart图表之highcharts

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、HighCharts是什么&#xff1f;二、使用步骤1.引入库2.前端代码3.展现结果4.后台自动截图总结前言 提示&#xff1a;这里可以添加本文要记录的大概内容&…

vue2前端实现html导出pdf功能

1. 功能实现方案 1.html转换成canvas后生成图片导出pdf&#xff08;本文选用&#xff09; html转canvas插件&#xff1a;html2canvas是一款将HTML代码转换成Canvas的插件&#xff1b;canvas生成pdf&#xff1a;jsPDF是一个使用Javascript语言生成PDF的开源库 2.HTML代码转出…

FPGA纯verilog实现RIFFA的PCIE通信,提供工程源码和软件驱动

目录1、前言2、RIFFA简介RIFFA概述RIFFA架构RIFFA驱动3、vivado工程详解4、上板调试验证并演示5、福利&#xff1a;工程代码的获取1、前言 PCIE是目前速率很高的外部板卡与CPU通信的方案之一&#xff0c;广泛应用于电脑主板与外部板卡的通讯&#xff0c;PCIE协议极其复杂&…

Python3实现AI版贪吃蛇

导语利用Python简单地实现AI版的贪吃蛇。。。just for fun...没有用深度学习。。。算法是由一个叫Hawstein的人在好多好多年以前提出&#xff0c;感觉很有趣&#xff0c;就花了点时间复现了一下他的想法。。。至于效果。。。看脸。。。真的只是觉得他的想法很有趣&#xff0c;仅…

AI_Papers周刊:第六期

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 2023.03.13—2023.03.19 文摘词云 Top Papers Subjects: cs.CL 1.UPRISE: Universal Prompt Retrieval for Improving Zero-Shot Evaluation 标题&#xff1a;UPRISE&#xff1a;改进零样本评估…

Java面向对象:接口的学习

本文介绍了Java中接口的基本语法, 什么是接口, java中的接口 语法规则, 接口的使用,接口的特性,如何实现多个接口,接口间的继承,以及抽象类和接口的区别 Java接口的学习一.接口的概念二.Java中的接口1.接口语法规则2.接口的使用3.接口的特性4.实现多个接口5.接口间的继承三.抽象…

Vue学习 -- 如何用Axios发送请求(get post)Promise对象 跨域请求问题

什么是Axios Vue本身是不支持发送axios请求&#xff0c;需要使用第三方插件&#xff0c;这里推荐使用Axios&#xff0c;Axios是基于promise的HTTP库&#xff1b;它会从浏览器中创建XMLHttpRequset对象。 安装Axios npm install axios -S下载后把axios.js文件复制进项目目录 …

QT串口助手开发3串口开发

系列文章目录 QT串口助手开发3串口开发 QT串口助手开发3系列文章目录一、UI界面程序的编写二、发送框程序编写一、UI界面程序的编写 根据上文的未解决问题&#xff1a;我们打开串口按钮打开后只能选择关闭串口&#xff0c;所以这个是循环的过程 上文链接 所以按钮对应的槽函数…

VR全景城市,用720全景树立城市形象,打造3D可视化智慧城市

随着城市化进程的加速&#xff0c;城市之间的竞争也日益激烈。城市管理者们需要寻求新的方式来提升城市的品牌形象和吸引力。在这个过程中&#xff0c;VR全景营销为城市提供了一种全新的营销手段&#xff0c;可以帮助提升城市的价值和吸引力。一、城市宣传新方式VR全景营销是一…

《Linux的权限》

本文主要对linux的一些基本权限进行讲解 文章目录前言Linux权限&#xff08;1&#xff09;权限的概念&#xff08;2&#xff09;linux下用户分类(root,普通)(3)linux的文件属性文件属性的分类文件权限修改文件权限1、chmod2、chown和chgrp3、fiile权限的三个重要的问题第一个问…

考研408每周一题(2019 41)

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

嵌入式学习笔记——STM32的时钟树

时钟树前言时钟树时钟分类时钟树框图LSI与LSEHSI、HSE与PLL系统时钟的产生举例AHB、APBx的时钟配置时钟树相关寄存器介绍1.时钟控制寄存器&#xff08;RCC_CR&#xff09;2.RCC PLL 配置寄存器 (RCC_PLLCFGR)3.RCC 时钟配置寄存器 (RCC_CFGR)4.RCC 时钟中断寄存器 (RCC_CIR)修改…

基于 Apache Flink 的实时计算数据流业务引擎在京东零售的实践和落地

摘要&#xff1a;本文整理自京东零售-技术研发与数据中心张颖&闫莉刚在 ApacheCon Asia 2022 的分享。内容主要包括五个方面&#xff1a; 京东零售实时计算的现状实时计算框架场景优化&#xff1a;TopN场景优化&#xff1a;动线分析场景优化&#xff1a;FLINK 一站式机器学…

软件测试面试找工作你必须知道的面试技巧(帮助超过100人成功通过面试)

目录 问题一&#xff1a;“请你自我介绍一下” 问题二&#xff1a;“谈谈你的家庭情况” 问题三&#xff1a;“你有什么业余爱好?” 问题四&#xff1a;“你最崇拜谁?” 问题五&#xff1a;“你的座右铭是什么?” 问题六&#xff1a;“谈谈你的缺点” 问题七&#xff…

【React】React入门--生命周期

&#x1f380;个人主页&#xff1a;努力学习前端知识的小羊 感谢你们的支持&#xff1a;收藏&#x1f384; 点赞&#x1f36c; 加关注&#x1fa90; 文章目录React生命周期初始化阶段运行中阶段销毁阶段老生命周期的问题新生命周期代替性能优化的方案重要的勾子即将废弃的勾子R…

Qt之QPainter绘制多个矩形/圆形(含源码+注释)

一、绘制示例图 下图绘制的是矩形对象&#xff0c;但是将绘制矩形函数&#xff08;drawRect&#xff09;更改为绘制圆形&#xff08;drawEllipse&#xff09;即可绘制圆形。 二、思路解释 绘制矩形需要自然要获取矩形数据&#xff0c;因此通过鼠标事件获取每个矩形的rect数…

“国产版ChatGPT”文心一言发布会现场Demo硬核复现

文章目录前言实验结果一、文学创作问题1 :《三体》的作者是哪里人&#xff1f;问题2&#xff1a;可以总结下三体的核心内容吗&#xff1f;如果要续写的话&#xff0c;可以从哪些角度出发&#xff1f;问题3&#xff1a;如何从哲学角度来进行续写&#xff1f;问题4&#xff1a;电…

学习28个案例总结

学习前 对于之前遇到的问题没有及时总结&#xff0c;导致做什么事情都是新的一样。没有把之前学习到接触到的内容应用上。通过这次对28个案例的学习。把之前遇到的问题总结成自己的经验&#xff0c;在以后的开发过程中避免踩重复性的坑。多看帮助少走弯路。 学习中 对28个案例…
最新文章