当前位置: 首页 > news >正文

公司成立费用/广州seo培训

公司成立费用,广州seo培训,b2b网站如何推广,虚拟空间wordpress固定链接404本文图皆为作者手绘,所有代码基于vs2022运行测试 系列文章目录 数据结构初探:顺序表篇 文章目录 系列文章目录前言一、链表基础概念二、链表的分类简化边界条件处理使代码更清晰简洁提高程序稳定性 1.单链表(不带头不循环的单链表);1.1存储结构;1.2准备工作1.3链表增删查改的实…

本文图皆为作者手绘,所有代码基于vs2022运行测试

系列文章目录

数据结构初探:顺序表篇


文章目录

  • 系列文章目录
  • 前言
  • 一、链表基础概念
  • 二、链表的分类
      • 简化边界条件处理
      • 使代码更清晰简洁
      • 提高程序稳定性
    • 1.单链表(不带头不循环的单链表);
      • 1.1存储结构;
      • 1.2准备工作
      • 1.3链表增删查改的实现
        • 1.SList.h
        • 2.SList.c
          • 2.1链表的打印
          • 2.2节点的创建
          • 2.3链表的尾插
          • 2.4链表的头插
          • 2.5链表的尾删
          • 2.6链表的头删
          • 2.7链表的查找
          • 2.8链表的中间插入
          • 2.9链表的pos位置删除;
          • 2.10 链表的在pos位置之后插入
          • 2.11链表的在pos位置之后删除
          • 2.12链表的销毁
          • 2.13==完整代码==
        • 3.test.c
      • 1.4单链表的优缺点
    • 2.双向带头循环链表
  • 总结


前言

在编程的世界里,数据结构是构建高效算法的基石,而链表作为一种基础且重要的数据结构,有着独特的魅力和广泛的应用场景。本文将深入探讨链表的奥秘,从基础概念到复杂操作,再到实际应用,帮助你全面掌握链表这一数据结构。


一、链表基础概念

链表,从本质上来说,是一种线性的数据结构。它由一系列节点组成,这些节点就像是链条上的环,相互连接,形成了链表。每个节点都包含两个关键部分:数据域和指针域。数据域用于存储我们需要的数据,比如一个整数、一个字符串或者更为复杂的对象;而指针域则存储着下一个节点的内存地址,通过这个指针,各个节点得以串联起来,形成了链表的结构。

与我们熟悉的数组相比,链表在内存分配和操作方式上有着显著的不同。数组在内存中是连续存储的,这使得它可以通过索引快速地访问任意位置的元素,实现高效的随机访问。但这种连续存储的特性也带来了一些限制,比如在插入和删除元素时,往往需要移动大量的元素,操作的时间复杂度较高。而链表则不同,它的节点在内存中并不要求连续存储,插入和删除操作只需要修改相关节点的指针即可,时间复杂度较低。不过,链表由于没有索引,访问特定位置的元素需要从头节点开始依次遍历,这在一定程度上影响了它的访问效率。
以下是其基础结构:
在这里插入图片描述

二、链表的分类

接下来我会用一张图让你知道所有链表的类别:
在这里插入图片描述
这里共有八种链表形式,其中要说的一点是带头/不带头,也可以叫做带哨兵位/不带哨兵位.
哨兵位:
在链表等数据结构中,带有哨兵位的头结点有以下重要作用:

简化边界条件处理

  • 插入操作:在普通链表插入节点到头部时,需要单独处理头指针的更新,以防丢失链表头部。而有了哨兵头结点,插入操作就可统一为在哨兵头结点后的节点插入,无需特殊处理头指针。
  • 删除操作:删除链表第一个节点也需特殊处理头指针。有了哨兵头结点,删除操作可视为删除哨兵头结点后的节点,使删除操作逻辑统一。

使代码更清晰简洁

  • 遍历操作:在遍历链表时,无需在循环中专门处理头指针的特殊情况,可直接从哨兵头结点的下一个节点开始遍历,让遍历代码更简洁。
  • 其他操作:对于链表的合并、拆分等复杂操作,哨兵头结点能使操作的边界条件更清晰,让代码逻辑更易理解和维护,减少因边界条件处理不当导致的错误。

提高程序稳定性

  • 避免空指针问题:在没有哨兵头结点的链表中,如果链表为空,进行某些操作可能会导致空指针引用错误。而有了哨兵头结点,即使链表暂时没有数据节点,也有一个固定的头结点存在,可有效避免空指针问题,提高程序的稳定性和健壮性。

但是在现实生活中用的最多的链表类型有两种,分别是:
不带头不循环的单链表;
双向带头循环链表;
接下来就让我们分开来讲讲吧!

1.单链表(不带头不循环的单链表);

1.1存储结构;

这张图就是它的结构:
在这里插入图片描述
看完了图结构,现在我们来了解一下它的逻辑代码结构:

typedef int SLTDataType;//方便修改变量类型;
//定义链表结构体
typedef struct SListNode
{SLTDataType data;//数据存储struct SListNode* next;//存储下一个节点的地址
}SLTNode;

这就是我们创建的节点结构:
在这里插入图片描述

这就是链表的节点结构,要想构成庞大的数据链,我们还需要增删查改函数的实现;

1.2准备工作

创建对应的三个文件夹:
1.SList.h:用于存储创建链表的节点结构体和增删查改函数的函数声明,以及对应的库函数,方便生成程序时的链接;
2.SList.c:用于函数的实现;
3.test.c:用于测试和修改;
ps:2和3,均要包含头文件1,即 #include"SList.h"

1.3链表增删查改的实现

1.SList.h

让我们先来看看链表的函数声明长什么样:

#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>typedef int SLTDataType;
//定义链表结构体
typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SLTNode;
//打印
void SLTPrint(SLTNode* phead);
//节点的创建
SLTNode* CreatNode(SLTDataType d);
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x);
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x);
//尾删
void SLTPopBack(SLTNode** pphead);
//头删
void SLTPopFront(SLTNode** pphead);
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//在pos位置之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//在pos位置删除
void SLTErase(SLTNode** pphead, SLTNode* pos);
//在pos位置之后插入
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//在pos位置之后删除
void SLTEraseAfter(SLTNode* pos);
//销毁
void SLTDestroy(SLTNode* phead);

是不是感觉和顺序表大差不差? 但是接下来具体实现的时候你就会感觉到不同了!

2.SList.c
2.1链表的打印
//因为是对结构体内部指向的数据进行访问,我们只需要传一级指针,
//就可以做到改变实参的操作;
void SLTPrint(SLTNode* phead)
{//将传的头指针赋给curSLTNode* cur = phead;//只要不为空,就一直执行循环while (cur != NULL){//相当于遍历链表,进行打印;printf("%d-->", cur->data);cur = cur->next;//更新循环条件;}printf("NULL\n");//防止多次调用后,数据连在一起,所以添加换行符;
}

不太理解的话可以结合下图辅助理解:
在这里插入图片描述

2.2节点的创建
//因为需要它返回一个新节点给我,所以,我们的返回类型为SLTNode*
SLTNode* CreatNode(SLTDataType x)//因为我们是为了存储值而创建的节点,所以要传对应类型的值;
{//进行动态内存开辟,开一个结构体大小的空间作为新节点SLTNode* newNode = (SLTNode*)malloc(sizeof(SLTNode));if (newNode == NULL)//判断是否开辟失败;{perror("malloc fail");//返回错误行return NULL;//直接结束}newNode->data = x;//赋值给data;newNode->next = NULL;//因为是新节点,还未链接,所以我们置空return newNode;//节点创建完成并返回;
}

基础的动态内存开辟的使用;

2.3链表的尾插
//思考:为什么传的是二级指针呢?
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);//进行断言,防止传为空,从而报错;SLTNode* newNode = CreatNode(x);//节点的创建的复用
//判断链表是否为空if (*pphead == NULL){//若为空,则直接链接,让头直接指向新节点;*pphead = newNode;}else{//若不为空,则找尾节点SLTNode* tail = *pphead;//从头开始找while (tail->next != NULL)//如果节点的next==NULL,则找到了尾,跳出循环{tail = tail->next;//进入循环则是还没找到尾,继续往下走;}tail->next = newNode;//找到尾后,直接将tail的next链接至新节点即可}
}

就是不断找尾,然后尾插,记得要先判断是否为空;
在这里插入图片描述
现在我们来看看为什么要传二级指针:我们在对链表进行插入、删除节点等操作时,可能需要修改头指针的值。若只传递头指针的副本(一级指针),函数内对指针的修改不会影响到函数外的头指针。而传递二级指针,即指针的指针,函数就可以通过修改二级指针所指向的内容来改变头指针的值,使函数外的头指针也能得到正确更新。比如在向空链表插入第一个节点时,需要让头指针指向新插入的节点,就需要通过二级指针来实现。

2.4链表的头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newNode = CreatNode(x);//复用newNode->next = *pphead;//本身头指针就是指向第一个节点的地址,现在我先让新节点的next指向头节点*pphead = newNode;//所以新节点就成为了链表的第一个节点,再对头指针进行更新;
}

在这里插入图片描述
这就是头插的完整逻辑,大家可以试试特殊情况下,这个还成不成立,比如说链表为空是的头插;

2.5链表的尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead);//暴力检查//assert(*pphead);//当为空链表时;if (*pphead == NULL)//判断是否为空{printf("链表为空,无法删除\n");return;//为空就直接结束;}//当只有一个节点时;if ((*pphead)->next == NULL){free(*pphead);//直接释放,这就已经删除了;*pphead = NULL;//然后置为空return;}//当有多个节点时;SLTNode* tail = *pphead;//从头开始找尾节点的前一个节点while (tail->next->next != NULL)//直接找尾的话,之后就无法找到新的尾的next进行置空了{tail = tail->next;}free(tail->next);//free掉真正的尾tail->next = NULL;//对新的尾的next进行置空;
}

结合下图进行理解:
在这里插入图片描述
完善逻辑链;

2.6链表的头删
void SLTPopFront(SLTNode** pphead)
{assert(pphead);//当链表为空时;if (*pphead == NULL){printf("链表为空,无法删除\n");return;}else//不为空链表时,指针名称可以是del,head,first...{SLTNode* head = *pphead;//保存第一个节点的地址*pphead = head->next;//将头指针链接到第二个节点free(head);//释放并且置空head = NULL;}
}

逻辑如图:
在这里插入图片描述

2.7链表的查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{//从头开始找SLTNode* cur = phead;while (cur)只要不为空就继续{if (cur->data == x)//如果找到了{return cur;//就返回所在节点的地址}else{cur = cur->next;//否则继续往下找}}return NULL;//没找到就返回空指针
}

就是遍历查找啦;

2.8链表的中间插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pos);assert(pphead);//pos为头部时;if (pos == *pphead){SLTPushFront(pphead, x);//相当于头插;//也可以直接将头插写在这里,然后给pos为头,直接复用也可}else{SLTNode* prev = *pphead;//从头开始找while (prev->next != pos)//直到找到pos节点的前一个{prev = prev->next;}SLTNode* newNode = CreatNode(x);//复用prev->next = newNode;newNode->next = pos;//进行链接}
}

也可以找尾,然后给尾地址,这个也可以变成尾插
逻辑参考下图:
在这里插入图片描述

2.9链表的pos位置删除;
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pos);assert(pphead);//为头部时if (pos == *pphead){SLTPopFront(pphead);//相当于头删}else{SLTNode* prev = *pphead;while (prev->next != pos)//找到pos的前一个{prev = prev->next;}prev->next = pos->next;//前一个与后一个链接起来free(pos);//再将pos释放}为尾部时的逻辑代码,理解即可//SLTNode* tail = *pphead;//while (tail->next != NULL)//{//	tail = tail->next;//}//if (pos == tail)//{//	SLTPopBack(*pphead);//}}

参考上文的图例,可以自己尝试画画图来加深印象哦!

2.10 链表的在pos位置之后插入
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newNode = CreatNode(x);newNode->next = pos->next;pos->next = newNode;//直接进行链接;}
2.11链表的在pos位置之后删除
void SLTEraseAfter(SLTNode* pos)
{assert(pos);assert(pos->next);SLTNode* del = pos->next;pos->next = pos->next->next;free(del);del = NULL;
}

后面这两个是不是非常简单,就是因为我们已经知道pos,而且只要往后找就好啦;

2.12链表的销毁
void SLTDestroy(SLTNode* phead)
{SLTNode* cur = phead;while (cur)//逐个销毁{SLTNode* tmp = cur->next;free(cur);cur = tmp;}
}
2.13完整代码
#define _CRT_SECURE_NO_WARNINGS 1#include"SList.h"//链表的打印
void SLTPrint(SLTNode* phead)
{SLTNode* cur = phead;while (cur != NULL){printf("%d-->", cur->data);cur = cur->next;}printf("NULL\n");
}//链表元素的创建
SLTNode* CreatNode(SLTDataType x)
{SLTNode* newNode = (SLTNode*)malloc(sizeof(SLTNode));if (newNode == NULL){perror("malloc fail");return NULL;}newNode->data = x;newNode->next = NULL;return newNode;
}//链表的尾部插入
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newNode = CreatNode(x);if (*pphead == NULL){*pphead = newNode;}else{SLTNode* tail = *pphead;while (tail->next != NULL){tail = tail->next;}tail->next = newNode;}
}//链表头部插入的逻辑代码;
void SLTPushFront1(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newNode = CreatNode(x);if (*pphead == NULL){*pphead = newNode;//完全可以忽略这一步,直接用下一步即可;}else{newNode->next = *pphead;*pphead = newNode;}
}//链表的头部插入(优化);
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newNode = CreatNode(x);newNode->next = *pphead;*pphead = newNode;
}//链表的尾部删除(详细);
void SLTPopBack(SLTNode** pphead)
{assert(pphead);//暴力检查//assert(*pphead);//当为空链表时;if (*pphead == NULL){printf("链表为空,无法删除\n");return;}//当只有一个节点时;if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;return;}//当有多个节点时;SLTNode* tail = *pphead;while (tail->next->next != NULL){tail = tail->next;}free(tail->next);tail->next = NULL;
}//链表的头部删除;
void SLTPopFront(SLTNode** pphead)
{assert(pphead);//当链表为空时;if (*pphead == NULL){printf("链表为空,无法删除\n");return;}else//不为空链表时,指针名称可以是del,head,first...{SLTNode* head = *pphead;*pphead = head->next;free(head);head = NULL;}
}//链表的数据查找;
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* cur = phead;while (cur){if (cur->data == x){return cur;}else{cur = cur->next;}}return NULL;
}//链表的中间插入;
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pos);assert(pphead);//pos为头部时;if (pos == *pphead){SLTPushFront(pphead, x);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}SLTNode* newNode = CreatNode(x);prev->next = newNode;newNode->next = pos;}
}//删除pos位置的链表节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pos);assert(pphead);//为头部时if (pos == *pphead){SLTPopFront(pphead);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);}为尾部时//SLTNode* tail = *pphead;//while (tail->next != NULL)//{//	tail = tail->next;//}//if (pos == tail)//{//	SLTPopBack(*pphead);//}}//在pos之后插入
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newNode = CreatNode(x);newNode->next = pos->next;pos->next = newNode;}//在pos位置后删除
void SLTEraseAfter(SLTNode* pos)
{assert(pos);assert(pos->next);SLTNode* del = pos->next;pos->next = pos->next->next;free(del);del = NULL;
}//链表的销毁
void SLTDestroy(SLTNode* phead)
{SLTNode* cur = phead;while (cur){SLTNode* tmp = cur->next;free(cur);cur = tmp;}
}

接下来是测试代码;

3.test.c

直接上完整代码;

#define _CRT_SECURE_NO_WARNINGS 1#include"SList.h"void test1()
{SLTNode* phead = NULL;//βSLTPushBack(&phead, 1);SLTPushBack(&phead, 2);SLTPushBack(&phead, 3);SLTPushBack(&phead, 4);SLTPrint(phead);//ͷSLTPushFront(&phead, 20);SLTPushFront(&phead, 40);SLTPushFront(&phead, 60);SLTPushFront(&phead, 360);SLTPrint(phead);//βɾSLTPopBack(&phead);SLTPopBack(&phead);SLTPopBack(&phead);SLTPrint(phead);//ͷɾSLTPopFront(&phead);SLTPopFront(&phead);SLTPopFront(&phead);SLTPopFront(&phead);SLTPopFront(&phead);SLTPopFront(&phead);SLTPrint(phead);}void test2()
{SLTNode* phead = NULL;SLTPushBack(&phead, 1);SLTPushBack(&phead, 2);SLTPushBack(&phead, 3);SLTPushBack(&phead, 4);SLTPrint(phead);SLTNode* ret = SLTFind(phead, 2);printf("%d\n", ret->data);ret->data *= 2;SLTPrint(phead);SLTInsert(&phead, ret, 5);SLTPrint(phead);SLTInsertAfter(ret, 10000);SLTPrint(phead);SLTEraseAfter(ret);SLTPrint(phead);SLTErase(&phead, ret);SLTPrint(phead);SLTDestroy(phead);phead = NULL;SLTPrint(phead);}int main()
{test2();return 0;
}

1.4单链表的优缺点

其优缺点如下:
优点

  • 动态性:单链表可以根据需要动态地分配和释放内存空间,无需预先知道数据的数量,在插入和删除节点时,只需修改指针,不需要移动大量数据,操作效率较高。例如在实时处理大量用户请求的系统中,使用单链表可以灵活地添加和删除请求节点。
  • 插入和删除操作简便:在单链表中插入和删除节点通常只需要修改相关节点的指针,时间复杂度为O(1)。比如要在两个节点A和B之间插入节点C,只需让A的指针指向C,C的指针指向B即可。
  • 节省内存:相较于一些需要连续内存空间的数据结构,单链表的节点可以分散存储在内存中,能更有效地利用内存碎片,只要内存中有可用空间,就可以创建新节点。

缺点

  • 随机访问困难:单链表只能顺序访问,要访问第i个节点,必须从表头开始逐个节点遍历,时间复杂度为O(n)。比如要查找链表中间的某个节点,需要从头开始依次查找。
  • 额外空间开销:每个节点除了存储数据本身外,还需要额外的空间存储指针,当数据量较小时,指针占用的空间比例可能较大,会造成一定的空间浪费。
  • 节点指针易出错:在进行插入、删除操作或遍历链表时,如果指针操作不当,如指针丢失、指针指向错误等,可能会导致链表结构破坏,出现数据丢失或程序崩溃等问题。而且调试这类问题相对困难。

2.双向带头循环链表

篇幅至此,且容我下次再续


总结

  • 单链表是一种优缺点都很鲜明的数据结构。在实际的编程应用中,我们需要根据具体的需求和场景来权衡是否选择使用单链表。如果应用场景对插入和删除操作的效率要求较高,而对随机访问的需求较少,同时内存空间比较零散,那么单链表无疑是一个很好的选择。但如果需要频繁地进行随机访问操作,或者对内存空间的利用率要求极高,我们可能就需要考虑其他更合适的数据结构了。通过深入了解单链表的特性,我们能够在编程过程中更加游刃有余地选择和运用合适的数据结构,从而编写出更高效、更优质的代码。

相关文章:

数据结构初探:链表之单链表篇

本文图皆为作者手绘,所有代码基于vs2022运行测试 系列文章目录 数据结构初探:顺序表篇 文章目录 系列文章目录前言一、链表基础概念二、链表的分类简化边界条件处理使代码更清晰简洁提高程序稳定性 1.单链表(不带头不循环的单链表);1.1存储结构;1.2准备工作1.3链表增删查改的实…...

介绍一下Mybatis的底层原理(包括一二级缓存)

表面上我们的就是Sql语句和我们的java对象进行映射&#xff0c;然后Mapper代理然后调用方法来操作数据库 底层的话我们就涉及到Sqlsession和Configuration 首先说一下SqlSession&#xff0c; 它可以被视为与数据库交互的一个会话&#xff0c;用于执行 SQL 语句&#xff08;Ex…...

Linux基础 ——tmux vim 以及基本的shell语法

Linux 基础 ACWING y总的Linux基础课&#xff0c;看讲义作作笔记。 tmux tmux 可以干嘛&#xff1f; tmux可以分屏多开窗口&#xff0c;可以进行多个任务&#xff0c;断线&#xff0c;不会自动杀掉正在进行的进程。 tmux – session(会话&#xff0c;多个) – window(多个…...

64位的谷歌浏览器Chrome/Google Chrome

64位的谷歌浏览器Chrome/Google Chrome 在百度搜索关键字:chrome&#xff0c;即可下载官方的“谷歌浏览器Chrome/Google Chrome”&#xff0c;但它可能是32位的&#xff08;切记注意网址&#xff1a;https://www.google.cn/....&#xff0c; 即&#xff1a;google.cn&#xff…...

jetson编译torchvision出现 No such file or directory: ‘:/usr/local/cuda/bin/nvcc‘

文章目录 1. 完整报错2. 解决方法 1. 完整报错 jetson编译torchvision,执行python3 setup.py install --user遇到报错 running build_ext error: [Errno 2] No such file or directory: :/usr/local/cuda/bin/nvcc完整报错信息如下&#xff1a; (pytorch) nxnx-desktop:~/Do…...

多线程创建方式三:实现Callable接口

实现Callable第三种方式存在的原因 作用&#xff1a;可以返回线程执行完毕后的结果。 前两种线程创建方式都存在的一个问题&#xff1a;假如线程执行完毕后有一些数据需要返回,他们重写的run方法均不能直接返回结果。 如何实现 ● JDK 5.0提供了Callable接口和FutureTask类来…...

Linux下的编辑器 —— vim

目录 1.什么是vim 2.vim的模式 认识常用的三种模式 三种模式之间的切换 命令模式和插入模式的转化 命令模式和底行模式的转化 插入模式和底行模式的转化 3.命令模式下的命令集 光标移动相关的命令 复制粘贴相关命令 撤销删除相关命令 查找相关命令 批量化注释和去…...

Docker技术相关学习二

一、Docker简介 1.Docker之父Solomon Hykes形容docker就像传统的货运集装箱。 2.docker的特点和优势&#xff1a; 轻量级虚拟化&#xff1a;Docker容器相较于传统的虚拟机更加的轻量和高效&#xff0c;能够快速的启动和停止来节省系统资源。 一致性&#xff1a;确保应用程序在不…...

【人工智能】多模态学习在Python中的应用:结合图像与文本数据的深度探索

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 多模态学习是人工智能领域的一个重要研究方向,旨在通过结合多种类型的数据(如图像、文本、音频等)来提高模型的性能。本文将深入探讨多模…...

【MySQL】常用语句

目录 1. 数据库操作2. 表操作3. 数据操作&#xff08;CRUD&#xff09;4. 高级查询5. 索引管理6. 用户与权限7. 数据导入导出8. 事务控制9. 其他实用语句注意事项 如果这篇文章对你有所帮助&#xff0c;渴望获得你的一个点赞&#xff01; 1. 数据库操作 创建数据库 CREATE DATA…...

Docker网络基础

一、Docker网络基础 1.docker安装后会自动创建3中网络&#xff0c;分别为bridge host none docker network ls 2.docker原生bridge网络&#xff1a; docker安装时会创建一个名为docker0的linux bridge,新建的容器会自动桥接到这个接口 bridge模式下没有公有ip,只有宿主机可以…...

重新刷题求职2-DAY2

977. 有序数组的平方 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。 示例 1&#xff1a; 输入&#xff1a;nums [-4,-1,0,3,10] 输出&#xff1a;[0,1,9,16,100] 解释&#xff1a;平方后…...

[STM32 标准库]EXTI应用场景 功能框图 寄存器

一、EXTI 外部中断在嵌入式系统中有广泛的应用场景&#xff0c;如按钮开关控制&#xff0c;传感器触发&#xff0c;通信接口中断等。其原理都差不多&#xff0c;STM32会对外部中断引脚的边沿进行检测&#xff0c;若检测到相应的边沿会触发中断&#xff0c;在中断中做出相应的处…...

Slint的学习

Slint是什么 Slint是一个跨平台的UI工具包&#xff0c;支持windows,linux,android,ios,web&#xff0c;可以用它来构建申明式UI,后端代码支持rust,c,python,nodejs等语言。 开源地址&#xff1a;https://github.com/slint-ui/slint 镜像地址&#xff1a;https://kkgithub.com/…...

STM32 DMA+AD多通道

接线图 代码配置 ADC单次扫描DMA单次转运模式 uint16_t AD_Value[4]; //DMAAD多通道 void DMA_Config(void) {//定义结构体变量 GPIO_InitTypeDef GPIO_InitStructure;//定义GPIO结构体变量 ADC_InitTypeDef ADC_InitStructure; //定义ADC结构体变量 DMA_InitTypeDef DMA_In…...

如何构建ObjC语言编译环境?构建无比简洁的clang编译ObjC环境?Windows搭建Swift语言编译环境?

如何构建ObjC语言编译环境? 除了在线ObjC编译器&#xff0c;本地环境Windows/Mac/Linux均可以搭建ObjC编译环境。 Mac自然不用多说&#xff0c;ObjC是亲儿子。(WSL Ubuntu 22.04) Ubuntu可以安装gobjc/gnustep和gnustep-devel构建编译环境。 sudo apt-get install gobjc gnus…...

【C语言】指针详解:概念、类型与解引用

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C语言 文章目录 &#x1f4af;前言&#x1f4af;指针的基本概念1. 什么是指针2. 指针的基本操作 &#x1f4af;指针的类型1. 指针的大小2. 指针类型与所指向的数据类型3. 指针类型与数据访问的关系4. 指针类型的实际意…...

VoIP中常见术语

在 VoIP&#xff08;Voice over Internet Protocol&#xff0c;基于互联网协议的语音传输&#xff09;技术中&#xff0c;涉及许多专业术语。以下是常见术语及其含义&#xff1a; 1. 核心协议相关 SIP&#xff08;Session Initiation Protocol&#xff0c;会话发起协议&#xf…...

360嵌入式开发面试题及参考答案

解释一下 802.11ax 和 802.11ac/n 有什么区别 速度与带宽 802.11n 支持的最高理论速率为 600Mbps,802.11ac 进一步提升,单流最高可达 866.7Mbps,多流情况下能达到更高,如 1.3Gbps 等。而 802.11ax(Wi-Fi 6)引入了更多先进技术,理论最高速率可达 9.6Gbps,相比前两者有大…...

物理群晖SA6400核显直通win10虚拟机(VMM)

写在前面&#xff1a;请先确保你的核显驱动支持开启SR-IOV 确保你的BIOS开启了以下选项&#xff1a; VT-D VMX IOMMU Above 4G ResizeBAR 自行通过以下命令确认支持情况&#xff1a; dmesg | grep -i iommudmesg | grep DMAR分配1个虚拟vGPU&#xff1a;echo 1 | sudo tee /sy…...

【NLP 20、Encoding编码 和 Embedding嵌入】

目录 一、核心定义与区别 二、常见Encoding编码 (1) 独热编码&#xff08;One-Hot Encoding&#xff09; (2) 位置编码&#xff08;Positional Encoding&#xff09; (3) 标签编码&#xff08;Label Encoding&#xff09; (4) 注意事项 三、常见Embedding词嵌入 (1) 基础词嵌入…...

雷赛LC2000

【一&#xff0c;概述】 这个是中型PLC 【二&#xff0c;外观】 网口编号&#xff1a; 【2】【3】 //默认ip&#xff1a;192.168.1.xxx 【0】【1】 可视化授权不如禾川Q系。 【三&#xff0c;总线轴】 因为本次带的轴是台达A2系列伺服 A2最快总线是【1ms】的倍数…...

ESP32开发工具介绍:Thonny——初学者的MicroPython利器

文章目录 引言什么是 Thonny?为什么选择 Thonny 开发 ESP32?1. **MicroPython 的天然支持**2. **极简的配置流程**3. **适合快速原型开发**如何用 Thonny 开发 ESP32?步骤 1:准备工作步骤 2:烧录 MicroPython 固件步骤 3:在 Thonny 中连接 ESP32步骤 4:编写并运行代码Th…...

【Go语言圣经】第六节:方法

第六章&#xff1a;方法 6.1 方法声明 在函数声明时&#xff0c;在其名字之前放上一个变量&#xff0c;这就是声明了变量对应类型的一个方法&#xff0c;相当于为这种类型定义了一个独占的方法。 下例为 Point 类型声明了计算两个点之间距离的方法&#xff1a; package mai…...

【Leetcode刷题记录】45. 跳跃游戏 II--贪心算法

45. 跳跃游戏 II 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向后跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j < nums[i]i j < n 返回到达 num…...

mysql_init和mysql_real_connect的形象化认识

解析总结 1. mysql_init 的作用 mysql_init 用于初始化一个 MYSQL 结构体&#xff0c;为后续数据库连接和操作做准备。该结构体存储连接配置及状态信息&#xff0c;是 MySQL C API 的核心句柄。 示例&#xff1a; MYSQL *conn mysql_init(NULL); // 初始化连接句柄2. mysql_…...

Qt网络相关

“ 所有生而孤独的人&#xff0c;葆有的天真 ” 为了⽀持跨平台, QT对⽹络编程的 API 也进⾏了重新封装。本章会上手一套基于QT的网络通信编写。 UDP Socket 在使用Qt进行网络编程前&#xff0c;需要在Qt项目中的.pro文件里添加对应的网络模块( network ). QT core gui net…...

deepseek接入pycharm 进行AI编程

要将DeepSeek接入PyCharm进行AI编程,可以按照以下步骤操作: ### 1. 获取DeepSeek API访问权限 DeepSeek通常以API的形式对外提供服务,你需要在其官方网站注册账号,申请API访问权限。在申请通过后,会获得API密钥(API Key),这是后续调用API的关键凭证。 ### 2. 安装必要…...

Verilog基础(三):过程

过程(Procedures) - Always块 – 组合逻辑 (Always blocks – Combinational) 由于数字电路是由电线相连的逻辑门组成的&#xff0c;所以任何电路都可以表示为模块和赋值语句的某种组合. 然而&#xff0c;有时这不是描述电路最方便的方法. 两种always block是十分有用的&am…...

生成式AI安全最佳实践 - 抵御OWASP Top 10攻击 (上)

今天小李哥将开启全新的技术分享系列&#xff0c;为大家介绍生成式AI的安全解决方案设计方法和最佳实践。近年来&#xff0c;生成式 AI 安全市场正迅速发展。据 IDC 预测&#xff0c;到 2025 年全球 AI 安全解决方案市场规模将突破 200 亿美元&#xff0c;年复合增长率超过 30%…...