[数据结构]—带头双向循环链表——超详解

news2024/11/27 0:44:20

💓作者简介🎉:在校大二迷茫大学生

💖个人主页🎉:小李很执着

💗系列专栏🎉:数据结构

每日分享✨:旅行是为了迷路,迷路是为了遇上美好❣️❣️❣️

目录

💓1.总体布局

 💓2.详细解读

❣️1.创建双向链表节点

❣️2.初始化双向循环链表

❣️3.打印双向循环链表

❣️4.循环双向链表尾插

❣️5.双向循环链表中删除尾节点

❣️6.双向链表的头插操作

❣️7.双向链表的头部删除操作

❣️8.循环链表中查找指定值节点

❣️9.该函双向链表中指定节点pos的前面插入一个新的节点

❣️10.双向链表中删除某个节

❣️11.销毁一个循环双向链表

💓3.部分代码进阶

❣️1.根据2—9:void LTInsert(LTNode* pos, LTDataType x)

❣️2.根据2—10void LTErase(LTNode* pos)

💓4.整体代码

❣️1.List.h

❣️2.List.c

❣️3.Test.c


💓1.总体布局

1.创建双向链表节点

LTNode* CreateLTNode(LTDataType x);

2.初始化双向循环链表

LTNode* LTInit();

3.打印双向循环链表
void LTPrint(LTNode* phead);

4.循环双向链表尾插
void LTPushBack(LTNode* phead, LTDataType x);

5.双向循环链表中删除尾节点
void LTPopBack(LTNode* phead);

6.双向链表的头插操作

void LTPushFront(LTNode* phead, LTDataType x);

7.双向链表的头部删除操作
void LTPopFront(LTNode* phead);

8.循环链表中查找指定值节点

LTNode* LTFind(LTNode* phead, LTDataType x);

9.该函双向链表中指定节点pos的前面插入一个新的节点
void LTInsert(LTNode* pos, LTDataType x);

10.双向链表中删除某个节
void LTErase(LTNode* pos);

11.销毁一个循环双向链表

void LTDestroy(LTNode * phead); 

 💓2.详细解读

❣️1.创建双向链表节点

函数输入参数为节点的值x,函数返回一个指向节点的指针。

函数内部实现:

  1. 使用malloc函数为新节点分配内存空间,分配的大小为一个LTNode结构体的大小。

  2. 判断内存分配是否成功,如果分配失败,则输出错误信息并退出程序。

  3. 对新节点进行初始化,将节点的值设置为x,next指针和prev指针设置为NULL。

  4. 返回指向新节点的指针。

LTNode* CreateLTNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->val = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}

❣️2.初始化双向循环链表

链表中的每个节点都是LTNode类型的结构体,其中包含一个指向前一个节点的指针prev和一个指向后一个节点的指针next。该函数首先创建一个值为-1的头节点,并将头节点的前一个节点和后一个节点都指向头节点本身,以形成一个空的双向循环链表。最后返回头节点的指针。

LTNode* LTInit()
{
	LTNode* phead = CreateLTNode(-1);
	phead->next = phead;
	phead->prev = phead;

	return phead;
}

❣️3.打印双向循环链表

其参数为双向循环链表的头结点指针,函数内部会从头结点开始遍历链表,并依次打印每个节点的值,直到遍历到头结点为止。最终输出的内容是形如“哨兵位<=>x<=>y<=>z<=>哨兵位”的字符串,其中x、y、z分别表示链表中的元素值。

void LTPrint(LTNode* phead)
{
	assert(phead);
	printf("哨兵位<=>");

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d<=>", cur->val);
		cur = cur->next;
	}
	printf("\n");
}

❣️4.循环双向链表尾插

将一个元素x插入到链表的最后一个节点的后面。

函数接收两个参数,一个是指向链表头结点的指针phead,另一个是要插入到链表尾部的元素x。

首先使用assert函数检查参数phead是否为NULL,如果是则直接终止程序。

接着定义两个指针tail和newnode,tail指向链表的最后一个节点,newnode是要插入到链表尾部的新节点。

然后将新节点插入到链表尾部。具体步骤如下:

  1. 让tail节点的next指针指向newnode节点,即tail->next = newnode。

  2. 让newnode节点的prev指针指向tail节点,即newnode->prev = tail。

  3. 让newnode节点的next指针指向链表头节点phead,即newnode->next = phead。

  4. 让phead节点的prev指针指向newnode节点,即phead->prev = newnode。

这样就完成了在链表尾部插入新节点的操作。

void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* tail = phead->prev;
	LTNode* newnode = CreateLTNode(x);
	// phead               tail  newnode
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}

❣️5.双向循环链表中删除尾节点

具体分析如下:

  1. 首先使用assert函数来判断phead是否为空,如果为空则程序立即终止。

  2. 由于是双向循环链表,在删除尾节点之前需要判断链表中是否存在节点。使用assert函数来判断phead的next指针是否指向phead本身,如果是则链表为空,程序立即终止。

  3. 设置指针tail指向链表的尾节点,并使用tailPrev指针来记录尾节点的前一个节点。

  4. 释放tail指向的节点,即删除尾节点。

  5. 将tailPrev节点的next指针指向phead节点,即将链表尾节点删除后,将尾节点的前一个节点的next指针指向头节点。

  6. 将phead节点的prev指针指向tailPrev节点,即将链表尾节点删除后,将头节点的prev指针指向链表的倒数第二个节点,以保证链表仍然是双向循环的。

注意,该函数的前提条件是链表中至少存在一个节点,否则会因为assert函数判断失败而终止程序。在使用该函数时需要注意链表的状态。

void LTPopBack(LTNode* phead)
{
	assert(phead);
	// 空
	assert(phead->next != phead);
	LTNode* tail = phead->prev;
	LTNode* tailPrev = tail->prev;
	free(tail);
	tailPrev->next = phead;
	phead->prev = tailPrev;

}

❣️6.双向链表的头插操作

将一个新节点插入到链表的第一个位置。

输入参数:

  • phead:头结点指针,其中包含链表的头指针和尾指针;
  • x:要插入的节点的值。

函数流程:

  1. 创建新节点,其数据域为x;
  2. 获取原链表中第一个节点的指针first;
  3. 将新节点插入到头结点之后的位置,使得新节点为原链表的第一个节点,first成为新节点的后继节点;
  4. 将原第一个节点的prev指针指向新节点,完成头插操作。

注意事项:

  • 函数中使用了assert宏,用于判断头结点是否存在;
  • 操作需要改变多个节点的指针,需要仔细考虑顺序和细节。
void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = CreateLTNode(x);
	LTNode* first = phead->next;
	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = first;
	first->prev = newnode;

}

❣️7.双向链表的头部删除操作

1. 首先使用assert函数判断传入的链表头结点指针phead是否为空,如果为空则终止程序运行。

2. 再通过assert函数判断链表是否为空,即头结点的下一个结点是否还是头结点自身,如果是则说明链表为空,同样终止程序运行。

3. 取出链表头结点的下一个结点first,以及第二个结点second。

4. 将头结点的下一个结点指向第二个结点,同时将第二个结点的前一个结点指向头结点,完成删除操作。

5. 最后使用free函数释放被删除的结点first的内存空间,并将first指针置为空。

void LTPopFront(LTNode* phead)
{
	assert(phead);
	// 空
	assert(phead->next != phead);
	LTNode* first = phead->next;
	LTNode* second = first->next;
	phead->next = second;
	second->prev = phead;
	free(first);
	first = NULL;
	
}

❣️8.循环链表中查找指定值节点

参数说明:

  • phead:指向循环链表头节点的指针。
  • x:需要查找的值。

函数实现步骤:

  1. 首先,断言链表头节点不为空。
  2. 定义一个指针 cur,指向链表的第一个节点。
  3. 遍历链表,如果找到了值为 x 的节点,直接返回该节点指针。
  4. 如果遍历完整个链表都没有找到值为 x 的节点,就返回 NULL 指针,表示没有找到。

需要注意的是,该函数是针对循环链表的查找实现,因此需要判断 cur 指针是否回到了头节点 phead,如果回到了头节点,则表示遍历完整个链表,需要退出循环。

LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->val == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

❣️9.该函双向链表中指定节点pos的前面插入一个新的节点

节点的值为x。函数实现需要注意以下几点:

  1. 首先需要判断pos节点是否存在,若不存在则直接返回。

  2. 创建一个新节点newnode,并将其值赋为x。

  3. 获取pos节点的前一个节点posPrev,posPrev节点和newnode节点之间需要插入新的节点。

  4. 将posPrev节点的next指针指向newnode节点,将newnode节点的prev指针指向posPrev节点,将newnode节点的next指针指向pos节点,将pos节点的prev指针指向newnode节点。

  5. 确保插入操作顺利完成后,函数返回。

// 在pos前面的插入
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

	LTNode* posPrev = pos->prev;
	LTNode* newnode = CreateLTNode(x);
	// posprev newnode pos
	posPrev->next = newnode;
	newnode->prev = posPrev;
	newnode->next = pos;
	pos->prev = newnode;
}

❣️10.双向链表中删除某个节

输入参数是要删除的节点指针pos。

首先通过断言语句assert(pos)来检查输入参数是否为空。

然后通过pos指针找到它的前驱节点posPrev和后继节点posNext,将它们之间的连接断开,即将posPrev的next指针指向posNext,将posNext的prev指针指向posPrev。

最后通过free函数释放pos指向的内存空间,完成删除操作。

// 删除pos位置
void LTErase(LTNode* pos)
{
	assert(pos);

	LTNode* posNext = pos->next;
	LTNode* posPrev = pos->prev;

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

❣️11.销毁一个循环双向链表

参数phead是链表的头指针,其指向一个LTNode类型的结构体,该结构体中有两个指针,分别指向链表的头节点和尾节点。

首先,函数中使用了断言assert(phead),判断参数phead是否为空指针,如果是,程序会终止运行,有助于在调用函数时发现错误。

然后,定义一个指针cur指向链表第一个节点,然后使用while循环遍历除了头节点之外的所有节点,直到遍历完所有节点为止。

在循环中,使用一个指针next指向当前节点的下一个节点,然后释放当前节点的内存空间,最后将cur指向下一个节点。

循环结束后,释放链表头节点的内存空间,销毁整个链表。

void LTDestroy(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
     free(phead);
}

💓3.部分代码进阶

❣️1.根据2—9:void LTInsert(LTNode* pos, LTDataType x)

 1.循环双向链表尾插操作

void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTInsert(phead, x);
}

2. 双向链表的头插操作

void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTInsert(phead->next, x);
}

❣️2.根据2—10void LTErase(LTNode* pos)

1.双向循环链表中删除尾节点

void LTPopBack(LTNode* phead)
{
	assert(phead);
	LTErase(phead->prev);
}

2. 双向链表的头部删除操作

void LTPopFront(LTNode* phead)
{
	assert(phead);
	// 空
	assert(phead->next != phead);
	LTErase(phead->next);
}

💓4.整体代码

❣️1.List.h

#pragma once

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

typedef int LTDataType;

typedef struct ListNode
{
	struct ListNode* next;
	struct ListNode* prev;
	LTDataType val;
}LTNode;

LTNode* CreateLTNode(LTDataType x);
LTNode* LTInit();
void LTPrint(LTNode* phead);
void LTPushBack(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);

void LTPushFront(LTNode* phead, LTDataType x);
void LTPopFront(LTNode* phead);

LTNode* LTFind(LTNode* phead, LTDataType x);


void LTInsert(LTNode* pos, LTDataType x);

void LTErase(LTNode* pos);

void LTDestroy(LTNode * phead); 

❣️2.List.c

#include"List.h"



LTNode* CreateLTNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	newnode->val = x;
	newnode->next = NULL;
	newnode->prev = NULL;

	return newnode;
}

LTNode* LTInit()
{
	LTNode* phead = CreateLTNode(-1);
	phead->next = phead;
	phead->prev = phead;

	return phead;
}

void LTPrint(LTNode* phead)
{
	assert(phead);
	printf("哨兵位<=>");

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d<=>", cur->val);
		cur = cur->next;
	}
	printf("\n");
}

void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);

	//LTNode* tail = phead->prev;
	//LTNode* newnode = CreateLTNode(x);

	 phead               tail  newnode
	//tail->next = newnode;
	//newnode->prev = tail;
	//newnode->next = phead;
	//phead->prev = newnode;

	LTInsert(phead, x);
}

void LTPopBack(LTNode* phead)
{
	assert(phead);

	// 空
	assert(phead->next != phead);

	/*LTNode* tail = phead->prev;
	LTNode* tailPrev = tail->prev;

	free(tail);
	tailPrev->next = phead;
	phead->prev = tailPrev;*/

	LTErase(phead->prev);
}

//void LTPushFront(LTNode* phead, LTDataType x)
//{
//	assert(phead);
//	LTNode* newnode = CreateLTNode(x);
//
//	newnode->next = phead->next;
//	phead->next->prev = newnode;
//
//	phead->next = newnode;
//	newnode->prev = phead;
//
//}

void LTPushFront(LTNode* phead, LTDataType x)
{
	/*assert(phead);
	LTNode* newnode = CreateLTNode(x);
	LTNode* first = phead->next;

	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = first;
	first->prev = newnode;*/

	LTInsert(phead->next, x);
}

void LTPopFront(LTNode* phead)
{
	assert(phead);
	// 空
	assert(phead->next != phead);

	/*LTNode* first = phead->next;
	LTNode* second = first->next;

	phead->next = second;
	second->prev = phead;
	free(first);
	first = NULL;*/
	LTErase(phead->next);
}

LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->val == x)
		{
			return cur;
		}

		cur = cur->next;
	}

	return NULL;
}

// 在pos前面的插入
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

	LTNode* posPrev = pos->prev;
	LTNode* newnode = CreateLTNode(x);

	// posprev newnode pos
	posPrev->next = newnode;
	newnode->prev = posPrev;
	newnode->next = pos;
	pos->prev = newnode;
}

// 删除pos位置
void LTErase(LTNode* pos)
{
	assert(pos);

	LTNode* posNext = pos->next;
	LTNode* posPrev = pos->prev;

	posPrev->next = posNext;
	posNext->prev = posPrev;
	free(pos);
	//pos = NULL;
}

void LTDestroy(LTNode* phead)
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}

	free(phead);
	//phead = NULL;
}

❣️3.Test.c

#include "List.h"

void TestList1()
{
	LTNode* plist = LTInit();
	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);
	LTPushBack(plist, 5);
	LTPushBack(plist, 4);

	LTPrint(plist);

	LTPushFront(plist, 10);
	LTPrint(plist);
}

void TestList2()
{
	LTNode* plist = LTInit();
	LTPushFront(plist, 10);
	LTPushFront(plist, 20);
	LTPushFront(plist, 30);
	LTPushFront(plist, 40);

	LTPrint(plist);

	LTPopFront(plist);
	LTPrint(plist);

	LTPopFront(plist);
	LTPrint(plist);

	LTPopFront(plist);
	LTPrint(plist);

	LTPopFront(plist);
	LTPrint(plist);

	//LTPopFront(plist);
	//LTPrint(plist);
}

void TestList3()
{
	LTNode* plist = LTInit();
	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);
	LTPushBack(plist, 5);
	LTPushBack(plist, 4);

	LTPrint(plist);

	LTNode* pos = LTFind(plist, 3);
	if (pos)
	{
		pos->val *= 10;
	}
	LTPrint(plist);

	LTInsert(pos, 30000);
	LTPrint(plist);

	LTInsert(plist, -1);
	LTPrint(plist);

	LTInsert(plist, -2);
	LTPrint(plist);
}

void TestList4()
{
	LTNode* plist = LTInit();
	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);
	LTPushBack(plist, 5);
	LTPushBack(plist, 4);

	LTPrint(plist);

	LTNode* pos = LTFind(plist, 3);
	if (pos)
	{
		LTErase(pos);
		pos = NULL;
	}
	LTPrint(plist);

	LTDestroy(plist);
	plist = NULL;
}

int main()
{
	TestList4();

	return 0;
}

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

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

相关文章

2年博士后|心外医生赴美国耶鲁大学开展研究

G医生决定放弃申报CSC&#xff0c;改为自费出国&#xff0c;并在美国密歇根大学安娜堡分校和耶鲁大学两所名校中选择了更为出名的后者。因为不是CSC出资&#xff0c;G医生得以通过签证&#xff0c;顺利出国&#xff0c;实现了在世界知名高校从事2年博士后的个人职业规划目标。 …

11月15日星期三今日早报简报微语报早读

1、2023胡润女企业家榜出炉&#xff1a;郭得胜夫人邝肖卿首次成为中国女首富&#xff0c;龙湖吴亚军蝉联中国白手起家女首富&#xff1b; 2、叶剑英元帅夫人吴博逝世&#xff0c;享年106岁&#xff1b; 3、外交部&#xff1a;所谓“联合国军”是冷战产物&#xff0c;于法无据…

低代码JNPF,发挥软件定制的威力

目录 1.介绍 2.可视化逻辑编排工具 提供自动化的解决方案 3.功能特色展示 4.结语 近几年&#xff0c;随着低代码与无代码相关话题的火热&#xff0c;逻辑编排作为其重要构成部分也备受关注&#xff0c;集团内外不乏优秀的实践。之前在做技术调研时发现了不少业内逻辑编排相关的…

智能导诊的开发技术有哪些?

智能导诊源码 智能导诊是医疗领域中一项重要的应用&#xff0c;它可以帮助医生和患者更快速、更准确地诊断疾病&#xff0c;提高医疗效率和精度。以下是智能导诊开发技术的几个方面: 1.数据收集整合 智能导诊系统需要收集大量的医疗数据&#xff0c;包括患者症状、病史、检查结…

jenkins+centos7上传发布net6+gitlab

工作中实践了一下jenkins的操作&#xff0c;所以记录一下这次经验 首先安装好jenkins并注册自己的jenkins账号 因为我们的项目代码管理使用的是gitlab&#xff0c;在开始之前先在jenkins上安装gitlab的插件&#xff0c;安装之后应该是要重启jenkins的服务&#xff0c;后续jen…

python+django+mysql个人博客项目部署(VMware部署)

目录 一、Vmware新建win7虚拟机 二、组件/软件安装 2.1 安装python3 2.2 更新pip 2.3 安装pycharm 2.4 安装django 2.5 win安装mysql 三、配置数据库 3.1 安装sqlite客户端 3.2 db.sqlite3导出为myblog.sql 3.3 Heidisql连接本地sql 四、部署项目 4.1 安装模块 4.2 尝试运行 …

python之 flask 框架(2)项目拆分的 执行逻辑

项目的结构图 app.py # 导入__init__.py 比较特殊 from APP import create_appapp create_app() if __name__ __main__:app.run(debugTrue)init.py # __inti__.py # 初始化文件&#xff0c;创建Flask应用 from flask import Flask from .views import bluedef create_ap…

OpenAI 上线新功能力捧 RAG,开发者真的不需要向量数据库了?

近期&#xff0c; OpenAI 的开发者大会迅速成为各大媒体及开发者的热议焦点&#xff0c;有人甚至发出疑问“向量数据库是不是失宠了&#xff1f;” 这并非空穴来风。的确&#xff0c;OpenAI 在现场频频放出大招&#xff0c;宣布推出 GPT-4 Turbo 模型、全新 Assistants API 和一…

初试 jmeter做压力测试

一.前言 压力测试是每一个Web应用程序上线之前都需要做的一个测试&#xff0c;他可以帮助我们发现系统中的瓶颈问题&#xff0c;减少发布到生产环境后出问题的几率&#xff1b;预估系统的承载能力&#xff0c;使我们能根据其做出一些应对措施。所以压力测试是一个非常重要的步…

【遗传算法】Genetic algorithms (GAs) 遗传算法原理入门与应用代码

目录 1 遗传算法 2 遗传算法的基本步骤 3 Python示例 4 遗传算法解决TSP&#xff08;旅行商问题&#xff09; 1 遗传算法 遗传算法是一种优化搜索算法&#xff0c;模拟自然选择和遗传机制来寻找问题的最优解。这种算法的设计灵感来自于达尔文的进化论和遗…

Android studio配置Flutter开发环境报错问题解决

博主前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住也分享一下给大家 &#x1f449;点击跳转到教程 报错问题截图 报错原因已经给出&#xff1a; You need Java 11 or higher to build your app with this version of G…

一步路难倒英雄汉?app自动化测试,怎么从零搭建appium!

不少软件测试想进阶到自动化测试&#xff0c;没有前人知道&#xff0c;只能像个无头的苍蝇&#xff0c;到处乱转&#xff0c;根本不知道从何处下手 特别是自学路上碰到需要安装什么程序、工具的时候&#xff0c;一个报错就需要在百度上查个半天&#xff0c;这么浪费时间的事情…

FPGA时序约束与分析-简单入门

FPGA时序约束与分析-简单入门 文章目录 FPGA时序约束与分析-简单入门1. 本课程概述2. 时序约束简介2.1 什么是时序约束2.2 合理的时序约束2.3 *基于Vivado的时序约束方法 3. 时序分析的基本概念3.1 时钟与时钟偏差3.2 建立时间和保持时间3.3 时序分析中路径、沿和关系的定义 4.…

立体库堆垛机控制程序故障输出功能块

故障输出块 A "提升变频器故障" // O "提升变频器通讯故障" // ON "提升变频器准备好" "提升变频故障" A "水平变频器故障" // O "水平变频器通讯故障" // ON…

Spring源码—初识IOC

&#x1f47d;System.out.println(“&#x1f44b;&#x1f3fc;嗨&#xff0c;大家好&#xff0c;我是代码不会敲的小符&#xff0c;双非大四&#xff0c;Java实习中…”); &#x1f4da;System.out.println(“&#x1f388;如果文章中有错误的地方&#xff0c;恳请大家指正&a…

水库大坝安全监测预警系统的重要作用

水库大坝建造在地质构造复杂、岩土特性不均匀的地基上&#xff0c;在各种荷载的作用和自然因素的影响下&#xff0c;其工作性态和安全状况随时都在变化。如果出现异常&#xff0c;又不被及时发现&#xff0c;其后果不堪设想。全天候实时监测&#xff0c;实时掌握水库水位、雨情…

基于JuiceFS 的低成本 Elasticsearch 云上备份存储

杭州火石创造是国内专注于产业大数据的数据智能服务商&#xff0c;为了解决数据存储及高效服务客户需求&#xff0c;选择了 Elasticsearch 搜索引擎进行云上存储。基于性能和成本的考虑&#xff0c;在阿里云选择用本地 SSD ECS 机型自建集群。但由于是自建集群&#xff0c;如何…

uniapp Android如何打开常用系统设置页面?

uniapp Android 如何打开常用系统设置页面&#xff1f; 在使用App过程时&#xff0c;有时候会对一些权限获取&#xff0c;比如打开蓝牙、打开通知栏通知等设置&#xff0c;我们如何快速跳转到需要的设置页面&#xff1f; 文章目录 uniapp Android 如何打开常用系统设置页面&…

μC/OS-II---消息队列管理2(os_q.c)

目录 消息队列的主要优点消息队列和消息邮箱消息队列相关操作向消息队列发送消息(FIFO)向消息队列发送消息(LIFO)向消息队列发送消息(扩展&#xff09;消息队列获取/无等待清空消息队列消息队列信息获取消息队列中断等待 消息队列的主要优点 消息队列的主要优点是解耦和异步通…

rocketmq5.X 单机搭建 虚拟机搭建rocketmq5.1.4 搭建最新版本mq rocketmq5.1.4版本单体搭建 rocketmq(一)

1. 官网下载地址&#xff1a; 下载 | RocketMQ 2. 配置环境&#xff1a; 我是在/etc/profile.d 新建了一个rocketmq_env.sh 配置了jdk, maven, 以及mq. mq文件下载的 配置完之后&#xff0c;刷新环境source /etc/profile 3. 配置rocket mq 的jvm配置&#xff0c;就是两个启…