数据结构之链表(2),双向链表
目录
前言
一、链表的分类详细
二、双向链表
三、双向链表的实现
四、List.c文件的完整代码
五、使用演示
总结
前言
接着上一篇单链表来详细说说链表中什么是带头和不带头,“哨兵位”是什么,什么是单向什么是双向,什么是循环和不循环。然后实现完整的双向链表代码。
❤️感谢支持,点赞关注不迷路❤️
一、链表的分类详细
上一篇已经说过,链表总共分为8中结构,常用的就是单链表和双链表
这里的单链表全称:不带头单向不循环链表。双链表全称:带头双向循环链表
以下是上一篇中提到的8中结构:
注意:因为常用的只有单链表和双链表,将这两种链表结构掌握之后就能够自行实现其他结构的链表。
1.带头和不带:
带头与不带头的区别就是:带头的链表有一个头结点(head),也叫哨兵位。
哨兵位:带头链表的头结点,不存储任何有效元素,仅用于占位,代表链表的头部,功能类似于“放哨”,因此叫哨兵位。
注意:上一篇中的单链表是不带头的,因此不存在头结点的说法,所以上一篇中说的头结点仅仅是为了方便称呼第一个节点的一种不规范叫法,只有带头链表才有头结点的说法。尤其是在链表的分类中。
2.单向和双向
区别:
- 结构上:单向链表只有一个指向下一个节点的指针。双向链表有一个指向前一个节点的指针和一个指向后一个节点的指针。
- 功能上:单向链表只能单向访问下一个节点。双向链表可以既可以访问下一个节点也可以访问上一个节点。
3.循环和不循环
区别:不循环链表的尾结点next指针会指向空指针。循环链表的尾结点next指针不为空,可以无限循环的访问下一个节点。
注意:循环链表的尾结点的next指针可以指向链表的第一个节点,或者中间的任一节点,甚至尾结点。这都叫循环链表。
二、双向链表
双向链表全称:带头双向循环链表。
结构图如下:
双向链表的结构声明如下:
typedef int LTDataType;
//定义双链表结构
typedef struct ListNode
{
LTDataType data;//存储数据
struct ListNode* next;//指向下一个节点
struct ListNode* prev;//指向上一个节点
}LTNode;
同样,双向链表的实现也分为三个文件:
- List.h:双向链表的头文件,包含各种库函数头文件、链表结构、函数的声明。
- List.c:用于实现各种接口(函数)。
- test.c:用于测试。
三、双向链表的实现
1.List.h文件
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>typedef int LTDataType;
//定义双链表结构
typedef struct ListNode
{LTDataType data;//存储数据struct ListNode* next;//指向下一个节点struct ListNode* prev;//指向上一个节点
}LTNode;//初始化
//第一种:传二级指针
//void LTInit(LTNode** pphead);
//第二种:不传参
LTNode* LTInit();//打印
void LTPrint(LTNode* phead);//尾插
void LTPushBack(LTNode* phead, LTDataType x);//头插
void LTPushFront(LTNode* phead, LTDataType x);//判空
bool LTEmpty(LTNode* phead);//尾删
void LTPopBack(LTNode* phead);//头删
void LTPopFront(LTNode* phead);//查找
LTNode* LTFind(LTNode* phead, LTDataType x);//在指定位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x);//删除指定位置的节点
void LTErase(LTNode* pos);//销毁
//第一种:传二级指针
//void LTDesTroy(LTNode** pphead);
//第二种:传一级指针,缺点是需手动将哨兵位置空
void LTDesTroy(LTNode* phead);
2.List.c文件
1.LTBuyNode函数
//申请新节点
LTNode* LTBuyNode(LTDataType x)
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;//前后指针全部指向自己,自循环newnode->next = newnode->prev = newnode;return newnode;
}
解析:
- 功能:用于申请新节点。
- 参数:新节点的需数据x
- 使用malloc函数申请一个节点空间,判空后将其数据域赋值x。注意,新节点的两个指针都需要指向自己,这样才是一个双向循环链表。
- 因为该函数只为其他函数服务,因此可以只在 List.c 文件中定义,无需在 List.h 中声明。
2.LTInit函数
//初始化
//第一种:传二级指针
//void LTInit(LTNode** pphead)
//{
// assert(pphead);
//
// //创建一个头结点(哨兵位)
// *pphead = LTBuyNode(-1);
//}//第二种:不传参
LTNode* LTInit()
{return LTBuyNode(-1);
}
解析:
- 该函数有两个版本,第二种为优化后的版本,为的是保持接口的一致性,因为双向链表的其它功能函数都只是传一级指针。为了避免混淆,优化为不传参版本。
- 功能:初始化为哨兵位(头结点)。
- 第一种:需要传参版本,传二级指针,因为只有二级指针才能修改一级指针的指向。因此这里使用二级指针进行初始化赋值。-1可以改为任何值,因为哨兵位存储的数据为无效数据。
- 第二种:不传参版本,直接创建并返回哨兵位的地址。
- 使用上的区别:第一种需要自己定义一个双向链表类型的空指针,然后使用调用该函数进行传参初始化,注意传参时需&。第二种自己创建一个双向链表类型的空指针并调用该函数进行赋值。
3.LTPrint函数
//打印
void LTPrint(LTNode* phead)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){printf("%d->", pcur->data);pcur = pcur->next;}printf("\n");
}
解析:
- 功能:打印链表的所有节点数据。
- 参数:链表的头结点(哨兵位)。
- 首先断言头结点不能为空,创建一个 pcur 指针指向第一个有效节点,
- 然后进行循环遍历时,因为该链表是循环链表,因此循环一遍的判定是 pcur 指向的节点不是头结点。循环体里就是打印数据然后让pcur走向下一个节点。打印完后换行即可。
4.LTPushBack函数
//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);//首先修改新节点的指针指向newnode->next = phead;//next指针指向哨兵位newnode->prev = phead->prev;//prev指针指向尾节点//再修改旧尾节点和哨兵位phead->prev->next = newnode;//旧尾节点的next指针指向新尾节点phead->prev = newnode;//哨兵位后指针指向新尾节点
}
解析:
- 功能:在链表的尾部插入一个节点
- 参数:头结点(哨兵位)和需要插入的节点数据
- 首先断言头结点不为空,申请新节点,然后就需要改变新节点和受影响节点的指针指向。这里需要注意修改的前后顺序,
- 先修改新节点的指针指向,新节点的 next 指针应该指向头结点,因为是循环链表,原尾结点可以通过头结点找到,即 phead->prev 指向的就是原来的尾结点,将新节点的 prev 指针指向原尾结点。
- 修改完新节点就需要修改头结点和原尾结点的指针指向,原尾结点依旧可以通过头结点的 prev 指针找到,将其 next 指针指向新节点。最后就是头结点的 prev 指针需要指向新节点。
5.LTPushFront函数
//头插
void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);//首先还是先修改新节点的指向newnode->next = phead->next;newnode->prev = phead;//然后修改旧头结点和哨兵位phead->next->prev = newnode;phead->next = newnode;
}
解析:
- 功能:在第一个有效节点之前插入数据,也就是头结点(哨兵位)之后
- 参数:头结点(哨兵位)和需插入的节点数据、
- 首先头插为什么不是在头结点前面插入数据,很简单,头结点的 prev 指针就是指向尾结点的,所以如果在头结点之前插入数据,其实就是尾插。
- 实现过程,开头还是断言和申请新节点,然后先修改新节点的前后指针指向,原第一个有效节点可以通过头结点的 next 指针找到。修改完新节点后就修改原第一节点的prve指针,使其指向新节点,最后修改头结点的 next 指针也指向新节点。
6.LTEmpty函数
//判空
//只有哨兵位节点说明链表为空
bool LTEmpty(LTNode* phead)
{assert(phead);return phead->next == phead;
}
解析:
- 功能:判断链表是不是空链表,也就是判断链表是不是只有头结点(哨兵位)
- 参数:头结点(哨兵位)
- 该函数主要为后续删除链表节点时判断链表是否为空链表,实现简单,断言头结点后,直接判断头结点的下一个节点是不是还是头结点即可。
7.LTPopBack函数
//尾删
void LTPopBack(LTNode* phead)
{assert(phead);assert(!LTEmpty(phead));//先保存尾结点和倒数第二个节点LTNode* del = phead->prev;LTNode* prev = del->prev;//再修改倒数第二节点和哨兵位节点指向prev->next = phead;phead->prev = prev;//释放尾结点free(del);del = NULL;
}
解析:
- 功能:删除尾结点
- 参数:头结点(哨兵位)
- 首先断言判断头结点是否为空指针以及链表是否为空链表,然后创建 del 指针保存尾结点,创建 prev 指针保存倒数第二个节点,避免修改完受影响节点的前后指针指向后找不到尾结点,
- 然后修改倒数第二个节点的 next 指针指向头结点,以及头结点的 prev 指针指向倒数第二个节点。最后释放尾结点(del),最后释放尾结点 del 即可。
8.LTPopFront函数
//头删
void LTPopFront(LTNode* phead)
{assert(phead);assert(!LTEmpty(phead));//还是先保存第一个有效节点LTNode* del = phead->next;del->next->prev = phead;//修改第二个有效节点指向phead->next = del->next;//修该哨兵位指向//释放free(del);del = NULL;
}
解析:
- 功能:删除第一个有效节点,也就是头结点(哨兵位)的下一个节点
- 参数:头结点(哨兵位)
- 还是先断言判断头结点和链表是否为空,创建 del 指针保存第一个有效节点,然后先通过 del指针找到并修改第二个有效节点的 prev 指针,使其指向头结点,再修改头结点的 next 指针指向第二个节点。最后就可以直接释放第一个节点 del 即可。
9.LTFind函数
//查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);LTNode* pcur = phead->next;//循环遍历while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}
解析:
- 功能:根据指定数据查找对应的节点,然后返回该节点的地址,主要是配合后面两个函数使用
- 有了上面函数实现的经验,查找函数实现简单,循环遍历,循环链表循环一遍的标志是下一节点为头结点,找到就返回节点的地址,找不到返回空指针。
10.LTInsert函数
//在指定位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newnode = LTBuyNode(x);//先修改新节点朝向newnode->next = pos->next;newnode->prev = pos;//在修改受影响的两个节点朝向,注意顺序pos->next->prev = newnode;pos->next = newnode;
}
解析:
- 功能:在指定位置之后插入数据,这个指定位置需要通过 LTFind 函数指定。
- 双向链表插入数据其实都差不多,先修改新节点的前后指针,再修改受影响节点的指针。这里不再赘述。
11.LTErase函数
//删除指定位置的节点
void LTErase(LTNode* pos)
{assert(pos);//先修改pos的前后节点pos->next->prev = pos->prev;pos->prev->next = pos->next;free(pos);pos = NULL;
}
解析:
- 功能:删除指定位置的节点
- 这个实现更加简单,通过 pos 指针先修改前后节点的指针指向,然后就可以直接释放掉自己。
11.LTDesTroy函数
//销毁
//第一种:传二级指针
//void LTDesTroy(LTNode** pphead)
//{
// assert(pphead && *pphead);
//
// LTNode* pcur = (*pphead)->next;
// while (pcur != *pphead)
// {
// LTNode* Next = pcur->next;
// free(pcur);
// pcur = Next;
// }
//
// free(*pphead);
// *pphead = NULL;
// pcur = NULL;
//}//第二种:传一级指针,缺点是需手动将哨兵位置空
void LTDesTroy(LTNode* phead)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){LTNode* Next = pcur->next;free(pcur);pcur = Next;}free(phead);phead = pcur = NULL;
}
解析:
- 销毁函数有两种,第二种为优化后的版本,同样也是为了接口的一致性,避免使用时混淆,因此传一级指针合适,但是带来的缺点就是需要手动将哨兵位置空。
- 两个版本的函数实现方式大致相同,循环遍历,在释放当前节点之前要先使用 Next 指针保存下一节点的地址。
- 第一个版本缺点是需要传二级指针,但是不需要手动将哨兵位置空,函数内部就可以实现置空,第二版本优点就是同其他接口一样,只需要传一级指针,缺点就是在调用完该函数后需要手动将哨兵位置空。
四、List.c文件的完整代码
#include "List.h"//申请新节点
LTNode* LTBuyNode(LTDataType x)
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;//前后指针全部指向自己,自循环newnode->next = newnode->prev = newnode;return newnode;
}//初始化
//第一种:传二级指针
//void LTInit(LTNode** pphead)
//{
// assert(pphead);
//
// //创建一个头结点(哨兵位)
// *pphead = LTBuyNode(-1);
//}//第二种:不传参
LTNode* LTInit()
{return LTBuyNode(-1);
}//打印
void LTPrint(LTNode* phead)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){printf("%d->", pcur->data);pcur = pcur->next;}printf("\n");
}//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);//首先修改新节点的指针指向newnode->next = phead;//next指针指向哨兵位newnode->prev = phead->prev;//prev指针指向尾节点//再修改旧尾节点和哨兵位phead->prev->next = newnode;//旧尾节点的next指针指向新尾节点phead->prev = newnode;//哨兵位后指针指向新尾节点
}//头插
void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);//首先还是先修改新节点的指向newnode->next = phead->next;newnode->prev = phead;//然后修改旧头结点和哨兵位phead->next->prev = newnode;phead->next = newnode;
}//判空
//只有哨兵位节点说明链表为空
bool LTEmpty(LTNode* phead)
{assert(phead);return phead->next == phead;
}//尾删
void LTPopBack(LTNode* phead)
{assert(phead);assert(!LTEmpty(phead));//先保存尾结点和倒数第二个节点LTNode* del = phead->prev;LTNode* prev = del->prev;//再修改倒数第二节点和哨兵位节点指向prev->next = phead;phead->prev = prev;//释放尾结点free(del);del = NULL;
}//头删
void LTPopFront(LTNode* phead)
{assert(phead);assert(!LTEmpty(phead));//还是先保存第一个有效节点LTNode* del = phead->next;del->next->prev = phead;//修改第二个有效节点指向phead->next = del->next;//修该哨兵位指向//释放free(del);del = NULL;
}//查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);LTNode* pcur = phead->next;//循环遍历while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}//在指定位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newnode = LTBuyNode(x);//先修改新节点朝向newnode->next = pos->next;newnode->prev = pos;//在修改受影响的两个节点朝向,注意顺序pos->next->prev = newnode;pos->next = newnode;
}//删除指定位置的节点
void LTErase(LTNode* pos)
{assert(pos);//先修改pos的前后节点pos->next->prev = pos->prev;pos->prev->next = pos->next;free(pos);pos = NULL;
}//销毁
//第一种:传二级指针
//void LTDesTroy(LTNode** pphead)
//{
// assert(pphead && *pphead);
//
// LTNode* pcur = (*pphead)->next;
// while (pcur != *pphead)
// {
// LTNode* Next = pcur->next;
// free(pcur);
// pcur = Next;
// }
//
// free(*pphead);
// *pphead = NULL;
// pcur = NULL;
//}//第二种:传一级指针,缺点是需手动将哨兵位置空
void LTDesTroy(LTNode* phead)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){LTNode* Next = pcur->next;free(pcur);pcur = Next;}free(phead);phead = pcur = NULL;
}
五、使用演示
使用测试文件 test.c 进行演示:
#include "List.h"void ListTest1()
{//创建哨兵位(头结点)LTNode* plist = LTInit();//尾插4个节点LTPushBack(plist, 1);LTPushBack(plist, 2);LTPushBack(plist, 3);LTPushBack(plist, 4);printf("尾插:");LTPrint(plist);//头插4个节点LTPushFront(plist, 11);LTPushFront(plist, 12);LTPushFront(plist, 13);LTPushFront(plist, 14);printf("头插:");LTPrint(plist);//在1后面插入一个数据LTInsert(LTFind(plist, 1), 66);printf("在1后面插入66:");LTPrint(plist);//尾删LTPopBack(plist);printf("尾删:");LTPrint(plist);//头删LTPopFront(plist);printf("头删:");LTPrint(plist);//删除11LTErase(LTFind(plist, 11));printf("删除11:");LTPrint(plist);//销毁LTDesTroy(plist);plist = NULL;//需要手动置空printf("链表已销毁\n");}int main()
{ListTest1();return 0;
}
运行结果:
总结
以上就是本文的全部内容,感谢支持。
相关文章:
数据结构之链表(2),双向链表
目录 前言 一、链表的分类详细 二、双向链表 三、双向链表的实现 四、List.c文件的完整代码 五、使用演示 总结 前言 接着上一篇单链表来详细说说链表中什么是带头和不带头,“哨兵位”是什么,什么是单向什么是双向,什么是循环和不循环。然后实…...
STL之list篇(下)(从底层分析实现list容器,逐步剥开list的外表)
文章目录 前言一、list的数据结构和类实现需求1.1 数据结构1.2 list类实现需求 二、list迭代器的实现2.1 为什么list不能直接把迭代器当作指针一样使用?2.2 list迭代器的框架设计2.3 *和-> 操作符的重载2.4 和-- 操作符的重载2.5 !和 操作符的重载 三、 list的函…...
视频去水印的3个技巧,教你无痕去水印
许多视频平台为了推广自身品牌或者广告用途,会在视频上添加水印。这些水印不仅影响了视频的美观,还可能限制了内容的传播范围。幸运的是,有几种简单而有效的方法可以帮助我们去除视频中的水印,同时保持视频的原始画质和观感。以下…...
LSTM模型改进实现多步预测未来30天销售额
关于深度实战社区 我们是一个深度学习领域的独立工作室。团队成员有:中科大硕士、纽约大学硕士、浙江大学硕士、华东理工博士等,曾在腾讯、百度、德勤等担任算法工程师/产品经理。全网20多万粉丝,拥有2篇国家级人工智能发明专利。 社区特色…...
八LAMP搭建
# LAMP ## 一、知识回顾 ### FTP samba nfs 特点 - 借用Linux用户作为映射用户,进行权限管理 - 软件本身还有管理控制权限 #### 客户端连接到服务器后进行读写执行等操作 ,必须同时具有: - 软件许可的权限 vsftpd: anon upload enableYES - 文件…...
Windows——解除Windows系统中文件名和目录路径的最大长度限制
第一步:打开本地组策略编辑器 按下Win R键打开运行窗口,输入 gpedit.msc 并回车,打开本地组策略编辑器。 第二步:开启 长路径设置 第三步:重启计算机...
黑名单与ip禁令是同一个东西吗
黑名单和IP禁令相关,但它们并不是完全相同的概念。以下是它们之间的区别: 黑名单 定义:黑名单通常是一个包含不允许或被禁止的用户、IP地址、域名或其他实体的列表。用途:用于阻止特定用户或实体访问某个系统或服务。黑名单可以…...
FuTalk设计周刊-Vol.075
国庆75周年,也是第75期周刊~ 祝大家国庆快乐~! #AI漫谈 热点捕手 1.万字深剖!13位AI巨擘联袂,1.6万字解码生成式AI产品「全攻略」 “生成式人工智能产品”主题论坛,邀请到了来自腾讯、商汤科…...
PE节表中是否存在misc.VirtualSize 比SizeofRawData还要大的情况
确实是存在的,这是win10自带记事本,可以看到 确实是大.所以在申请imagebuffer的时候,还是需要比较大小.但是在还原的时候.只考虑sizeofRawData即可>...
栈及笔试题
目录 栈的实现 1、数组栈 2、链式栈 栈的创建 栈的打印 内存泄漏 栈溢出 练习 有效的括号 栈的实现 栈后入先出 1、数组栈 (最佳实现,且访问数据的时候CPU告诉访存命中率比较高,因为地址连续存放,访问时CPU从cache里一…...
【工程测试技术】第3章 测试装置的基本特性,静态特性和动态特性,一阶二阶系统的特性,负载效应,抗干扰性
目录 3.1 概述 1测量装置的静态特性 2.标准和标准传递 3.测量装置的动态特性 4.测量装置的负载特性 5.测量装置的抗干扰性 1.线性度 2.灵敏度 3.回程误差 4.分辨力 5.零点漂移和灵敏度漂移 3.3.1 动态特性的数学描述 1.传递函数 2.频率响应函数 3.脉冲响应函数 …...
ireport 5.1 中文生辟字显示不出来,生成PDF报字体找不到
ireport生成pdf里文字不显示。本文以宋体中文字不显示为例。 问题:由浅入深一步一步分析 问题1、预览正常,但生成pdf中文不显示 报告模板编辑后,预览正常,但生成pdf中文不显示。以下是试验过程: 先编辑好一个报告单模…...
给Ubuntu虚拟机设置静态IP地址(固定IP)
查看 为Ubuntu虚拟机配置静态IP地址(固定IP)的方法经过亲自测试,已被证实有效。 这里你记得网关就可以了,等下要用 查看配置前的网络信息 ifconfig 查看网关 route -n 配置 配置网络文件 cd /etc/netplan/ ls 查看自己的文件的名…...
spring boot文件上传之x-file-storage
spring boot文件上传之x-file-storage 今天看到一个文件上传的开源组件x-file-storage,官方地址如下: https://x-file-storage.xuyanwu.cn/#/ 该组件官网是这样介绍的,如下: 一行代码将文件存储到本地、FTP、SFTP、WebDAV、阿…...
Object.values() 、 Object.keys()
拿到当前对象里面的value值 // 假设你有一个对象 const myObject {name: Kimi,age: 30,country: Moon };// 获取对象的所有值 const values Object.values(myObject);// 输出值数组 console.log(values); // ["Kimi", 30, "Moon"] 如果你需要在 Vue 组…...
脸爱云管理系统存在任意文件上传漏洞
漏洞描述 脸爱云一脸通智慧管理平台是一套功能强大、运行稳定、操作简单方便、用户界面美观的一脸通系统。该平台整合了人脸识别技术和智能化解决方案,可以实现识别和管理个体身份,为各种场景提供便捷的身份验证和管理功能。其存在任意文件上传漏洞&…...
elasticsearch_exporter启动报错 failed to fetch and decode node stats
最近把服务器迁移到了ubuntu系统,结果发现在centos还正常运行的elasticsearch_exporter,用systemd启动后一直报错 failed to fetch and decode node stats 在网上翻了大半年,竟然都无解!这种报错,很明显就是你的ES密码…...
Git 使用方法
简介 Git常用命令 Git 全局设置 获取Git 仓库 方法二用的比较多 将仓库链接复制 在 git base here ----> git clone 仓库链接 工作区、暂存区、版本库 Git 工作区中文件中的状态 本地仓库的操作 远程仓库操作 git pull 将代码推送到远程仓库 1. git add 文件名 ---放…...
生产环境升级mysql流程及配置主从服务
之前写到过mysql升级8.4的文章, 因此不再介绍mysql的安装过程 避免服务器安装多个mysql引起冲突的安装方法_安装两个mysql会冲突吗-CSDN博客 生产环境升级mysql8.4.x流程 安装mysql 参考之前文章: 避免服务器安装多个mysql引起冲突的安装方法_安装两个mysql会冲突吗-CSDN博客…...
论软件体系结构的演化
摘要 2022年3月,我加入了公司的新智慧公交平台项目研发团队,并担任系统架构师角色,负责系统整体架构的设计与评审。该项目采用了物联网三层架构模型,其中设备接入层和网络交互层基于公司的中台战略,我们有效复…...
【go入门】常量
目录 定义枚举iota思考题 定义 go语言常量的定义和其他语言类似,常量中的数据类型只能是布尔型,数字型(整型、浮点型、复数)和字符串型 常量的定义方式和变量一样,只不过变量定义使用 var 关键字,而常量定…...
2.1 HuggingFists系统架构(二)
部署架构 上图为HuggingFists的部署架构。从架构图可知,HuggingFists主要分为服务器(Server)、计算节点(Node)以及数据库(Storage)三部分。这三部分可以分别部署在不同的机器上,以满足系统的性能需求。为部署方便,HuggingFists社区版将这三部…...
工具类:JWT
工具类:JWT 依赖JwtUtil.java 依赖 <!-- 创建、解析 和 验证JSON Web Tokens (JWT)--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependenc…...
王道-计网
2 采用滑动窗口机制对两个相邻结点A(发送方)和B(接收方)的通信过程进行流量控制。假定帧的序号长度为3比特,发送窗口与接收窗口的大小均为7,当A发送了编号为0、1、2、3这4个帧后,而B接收了这4个帧,但仅应答了0、1两个帧,A继续发送4、5两个帧,且这两个帧已进入B的接收…...
【机器学习(十)】时间序列案例之月销量预测分析—Holt-Winters算法—Sentosa_DSML社区版
文章目录 一、Holt-Winters算法原理(一) 加法模型(二) 乘法模型(三) 阻尼趋势 二、Holt Winters算法优缺点优点缺点 三、Python代码和Sentosa_DSML社区版算法实现对比(一) 数据读入和统计分析(二) 数据预处理(三) 模型训练和模型评估(四) 模型可视化 四、总结 一、Holt-Winters…...
Webpack优化问题
目录 打包流程swcthread-loaderhash升级插件 打包流程 webpack 的打包流程大致可以分为以下几个步骤: 初始化:webpack 通过配置文件和 Shell 参数,初始化参数,确定入口文件、输出路径、加 载器、插件等信息。接下来读取配置文件…...
yjs10——pandas的基础操作
1.pandas读入文件——pd.read_cvs() data pd.read_csv("E:/机器学习/data/salary.csv") 注意:1.是pd.read_cvs,不要顺手写成np.read_cvs 2.路径的斜杠方向是/,不是\,如果直接从电脑粘贴路径,路径写法是\&am…...
Squaretest单元测试辅助工具使用
1、idea安装插件 Squaretest 然后关掉idea 2、安装字节码软件(jclasslib) 3、找到idea里面的Squaretest安装目录 找到包含TestStarter的jar包 4、打开 com.squaretest.c.f 打开后选择常量池 5、找到第16个修改 Long value值,修改的数字即为使…...
MFU简介
1、缩写 MFU - Mask Field Utilization(光刻掩膜版有效利用比例) GDPW - Gross Die Per Wafer,每张wafer上die的数量 2、什么是MASK 在光刻机中,光源(紫外光、极紫外光)透过mask曝光在晶圆上形成图…...
十分钟实现内网连接,配置frp
十分钟实现内网连接,配置frp 一.frp是什么?其实是一款实现外网连接内网的一个工具,个人理解,说白了就像是teamviwer一样,外网能访问内网。 利用处于内网或防火墙后的机器,对外网环境提供 http 或 https 服…...
wordpress编辑用户头像/网络广告人社区官网
上一篇文章介绍了RSA涉及的数学知识,本章将应用这些知识详解RSA的加密与解密。 RSA算法的密钥生成过程 密钥的生成是RSA算法的核心,它的密钥对生成过程如下: 1. 选择两个不相等的大素数p和q,计算出npq,n被称为RSA算法的…...
海外建站推广/app推广软文范文
上篇博客中,已经实现了一个Struts例子,现在就来分析下这个例子。详细的解读Struts1。 Struts1的几个核心组件是值得我们注意的: (1)ActionServlet(核心控制器)。 (2)ActionForm(接收页面中传…...
Fastcgi做网站/软文案例300字
今天上课老师用Java实现了打地鼠游戏的界面和具体逻辑,那么我也尝试使用Android语言实现其功能。首先是打地鼠游戏的玩法1.每隔1秒或者0.5秒地鼠会出现在九宫格中的任一位置2.点击界面,如果地鼠出现的位置与点击位置相同,则认为打中地鼠。否则…...
网站建站网站设计/网络营销的发展现状如何
0.前言随机访问迭代器: vector、string、dequeSTL的一个革命性的方面就是它的计算复杂性保证 条款01:慎重选择容器类型 c提供的容器:标准STL序列容器:vector、string、deque、list标准STL关联容器:set、multiset、map、multimap非标准STL容器:…...
wordpress is author/企业网站设计欣赏
MarketMonitor是2009年微软专业开发者大会上StreamInsight小组资深程序经理Torsten Grabs演讲中的第一个StreamInsight Demo。这个Demo演示了如何从一个随机的股票报价源中通过WHERE子句过滤出想要的结果。 准备工作 下载安装StreamInsight目前的最新版本StreamInsight 1.1&…...
网站建设的规划方案/永久免费自助建站系统
菱形 程序…………………… #include<stdio.h> int main() {int i,j,k; printf("\n\n");for(i1;i<4;i) //从第一行到第四行 {for(j1;j<16-i;j) //第一行15个空格 printf(" ");for(k1;k<2*i-1;k)printf("*"); //也可将 * …...