数据结构:为什么说链表是顺序表的升级版(c语言实现)

前言:

    我们在之前的几篇文章中详细的讲解了顺序表的特点,增删改查操作和动态顺序表的优点,并使用顺序表的底层结构实现了通讯录项目,似乎顺序表是一个非常完美的数据结构,它可以实现按照需求实现增删查改,对内存的控制也较为合理,空间都是在需要时手动开辟的。但是顺序表真的完美吗?事实上它并不完美,经过我们思考,顺序表还是存在一些问题,例如:(1)顺序表中间/头部的插入删除,时间复杂度为O(N) ( 2) 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。 (3)增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们 再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。这些问题都是我们应该考虑的,而我们接下来要介绍的另一种数据结构——链表,就能在实现属性表的各个功能的前提下很好的解决这些问题。

1.链表

1.1 链表的概念及结构

    链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。换句话说,链表就是一块一块的空间由指针像链子一样将它们链接起来了,方便我们去访问链表的每一个节点。

    它的结构像极了图中的小火车:

我们来看看真实链表的结构 :

      链表是由一个指针指向链表的头节点,每个节点分为两个部分,分别是数据部分和指针部分,数据部分负责存储我们要存储的数据,指针部分负责存储下一个节点的地址,链表的每一个节点都储存着下一个节点的地址,最后一个节点的指针存储的是空指针,这使我们能够很方便的访问整个链表。

链表的特点:

1.从图中可以看出,链表的的结构在逻辑上是连续的,但是在物理上不一定连续。

2.每一个节点一般都是从堆上申请的。

3.从堆上申请的空间,是按照一定的策略分配的,两次申请的空间可能连续,可能不连续

1.2 链表的分类

链表的实际分类种类多达八种:

     链表分为单向和双向链表,带头和不带头链表,循环或者非循环链表,虽然由有这么多种链表,但是我们实际使用时最常用的只有两种:分别是无头单向非循环链表和带头双向循环链表。

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

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

1.3 单链表的实现

    我们这一期用c语言代码来实现单链表。与顺序表相同,我们将实现链表的文件分成三个,分别是头文件SList.h文件,SLIst.c方法实现文件和测试文件test.c文件:

1.3.2 链表实现 

     链表由数据和指针两部分组成(前面已经详细解释),由于我们不确定会存储哪种类型的数据,使用typedef来作为我们的数据类型,要更数据类型时只需要更改typedef重命名的数据类型就可以:

 1、无头+单向+非循环链表增删查改实现
typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;

}SLTNode;

在这里我们使用的是int类型,最后将链表的名字改为SLTNode。

实现一个顺序表要实现许多方法,链表也是如此:

SLTNode* SLTBuyNode(SLTDataType x);//申请一个节点
void SLTPushBack(SLTNode** pphead, SLTDataType x);//尾删

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

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

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

void SLTPosFront(SLTNode** pphead);//头删
 
SLTNode* SLTFind(SLTNode** pphead, SLTDataType x);//查找

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//指定位置之前插入

void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//指定位置之后插入

void SLTErase(SLTNode** pphead, SLTNode* pos);
//指定位置删除

void SLTEraseAfter(SLTNode* pos);
//指定位置之后删除

void SLTDestory(SLTNode** pphead);
//销毁
(1)申请一个节点 

     增删查改,只有里面有数据才能使用其他三个功能,所以我们首先实现插入功能,每次插入数据又需要申请一块空间,这会使得程序多出许多相同的代码,所以我们将申请空间封装成一个函数,在我们需要插入数据时,调用这个函数就可以了。而实现这个函数也非常的简单,只需要申请一块空间,将我们要插入的数据给它的data,next指针指向空就可以了,执行完这些操作后,返回这块空间:

SLTNode* SLTBuyNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;

}//申请节点
(2)尾插 

     尾插我们需要考虑到我们要插入数据,传递的是一级指针的地址,就需要用二级指针来接收,传过来的指针不能为空,否则会出现非法访问的错误,如果传过来的是一个空链表,我们直接让头执针指向我们新开辟的空间就可以,如果不是链表,我们定义一个尾指针,让它遍历整个链表最终走到结尾,然后让它指向我们新开辟的节点,实现尾插操作:

void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = SLTBuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		tail->next = newnode;

	}
}
//尾插
(3)头插 

  头插的操作比较简单,只需要申请一个新节点,让它的next指针指向头节点,再让头指针指向它:

void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}//头插
(4)查找

     为什么先讲查找呢?因为我们接下来的指定位置插入和删除需要用到它。我们要先判断传过来的地址和指针是否为空,如果为空,则没有查找的必要,确保地址和指针都不为空的情况下,我们才能进行查找操作。将链表遍历,如果发现数据内容相等,则视为找到了,返回这个节点,如果遍历完整个数组还没有找到这个节点,则说明链表里面没有这个节点,返回空指针:

SLTNode* SLTFind(SLTNode** pphead, SLTDataType x)
{
	assert(pphead && *pphead);
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}//查找函数
(5)指定位置之前插入 

  我们首先要确保传过来的地址和链表不为空,指定的那个节点也不能为空,如果为空,我们就无法执行插入操作,还要考虑如果我们指定的节点就是头节点和指定的节点在后面的情况能不能用同一种方法解决,我们发现这两种情况不能用同一种方法解决,所以如果指定的位置就是头节点,我们就使用头插的方法,如果在后面,就需要找到指定位置的前一个节点,让新节点的next指针指向我们指定的那个节点,然后让指定位置的前一个节点的next指针指向新节点:

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead && *pphead);
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	if (*pphead == pos)
	{
		SLTPushFront(pphead,x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SLTNode* next = prev->next;
		newnode->next = next;
		prev->next = newnode;
	}

}//指定位置之前插入数据
(6)指定位置之后插入

    指定位置之后插入数据需要将它的next指针存储起来,让新节点的next指针存储它的next指针,再将我们指定节点的next指针存储新节点的地址:

void SLTInsertAfter( SLTNode* pos, SLTDataType x)
{
	
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}//指定位置后插入
(7)尾删 

      删除操作我们要确保链表不能为空,传过来的地址也不能为空,而链表只有一个节点和多个节点的情况也是不同的,如果链表只有一个节点,我们只要将它置空就可以了,如果有多个节点,我们则需要遍历链表,先将最后一个节点释放,然后将指向它的前一个节点的next指针置空:

void SLTPosBack(SLTNode** pphead)
{
	(pphead && *pphead);
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* pcur = *pphead;
		SLTNode* prev = *pphead;
		while (pcur->next != NULL)
		{
			prev = pcur;
			pcur = pcur->next;
		}
		free(pcur);
		prev->next = NULL;
		pcur = prev;
	}
}//尾删
(8)头删 

    头删的实现也比较简单,只需要将头节点的next指针存起来,然后将头节点指向的空间释放,最后让头节点指向我们存起来的next指针:

void SLTPosFront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}//头删
(9)指定位置删除 

     删除也要确保地址和指针不能为空,我们指定的节点也不能为空,否则无法进行删除操作,链表内只有一个节点和有多个节点的情况也是不一样的,如果只有一个节点,我们调用头删函数就可以了,如果有多个节点,就需要找到指定位置的前一个节点,将我们指定的节点释放后,再将它的前一个节点的next指针置空:

void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);
	SLTNode* prev = *pphead;
	if (pos == *pphead)
	{
		SLTPosFront(pphead);
	}
	else
	{
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
	
}
//指定位置删除
(10)指定位置之后删除 

   我们要确保链表不能为空,也要确保链表呢有两个及以上的节点,否则我们无法指定删除某一个节点的后一个节点。先将我们要删除的节点的后一个节点存起来,将我们删除的节点释放后,用它的前一个节点的next指针指向那个存起来的节点,我们就能实现指定位置后删除:

void SLTEraseAfter(SLTNode* pos)
{
	assert(pos&&pos->next);
	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}//指定位置之后删除
(11)打印链表数据 

   打印链表要先确保链表不为空,为空则无法调用该函数,如果确保不为空,我们只需要遍历打印就可以:

void SLTPrint(SLTNode* phead)
{
	assert(phead);
	while (phead)
	{
		printf("%d->", phead->data);
		phead = phead->next;
	}
	printf("NULL\n");
}//打印
(12)销毁链表

   由于链表的空间是使用malloc在堆上开辟的,只有在程序结束之后才会释放,所以我们使用完这些空间后要手动释放:

(13)测试

  实现完所有的方法,我们来测试一下吧:

我们调用这些方法都是没有问题的。 

以上就是这一期单链表的所有内容了,它不需要像顺序表一样插入删除数据时要将数据频繁挪动,空间也是插入一个数据开辟一块空间,毫无疑问它称得上是顺序表的升级版,我将源码放在下面,感兴趣的小伙伴可以试试哦。

SList.h :

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;

}SLTNode;

SLTNode* SLTBuyNode(SLTDataType x);//申请一个节点
void SLTPushBack(SLTNode** pphead, SLTDataType x);//尾删

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

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

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

void SLTPosFront(SLTNode** pphead);//头删
 
SLTNode* SLTFind(SLTNode** pphead, SLTDataType x);//查找

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//指定位置之前插入

void SLTInsertAfter( SLTNode* pos, SLTDataType x);
//指定位置之后插入

void SLTErase(SLTNode** pphead, SLTNode* pos);
//指定位置删除

void SLTEraseAfter(SLTNode* pos);
//指定位置之后删除

void SLTDestory(SLTNode** pphead);
//销毁

SList.c :

#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"

SLTNode* SLTBuyNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;

}//申请节点
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = SLTBuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		tail->next = newnode;

	}
}
//尾插

void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}//头插
void SLTPosBack(SLTNode** pphead)
{
	(pphead && *pphead);
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* pcur = *pphead;
		SLTNode* prev = *pphead;
		while (pcur->next != NULL)
		{
			prev = pcur;
			pcur = pcur->next;
		}
		free(pcur);
		prev->next = NULL;
		pcur = prev;
	}
}//尾删

void SLTPosFront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}//头删

SLTNode* SLTFind(SLTNode** pphead, SLTDataType x)
{
	assert(pphead && *pphead);
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}//查找函数

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead && *pphead);
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	if (*pphead == pos)
	{
		SLTPushFront(pphead,x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SLTNode* next = prev->next;
		newnode->next = next;
		prev->next = newnode;
	}

}//指定位置之前插入数据

void SLTInsertAfter( SLTNode* pos, SLTDataType x)
{
	
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}//指定位置后插入
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);
	SLTNode* prev = *pphead;
	if (pos == *pphead)
	{
		SLTPosFront(pphead);
	}
	else
	{
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
	
}
//指定位置删除
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos&&pos->next);
	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}//指定位置之后删除
void SLTDestory(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* pcur = *pphead;
	
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}//销毁
void SLTPrint(SLTNode* phead)
{
	assert(phead);
	while (phead)
	{
		printf("%d->", phead->data);
		phead = phead->next;
	}
	printf("NULL\n");
}//打印

test.c :

#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"


void test02()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);

	SLTPrint(plist);

	SLTPushFront(&plist, 9);
	SLTPushFront(&plist, 89);
	SLTPushFront(&plist, 9);
	SLTPrint(plist);

	SLTPosBack(&plist);
	SLTPrint(plist);

	SLTPosFront(&plist);
	/*SLTPosFront(&plist);
	SLTPosFront(&plist);
	SLTPosFront(&plist);
	SLTPosFront(&plist);
	SLTPosFront(&plist);*/
	SLTPrint(plist);

	SLTNode* find = SLTFind(&plist,2);
	SLTEraseAfter(find);
	//SLTErase(&plist, find);
	/*SLTInsertAfter(&plist, find, 32);*/
	SLTPrint(plist);
	SLTDestory(&plist);
	
	

	/*SLTInsert(&plist, find, 8);
	SLTPrint(plist);*/

	/*if (find == NULL)
	{
		printf("找不到!\n");
	}
	else
	{
		printf("找到了!\n");
	}*/
}
int main()
{
	
	test02();
   //test01();
	return 0;
}

 

 

 

    

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

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

相关文章

由于bug造成truncate table卡住问题

客户反应truncate table卡主&#xff0c;检查awr发现多个truncate在awr报告期内一直没执行完&#xff0c;如下&#xff1a; 检查ash&#xff0c;truncate table表的等待事件都是“enq: RO - fast object reuse”和“local write wait” 查找“enq: RO - fast object reuse”&am…

2024年能源电力行业CRM研究报告

中国能源电力行业属于大制造业的重要组成部分&#xff0c;在国民经济中的地位举足轻重。据统计&#xff0c;近十年来能源电力行业的整体投资呈现出增长趋势&#xff0c;尤其是“十四五”期间增长显著&#xff0c;2022年全国主要电力企业共完成投资12470亿元&#xff0c;同比增长…

Nuxt3 [Vue warn]: Hydration node mismatch:【解决方案】

[Vue warn]: Hydration node mismatch: 水合节点不匹配 Server rendered element contains more child nodes than client vdom. 服务器呈现的元素包含的子节点多于客户端vdom。 这个问题解决起来也很好解决&#xff0c;看这个问题是怎么出来的&#xff0c;看代码&#xff1a;…

vs工程添加属性表

一、简介 1、 vs工程属性表以&#xff08;.props&#xff09;为后缀 2、 作用&#xff1a;当多个工程需要配置很多相同的属性配置时方便同步&#xff0c;比如多个工程需要链接相同的头文件&#xff0c;库文件&#xff0c;输出路径&#xff0c;中间目录等 3、本章内容测试环境&a…

Mybatis框架的缓存

Mybatis框架的缓存 一.为什么使用缓存 缓存(cache&#xff09;的作用是为了减去数据库的压力&#xff0c;提高查询性能。缓存实现的 原理是从数据库中查询出来的对象在使用完后不要销毁&#xff0c;而是存储在内存&#xff08;缓存&#xff09; 中&#xff0c;当再次需要获取…

做好海外ASO优化的7大核心要素你了解几个?

海外App进行ASO优化时&#xff0c;需要综合考虑多个方面以确保应用在应用商店中获得更高的曝光率和下载量。以下是一些关键的ASO优化步骤&#xff0c;结合参考文章中的相关信息进行详细阐述&#xff1a; 1.关键词优化 调研目标市场的用户行为和检索习惯&#xff0c;挖掘与应用…

Java和C语言中基础概念中的区别有哪些?

Java和C语言中基础概念中的区别有哪些&#xff1f; 标识符数据类型运算符加号%号& 和 | 关系表达式函数声明代码规范数组 以下是Java和C语言在一些基础概念中的区别&#xff08;不包含面向对象等的高级知识&#xff09; 标识符 在Java中&#xff0c;标识符可以由数字、字母…

opencv中文路径问题

目的 在windows系统上&#xff0c;就是直接用QT的utf8编码作为图片路径用在opencv读取或者写入函数&#xff0c;在路径当中含有中文时&#xff0c;会提示编码错误。 就是解决opencv中的中文路径的问题。 情况 代码如下&#xff1a; #pragma execution_character_set("…

Ubuntu系统通过GRUB引导菜单进入恢复模式修改账户密码

当在Ubuntu系统中忘记了账户密码时&#xff0c;有几种方法可以破解或重置密码。 本指引文档方法&#xff1a;通过GRUB引导菜单进入恢复模式 实践环境为&#xff1a;20.04.6 LTS (Focal Fossa) 1. 重启Ubuntu系统&#xff1a;首先&#xff0c;你需要重启你的Ubuntu系统。 2. …

NSSCTF-Web题目14

目录 [CISCN 2019华东南]Web11和[NISACTF 2022]midlevel 1、题目 2、知识点 3、思路 [HDCTF 2023]SearchMaster 1、题目 2、知识点 3、思路 [CISCN 2019华东南]Web11和[NISACTF 2022]midlevel 这两道题目一样 1、题目 2、知识点 SSTI&#xff08;服务端模板注入漏洞&…

Vue74-路由传参2

一、$route中的params参数 二、在配置路由的index.js文件中&#xff0c;声明传参 占位符用的什么名字&#xff0c;params里面的key就是什么。 三、<router-link>标签中传参 3-1、to字符串写法 3-2、to的对象写法 注意&#xff1a;若是用params携带参数&#xff0c;不…

如何下载油管视频

文章目录 1、IDM下载1.1 安装IDM工具1.2 浏览器安装IDM插件 2、命令行工具下载2.1 youtube-dl工具2.1.1 安装使用2.1.2 更新工具 2.2 yt-dlp 工具2.2.1 安装使用2.2.2 保存路径查看当前工作目录指定下载目录示例 2.2.3 保存文件名2.2.4 避坑指南1、请求被拒绝2、其他问题 在全球…

汇凯金业:现货黄金投资平仓策略有哪些

现货黄金作为全球投资者广泛关注与参与的财富增值途径&#xff0c;其双向交易制度为市场参与者在不同行情下提供了盈利的可能。然而&#xff0c;如何在波动的市场中把握最佳的平仓时机&#xff0c;从而最大化收益&#xff0c;是所有投资者心中的疑问。正确的平仓策略可以说是现…

【html】用html+css实现银行的账户信息表格

我们先来看一看某银行的账户信息表格 我们自己也可以实现类似的效果 效果图: 大家可以看到&#xff0c;其实效果差不多 接下来看看我们实现的代码 源码&#xff1a; <!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"&…

Claude 3.5 强势出击:解析最新AI模型的突破与应用

近年来&#xff0c;人工智能领域的发展迅猛&#xff0c;各大科技公司纷纷推出了自家的高级语言模型。在这场技术竞赛中&#xff0c;Anthropic的Claude系列模型凭借其强大的性能和创新的功能脱颖而出。最近&#xff0c;Anthropic发布了Claude 3.5 Sonnet模型&#xff0c;引起了广…

尚硅谷爬虫学习第一天(3) 请求对象定制

#url的组成 #协议 http&#xff0c;https&#xff0c;一个安全&#xff0c;一个不安全。 #主机&#xff0c; 端口号 学过java 的肯定知道 沃日&#xff0c;以前面试运维的时候&#xff0c;问到主机地址&#xff0c;我懵逼了下&#xff0c;回了个8080 # 主机地址 80 # …

气象数据NC、grb2解析成矢量json、CMIS、MICPS及图片应用到webgis

一、基础概念 气象数据通常以多种格式存储和交换&#xff0c;以适应不同的应用需求和处理工具。以下是一些常见的气象数据格式及其转换方法的概述&#xff1a; 常见气象数据格式 1. NetCDF&#xff08;Network Common Data Form&#xff09;&#xff1a;一种自描述、自包含的…

用VScode打开keil下的文件中文编码乱码的问题,以及利用VScode转换字符编码的方法

目录 问题描述 解决方法 利用VScode转换字符编码的方法 问题描述 keil中默认的编码是ANIS如下图所示。 而VScode中默认的编码为UTF-8 &#xff0c;打开后如下。 解决方法 建议另存后&#xff0c;再打开目标文件&#xff0c;防止误操作&#xff01; 在VScode的最下方可以找…

海豚调度调优 | 正在运行的工作流(DAG)如何重新拉起失败的任务(Task)

&#x1f4a1; 本系列文章是DolphinScheduler由浅入深的教程&#xff0c;涵盖搭建、二开迭代、核心原理解读、运维和管理等一系列内容。适用于想对 DolphinScheduler了解或想要加深理解的读者。 *祝开卷有益。 * 本系列教程基于 DolphinScheduler 2.0.5 做的优化。&#xff…

javaSE字符串学习笔记

API和API帮助文档 API API(Application Programming Interface)&#xff1a;应用程序编程接口简单理解&#xff1a;API酒啊别人已经写好的东西&#xff0c;我们不需要自己编写&#xff0c;直接使用即可。 API这个术语在编程圈中非常常见.我第一次接触API这个词语是在大一下。老…