数据结构:链表

目录

一.为什么要使用链表存储数据?

二.链表的分类

单向或者双向链表:

带头或者不带头:

循环或者非循环:

三.链表的实现

3.1无头单向非循环链表的实现:

3.1.1单向无头非循环链表的声明

3.1.2动态申请一个节点

 3.1.3单链表打印

  3.1.4单链表尾插

   3.1.5单链表的头插

   3.1.6单链表的尾删

   3.1.7单链表头删

   3.1.8单链表查找

  3.1.9单链表在pos位置之前插入x

 3.1.10单链表在pos位置之后插入x

  3.1.11单链表删除pos之后的值

 3.1.12单链表删除pos位置的值

3.1.13销毁单链表

 头文件:

 测试文件:

3.2带头双向循环链表的实现

3.2.1带头双向循环链表的声明

3.2.2动态申请一个节点

 3.2.3哨兵位初始化(创建链表的头结点)

 3.2.4带头双向循环链表打印

 3.2.5双向链表尾插

  3.2.6双向链表头插

3.2.7双线链表尾删

3.2.8双线链表头删

3.2.9双向链表查找

 3.2.10双向链表在pos的前面进行插入

3.2.11双向链表删除pos位置的结点

 3.2.12双向链表销毁

头文件:

测试文件:

四.链表总结


一.为什么要使用链表存储数据?

内存空间是所有程序的公共资源,在一个复杂的系统运行环境下,空闲的内存空间可能散落在内存各处。我们知道,存储数组的内存空间必须是连续的,而当数组非常大时,内存可能无法提供如此大的连续空间。此时链表的灵活性优势就体现出来了。

让我们来看看链表的结构:

  •  可以得出:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
  • 上述图中的链表知识链表中的其中一种

二.链表的分类

单向或者双向链表:

带头或者不带头:

循环或者非循环:


虽然链表的种类很多,但我们主要使用的还是无头单向非循环链表(OJ题中最常见的链表)和带头循环双向链表(实践应用)

三.链表的实现

3.1无头单向非循环链表的实现:

对于项目我们需要区分测试文件和接口文件,这样做有利于培养良好的代码能力。

3.1.1单向无头非循环链表的声明

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int SLNDataType;// 声明一个  Single Link List Node (单向无头链表)typedef struct SLLN
{//节点值SLNDataType val;//指向下一个节点的指针struct SLLN* next;}SLNode;

3.1.2动态申请一个节点

// 动态申请一个节点
SLNode* CreateNode(SLNDataType x)
{SLNode* newnode = (SLNode*)calloc(1, sizeof(SLNode));if (newnode == NULL){perror("calloc");//直接终止程序exit(-1);}newnode->val = x;newnode->next = NULL;return newnode;
}

 3.1.3单链表打印

// 单链表打印
void SLNodePrint(SLNode* plist)
{SLNode* cur = plist;if (cur != NULL){while (cur){printf("%d->", cur->val);cur = cur->next;}printf("NULL");printf("\n");}else{printf("链表为空无需打印\n");}
}

  3.1.4单链表尾插

// 单链表尾插
void SLNodePushBack(SLNode** pplist, SLNDataType x)
{//先创造一个新节点SLNode* newnode = CreateNode(x);SLNode* tail = *pplist;//将plist赋值给tail//找尾if (*pplist == NULL){*pplist = newnode;}else{while (tail->next != NULL){tail = tail->next;}tail->next = newnode;}
}

   3.1.5单链表的头插

// 单链表的头插
void SLNodePushFront(SLNode** pplist, SLNDataType x)
{//先创建一个新节点SLNode* newnode = CreateNode(x);newnode->next = *pplist;*pplist = newnode;
}

   3.1.6单链表的尾删

// 单链表的尾删
void SLNodePopBack(SLNode** pplist)
{//如果链表为空则不能删除,报错assert(*pplist);//找尾SLNode* tail = *pplist;//tail移动时我们还需要有一个前驱指针 prev 跟在tail后面SLNode* prev = NULL;//单链表只有一个节点情况下尾删if (tail->next == NULL){free(*pplist);*pplist = NULL;}//单链表有多个节点情况下尾删else{while (tail->next != NULL){prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;}}

   3.1.7单链表头删

// 单链表头删
void SLNodePopFront(SLNode** pplist)
{//当链表为空时不能删除,报错assert(*pplist);SLNode* cur = *pplist;SLNode* newplist = (*pplist)->next;free(cur);cur = NULL;*pplist = newplist;}

   3.1.8单链表查找

// 单链表查找(配合在pos位置插入或者删除使用)
SLNode* SLNodeFind(SLNode* plist, SLNDataType x)
{while (plist != NULL){if (plist->val == x){return plist;}else{plist = plist->next;}}return NULL;
}

  3.1.9单链表在pos位置之前插入x

//单链表在pos位置之前插入x
void SLNodeInsertBefore(SLNode** pplist, SLNode* pos, SLNDataType x)//此处传入二级指针pplist是为了在头插时改变plist的值,传址调用
{//此处需要对哪个指针进行断言检查呢?assert(pos && *pplist);//防止人为乱传空SLNode* cur = *pplist;SLNode* prev = NULL;//前驱指针prev保存cur前一个节点的地址if (pos == *pplist){//在头节点位置前插入x实质上就是头插,我们调用之前写的头插函数即可SLNodePushFront(pplist, x);}else{SLNode* newnode = CreateNode(x);while (cur != pos){prev = cur;cur = cur->next;}prev->next = newnode;newnode->next = cur;}
}

 3.1.10单链表在pos位置之后插入x

// 单链表在pos位置之后插入x
void SLNodeInsertAfter(SLNode* pos, SLNDataType x)
{//链表为空无法在pos位置之后插入xassert(pos);SLNode* newnode = CreateNode(x);if (newnode == NULL){perror(calloc);return;}SLNode* next = pos->next;pos->next = newnode;newnode->next = next;}

  3.1.11单链表删除pos之后的值

//单链表删除pos之后的值
void SLNodeEraseAfter(SLNode* pos)
{//链表为空无法删除assert(pos);//当链表只剩下一个节点或者pos后面无节点时也无法删除assert(pos->next);SLNode* del = pos->next;pos->next = pos->next->next;free(del);del == NULL;
}

 3.1.12单链表删除pos位置的值

//单链表删除pos位置的值
void SLNodeErasepos(SLNode** pplist, SLNode* pos)
{//链表为空不能删除assert(*pplist);SLNode* cur = *pplist;SLNode* prev = NULL;//前驱指针prev保存cur前一个节点的地址if (pos != *pplist){while (cur != pos){prev = cur;cur = cur->next;}SLNode* next = cur->next;free(cur);cur = NULL;//链接两个节点prev->next = next;}else{SLNode* next = cur->next;free(cur);cur = NULL;//更新头节点*pplist = next;}
}

3.1.13销毁单链表

//销毁链表
void SLNodeDestory(SLNode** pplist)
{assert(pplist);assert(*pplist);SLNode* cur = *pplist;while (cur){SLNode* next = cur->next;free(cur);cur = next;}*pplist = NULL;
}

以上代码为单链表所实现的所有功能(接口)


 头文件:

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int SLNDataType;// 声明一个  Single Link List Node (单向无头链表)typedef struct SLLN
{//节点值SLNDataType val;//指向下一个节点的指针struct SLLN* next;}SLNode;// 动态申请一个节点
SLNode* CreateNode(SLNDataType x);// 单链表打印
void SLNodePrint(SLNode* plist);// 单链表尾插
void SLNodePushBack(SLNode** pplist, SLNDataType x);// 单链表的头插
void SLNodePushFront(SLNode** pplist, SLNDataType x);// 单链表的尾删
void SLNodePopBack(SLNode** pplist);// 单链表头删
void SLNodePopFront(SLNode** pplist);// 单链表查找
SLNode* SLNodeFind(SLNode* plist, SLNDataType x);//单链表在pos位置之前插入x
void SLNodeInsertBefore(SLNode** pplist, SLNode* pos, SLNDataType x);//单链表在pos位置之后插入x
void SLNodeInsertAfter(SLNode* pos, SLNDataType x);//单链表删除pos位置之后的值
void SLNodeEraseAfter(SLNode* pos);//单链表删除pos位置的值
void SLNodeErasepos(SLNode** pplist, SLNode* pos);//销毁单链表
void SLNodeDestory(SLNode** pplist);

 测试文件:

#define  _CRT_SECURE_NO_WARNINGS 1
#include"Single_linked_lists.h"//单链表尾插测试
void text1()
{SLNode* plist = NULL;SLNodePushBack(&plist, 1);SLNodePushBack(&plist, 2);SLNodePushBack(&plist, 3);SLNodePushBack(&plist, 4);SLNodePrint(plist);
}
//单向链表头插测试
void text2()
{SLNode* plist = NULL;SLNodePushFront(&plist, 1);SLNodePushFront(&plist, 2);SLNodePushFront(&plist, 3);SLNodePushFront(&plist, 4);SLNodePrint(plist);}
//单向链表尾删测试
void text3()
{//SLNode* plist = NULL;//SLNodePushBack(&plist, 1);//SLNodePushBack(&plist, 2);//SLNodePushBack(&plist, 3);//SLNodePushBack(&plist, 4);多节点尾删测试//SLNodePopBack(&plist);SLNode* plist = NULL;SLNodePushBack(&plist, 1);//单节点尾删测试SLNodePopBack(&plist);SLNodePrint(plist);
}
//单向链表头删测试
void text4()
{SLNode* plist = NULL;SLNodePushBack(&plist, 1);SLNodePushBack(&plist, 2);SLNodePushBack(&plist, 3);SLNodePushBack(&plist, 4);//头删测试SLNodePopFront(&plist);SLNodePrint(plist);
}//单向链表查找 val 测试
void text5()
{SLNode* plist = NULL;SLNodePushBack(&plist, 1);SLNodePushBack(&plist, 2);SLNodePushBack(&plist, 3);SLNodePushBack(&plist, 4);//查找val测试SLNode* pos = SLNodeFind(plist, 4);printf("%p\n", pos);
}// 单链表在pos位置之后插入 x 测试
void text6()
{SLNode* plist = NULL;SLNodePushBack(&plist, 1);SLNodePushBack(&plist, 2);SLNodePushBack(&plist, 3);SLNodePushBack(&plist, 4);//pos之后插入xSLNode* pos = SLNodeFind(plist, 3);SLNodeInsertAfter(pos, 0);SLNodePrint(plist);
}//单链表删除pos之后的值测试
void text7()
{SLNode* plist = NULL;SLNodePushBack(&plist, 1);SLNodePushBack(&plist, 2);SLNodePushBack(&plist, 3);SLNodePushBack(&plist, 4);//单链表删除pos之后的值测试SLNode* pos = SLNodeFind(plist, 3);SLNodeEraseAfter(pos);SLNodePrint(plist);}
//单链表在pos前一个位置插入 x 测试
void text8()
{SLNode* plist = NULL;SLNodePushBack(&plist, 1);SLNodePushBack(&plist, 2);SLNodePushBack(&plist, 3);SLNodePushBack(&plist, 4);SLNodePrint(plist);SLNode* posbefore = SLNodeFind(plist, 3);//单链表在pos前一个位置插入 x 测试SLNodeInsertBefore(&plist, posbefore, 0);SLNodePrint(plist);}
//单链表删除pos位置的值
void text9()
{SLNode* plist = NULL;SLNodePushBack(&plist, 1);SLNodePushBack(&plist, 2);SLNodePushBack(&plist, 3);SLNodePushBack(&plist, 4);SLNodePrint(plist);//单链表删除pos位置的值SLNode* pos1 = SLNodeFind(plist, 1);SLNode* pos2 = SLNodeFind(plist, 3);SLNodeErasepos(&plist, pos1);SLNodeErasepos(&plist, pos2);SLNodePrint(plist);}
int main()
{//text1();//单链表尾插测试//text2();//单向链表头插测试//text3();//单向链表尾删测试//text4();//单向链表头删测试//text5();//单向链表查找 val 测试//text6();// 单链表在pos位置之后插入 x 测试//text7();//单链表删除pos之后的值测试//text8();//单链表在pos前一个位置插入 x 测试//text9();//单链表删除pos位置的值测试return 0;
}

3.2带头双向循环链表的实现

3.2.1带头双向循环链表的声明

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int DataType;typedef struct ListNode
{DataType val;struct LTNode* next;struct LTNode* prev;
}LTNode;

3.2.2动态申请一个节点

//Create one newnode
LTNode* Createnode(DataType x)
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){perror("malloc");exit(-1);}newnode->val = x;newnode->next = NULL;newnode->prev = NULL;return newnode;
}

 3.2.3哨兵位初始化(创建链表的头结点)

//哨兵位初始化(创建链表的头结点)
LTNode* LTInit()
{LTNode* plist = Createnode(-1);plist->next = plist;plist->prev = plist;return plist;
}

 3.2.4带头双向循环链表打印

//带头双向循环链表打印
void LTPrint(LTNode* plist)
{assert(plist);LTNode* cur = plist->next;printf("哨兵位<=>");while (cur != plist){printf("%d<=>", cur->val);cur = cur->next;}printf("\n");
}

 3.2.5双向链表尾插

// 双向链表尾插
void LTNodePushBack(LTNode* plist, DataType x)
{LTNode* newnode = Createnode(x);LTNode* tail = plist->prev;tail->next = newnode;newnode->prev = tail;plist->prev = newnode;newnode->next = plist;}

  3.2.6双向链表头插

// 双向链表头插
void LTNodePushFront(LTNode* plist, DataType x)
{assert(plist);LTNode* first = plist->next;LTNode* newnode = Createnode(x);newnode->next = first;first->prev = newnode;plist->next = newnode;newnode->prev = plist;}

3.2.7双线链表尾删

// 双向链表尾删
void LTNodePopBack(LTNode* plist)
{//防止链表不存在assert(plist);//防止链表为空assert(plist->next);LTNode* tail = plist->prev;plist->prev = tail->prev;LTNode* tailprev = tail->prev;tailprev->next = plist;
}

3.2.8双线链表头删

// 双向链表头删
void LTNodePopFront(LTNode* plist)
{//防止链表不存在assert(plist);//防止链表为空assert(plist->next);LTNode* first = plist->next;LTNode* second = first->next;plist->next = second;second->prev = plist;free(first);first = NULL;}

3.2.9双向链表查找

// 双向链表查找
LTNode* LTNodeFind(LTNode* plist, DataType x)
{//防止链表不存在assert(plist);//防止链表为空assert(plist->next);LTNode* cur = plist->next;while (cur != plist){if (cur->val == x){return cur;}cur = cur->next;}return NULL;
}

 3.2.10双向链表在pos的前面进行插入

// 双向链表在pos的前面进行插入
void LTNodeInsert(LTNode* pos, DataType x)
{assert(pos);LTNode* newnode = Createnode(x);LTNode* posprev = pos->prev;posprev->next = newnode;newnode->next = pos;pos->prev = newnode;newnode->prev = posprev;
}

3.2.11双向链表删除pos位置的结点

// 双向链表删除pos位置的结点
void LTNodeErase(LTNode* pos)
{//防止传空assert(pos);LTNode* posnext = pos->next;LTNode* posprev = pos->prev;posprev->next = posnext;posnext->prev = posprev;free(pos);
}

 3.2.12双向链表销毁

// 双向链表销毁
void LTNodeDestory(LTNode* plist)
{//防止链表不存在assert(plist);LTNode* cur = plist->next;while (cur != plist){LTNode* next = cur->next;free(cur);cur = next;}free(plist);plist = NULL;
}

以上代码为单链表所实现的所有功能(接口)


头文件:

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int DataType;typedef struct ListNode
{DataType val;struct LTNode* next;struct LTNode* prev;
}LTNode;//生成新节点
LTNode* Createnode(DataType x);//哨兵位初始化
LTNode* LTInit();//带头双向循环链表打印
void LTPrint(LTNode* plist);// 双向链表销毁
void LTNodeDestory(LTNode* plist);// 双向链表尾插
void LTNodePushBack(LTNode* plist, DataType x);// 双向链表尾删
void LTNodePopBack(LTNode* plist);// 双向链表头插
void LTNodePushFront(LTNode* plist, DataType x);// 双向链表头删
void LTNodePopFront(LTNode* plist);// 双向链表查找
LTNode* LTNodeFind(LTNode* plist, DataType x);// 双向链表在pos的前面进行插入
void LTNodeInsert(LTNode* pos, DataType x);// 双向链表删除pos位置的结点
void LTNodeErase(LTNode* pos);

测试文件:

#define  _CRT_SECURE_NO_WARNINGS 1
#include"list.h"
// 初步测试
void test1()
{// 创建一个哨兵位头节点LTNode* plist = LTInit();LTPrint(plist);
}// 尾插尾删测试
void test2()
{LTNode* plist = LTInit();LTNodePushBack(plist, 1);LTNodePushBack(plist, 2);LTNodePushBack(plist, 3);LTNodePushBack(plist, 4);LTNodePushBack(plist, 5);LTPrint(plist);LTNodePopBack(plist);LTPrint(plist);
}
// 头插头删测试
void test3()
{LTNode* plist = LTInit();LTNodePushFront(plist, 1);LTNodePushFront(plist, 2);LTNodePushFront(plist, 3);LTNodePushFront(plist, 4);LTNodePushFront(plist, 5);LTPrint(plist);LTNodePopFront(plist);LTPrint(plist);
}
// 链表查找测试
void test4()
{LTNode* plist = LTInit();LTNodePushBack(plist, 1);LTNodePushBack(plist, 2);LTNodePushBack(plist, 3);LTNodePushBack(plist, 4);LTNodePushBack(plist, 5);LTPrint(plist);LTNode* pos = LTNodeFind(plist, 3);printf("%d\n", pos->val);}
// 双向链表在pos的前面进行插入测试
void test5()
{LTNode* plist = LTInit();LTNodePushBack(plist, 1);LTNodePushBack(plist, 2);LTNodePushBack(plist, 3);LTNodePushBack(plist, 4);LTNodePushBack(plist, 5);LTPrint(plist);LTNode* pos = LTNodeFind(plist, 3);LTNodeInsert(pos, 100);LTNodeInsert(pos, 200);LTPrint(plist);}
// 双向链表删除pos位置的结点测试
void test6()
{LTNode* plist = LTInit();LTNodePushBack(plist, 1);LTNodePushBack(plist, 2);LTNodePushBack(plist, 3);LTNodePushBack(plist, 4);LTNodePushBack(plist, 5);LTPrint(plist);LTNode* pos = LTNodeFind(plist, 3);LTNodeErase(pos);LTPrint(plist);
}
// 链表销毁测试
void test7()
{LTNode* plist = LTInit();LTNodePushBack(plist, 1);LTNodePushBack(plist, 2);LTNodePushBack(plist, 3);LTNodePushBack(plist, 4);LTNodePushBack(plist, 5);LTNodeDestory(plist);plist = NULL;LTPrint(plist);
}
int main()
{// 初步测试test1();// 尾插尾删测试test2();// 头插头删测试test3();// 链表查找测试test4();// 双向链表在pos的前面进行插入测试test5();// 双向链表删除pos位置的结点测试test6();// 链表销毁测试//test7();return 0;
}

四.链表总结

这是一个我个人做的思维导图,对于学习链表的一些总结,希望对你有所帮助:

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

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

相关文章

亚马逊防关联要注意什么?看这一篇,防关联有技巧!

亚马逊账号关联的问题&#xff0c;对于跨境电商来说都不陌生&#xff0c;店铺的安全问题往往和账号关联有关&#xff0c;一旦亚马逊账号被关联就很可能导致我们的店铺被封&#xff0c;对于被亚马逊封店的卖家都会有申诉机会&#xff0c;如果无法成功申诉&#xff0c;那将永久被…

C语言——利用函数递归,编写函数不允许创建临时变量,求字符串长度

#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>int my_strlen(char* str) {if(*str ! \0)return 1my_strlen(str1);elsereturn 0; }int main() {char arr[] "hello";int len my_strlen(arr); //arr是数组&#xff0c;数组传参&#xff0c;传过去的是第…

NB水表能承受最大的水压是多少?

NB水表&#xff0c;作为新一代智能水表&#xff0c;以小巧的体积、稳定的性能和强大的功能赢得了市场的认可。那么&#xff0c;它究竟能承受多大的水压呢&#xff1f;接下来&#xff0c;小编来为大家揭秘下&#xff0c;一起来看下吧&#xff01; 一、NB水表概述 NB水表&#xf…

安卓手机好用的清单软件有哪些?

生活中每个人都有丢三落四的习惯&#xff0c;伴随着生活节奏的加快&#xff0c;人们常忘事的情况会更加频繁的出现&#xff0c;这时候很多人就开始选择手机上记录清单类的软件&#xff0c;安卓手机在手机市场中占有很大的分量&#xff0c;在安卓手机上好用的记录清单的软件有哪…

SiP封装、合封芯片和芯片合封是一种技术吗?都是合封芯片技术?

合封芯片、芯片合封和SiP系统级封装经常被提及的概念。但它们是三种不同的技术&#xff0c;还是同一种技术的不同称呼&#xff1f;本文将帮助我们更好地理解它们的差异。 一、合封芯片与SiP系统级封装的定义 首先合封芯片和芯片合封都是一个意思 合封芯片是一种将多个芯片&a…

【Leetcode每日一题】mid——6. N 字形变换

题目描述 https://leetcode.cn/problems/zigzag-conversion/description/ 将一个给定字符串 s 根据给定的行数 numRows &#xff0c;以从上往下、从左到右进行 Z 字形排列。 比如输入字符串为 “PAYPALISHIRING” 行数为 3 时&#xff0c;排列如下&#xff1a; 之后&#xff0…

技术细分|推荐系统——推荐系统中的数据去偏方法

本篇的主要脉络同样依据中科大何向南教授、合工大汪萌教授联合在 TKDE 上的一篇综述文章展开&#xff1a;Bias and Debias in Recommender System: A Survey and Future Directions。 下面按照前导文章中介绍的数据偏差 Selection Bias、Conformity Bias、Exposure Bias、Posit…

找JPG格式图片的地址(持续更新中)

问题描述&#xff1a;找JPG格式图片的地址 解决办法&#xff1a; 第一个 谷歌的images 第二个&#xff0c;搜狗图片和百度图片 不过下载是WEBP格式&#xff0c;可以使用一个在线WEBP格式转JPG格式的在线网站即可。 转换的网址为&#xff1a; https://ezgif.com/webp-to-j…

【学历是敲门砖】如果你想有个好的起点,不妨冲一冲计算机考研,这本书将会助你一臂之力

目录 计算机考研难点 《计算机考研精炼1000题》揭秘问答 1. 为什么是1000题&#xff1f; 2. 有什么优势&#xff1f; 3. 编写团队水平如何&#xff1f; 4. 题目及解析品质如何&#xff1f;可以试读吗&#xff1f; 购买链接 高质量的学习提升圈子 京东热卖下单链接&…

可视化NGINX管理平台Nginx Proxy Manager

# for CentOSyum install docker-compose -y# for Ubuntuapt-get install docker-compose -y 如果提示&#xff1a; 没有可用软件包 docker-compose&#xff0c; 错误&#xff1a;无须任何处理 通过 pip 安装 docker-compose # 添加企业版附加包 yum -y install epel-rel…

java--static的注意事项

1.使用类方法、实例方法时的几点注意事项 ①类方法中可以直接访问类的成员&#xff0c;不可以直接访问实例成员。 ②实例方法中既可以直接访问类成员&#xff0c;也可以直接访问实例成员。 ③实例方法中可以出现this关键字&#xff0c;类方法中不可以出现this关键字的。

甲方使用外包真的能节约成本吗?

语&#xff1a;外包作为一种常见的业务模式&#xff0c;被广泛应用于各行各业。然而&#xff0c;甲方在选择外包时&#xff0c;是否真的能够实现成本节约呢&#xff1f;本文将从多个角度进行探讨。 正文&#xff1a; 降低人力成本&#xff1a;外包通常是将某些业务环节或项目交…

2022年全国英烈纪念设施数据,各区县均有!

中国是一个拥有悠久历史和灿烂文化的国家&#xff0c;其英烈纪念设施承载着中国人民对为国家独立、民族解放和民主进步而英勇斗争的先烈们的崇敬和缅怀之情。 这些设施不仅是中国革命历史和先烈精神的重要载体&#xff0c;也是传承红色文化、弘扬革命精神的重要场所。 今天分享…

【RtpRtcp】3: webrtc m79:video 相关创建及切片、发送

m79 的 客户端代码流程。 对于视频帧: CreateRtpStreamSenders 管理一组RtpStreamSender ,每一个RtpStreamSender 都进行rtp包的发送: 具体发送是RTPSenderVideo 处理,RTPSenderVideo 对收到的h264 帧,进行帧分片rtp,然后一组包一起使用LogAndSendToNetwork 发送的。 Rtp…

Linux中df命令使用

在Linux中&#xff0c;df命令用于显示磁盘空间的使用情况。它的基本语法如下&#xff1a; df [选项] [文件或目录]这个命令可以用来查看当前系统上各个磁盘分区的使用情况。如果没有指定文件或目录&#xff0c;则所有当前被挂载的文件系统的可用空间将被显示。 df命令的一些常…

windows11下安装Tensor RT,并在conda虚拟环境下使用

建议仔细读一读NVIDIA官方出的安装教程&#xff0c;里面有windows、linux等安装教程&#xff0c;非常详细&#xff0c;这里再做一下简要总结。 TensorRT主要有三种安装模式、五种安装方式 毫无疑问&#xff0c;在windows系统中&#xff0c;我们只能选择zip安装。 安装tensorR…

安全牛《数据分类分级自动化建设指南》发布|美创入选代表厂商,分享智能化探索

近日&#xff0c;安全牛发布《数据分类分级自动化建设指南》研究报告&#xff0c;对数据分类分级的主要技术、实施要点、选型指导、发展趋势等展开深入探讨&#xff0c;为各行业数据分类分级自动化工作落地提供帮助与指引。 美创科技被列为代表推荐厂商&#xff0c;落地案例—农…

STM32-SPI协议详解及CubeMX+HAL函数配置分析

1 SPI协议 SPI(Serial Peripheral interface)串行外围设备接口是同步全双工的通信总线,在芯片的管脚上只占用四根线。 1.1 物理层 SS/NSS/CS:从设备选择信号线(片选信号线)。由主设备控制,选择指定的从设备。 当主机要选择从设备时,把该从设备的SS信号线设置为低电平…

轻松记录收支明细,一键打印,财务无忧!

作为现代人&#xff0c;管理好个人财务是非常重要的。但是&#xff0c;如何记录收支明细并打印出来呢&#xff1f;今天&#xff0c;我们向您推荐一款财务软件&#xff0c;帮助您轻松解决这个问题。 首先第一步&#xff0c;我们要打开【晨曦记账本】&#xff0c;并登录账号。 第…

2023.11.22 IDEA Spring Boot 项目热部署

目录 引言 操作步骤 1. 在 pom.xml 中添加热部署框架支持 2. Setting 开启项目自动编译 3. 以后创建的新项目进行同步配置 4. 重复 配置 步骤2 的内容 5. 开启运行中的热部署 引言 Spring Boot 的热部署是一种在项目正在运行的时候修改代码&#xff0c;却不需要重新启动…