数据结构_顺序表专题

news2024/11/15 10:48:01

何为数据结构?

咱今天也来说道说道......

数据结构介绍

  • 准确概念

数据结构就是计算机存储、组织数据的方式

  • 概念分析

从上句分析,数据结构是一种方式。一种管理数据的方式。为了做什么?为的就是计算机存储数据,组织数据。

存储数据好理解,就是把数据导进计算机。

组织数据:组织二字:就好比将军带兵。计算机无疑就是将军,数据就是征入军营的士兵们,数据现在还没真正成为士兵,得靠将军对它们进行管理、操练。比如增加能人志士,淘汰不合格的士兵,平时点卯(对士兵进行点名),对士兵在刀剑、骑射方面进行能力的增强。

概念解释清楚了,再看一眼概念:相信同学们已经把概念刻进了DNA。

数据结构就是计算机存储、组织数据的方式

将军之所以能为将帅之才,那自然是依靠它们善带兵之道,识人善用——即针对不同基础的士兵,独有一套招纳、组织方式。

今日,我们就看“队伍”之一——名为:顺序表这支军队里面平时如何招纳、组织数据。

先来看看这支队伍里面的士兵是为何种数据?

数据篇—顺序表里的数据

开门见山,核心一句话:顺序表的底层就是数组

什么意思且听我一言:既然是军队,那必然有队列阵型,每一个人站位何处都是定好了的。总不能这立一个,那躺一个,无组织无纪律,这明显和数据结构所达目的背道而驰。

数组为何能成为顺序表的底层数组,是⼀组相同类型元素的集合。听听,集合二字,可见它的组织性;相同类型元素,则暗示它浑然天成能打造一支基本素质不低的军队。说白了,顺序表要的就是数组这种凝聚力。

  • 阵型排列

具体阵型,简单看看:

地址我随便意思了一下,重点是什么? 这个顺序表和数组一个样,数组里面的元素数据类型是int型,每个数据拥有4个字节的空间,在数组里元素和元素之间地址连续

好了,如果本段的解释,让你明白:顺序表的底层就是数组,这一小节儿的重点就把握了。

  • 数据类型

照例,先给出核心:顺序表里要求的数据的类型较为广泛,上到内置类型(比如:int,float,double),下到自定义类型(结构体),再到大杂烩(基于内置类型与自定义类型定义的各种数据,比如,int a[ ],char b[ ]。

可以说,数据类型在顺序表里不作重点,简单了解到类型涵盖广泛就足够。

结构篇—顺序表里存储、组织数据

先来看,顺序表是如何招纳(存储)数据的,俗话说“巧妇难为无米之炊”,先得有数据才能管理、组织数据。

存储数据

顺序表在招纳数据的时候,为了满足顺序表本身是数组的特点,数组结构要求有二:类型与大小。

类型我们在前面已讨论—每个数据类型统一且数据类型范围较为广泛。

那么大小呢?在建立“这支队伍”之初,将军似乎不能预估士兵的多少,符合要求的数据到底有多少我们也未知,后续还会需要新的数据,空间不够怎么办?

谁来决定数组的大小?似乎成了问题,“韩信点兵,多多益善”,随着将军带兵之道不断精进,能带善于带更多的士兵(需要的数据不断引进),我们要以数组的形式的排列数据,但是又得灵活地开辟空间,大小是无法一口气拍定的。所以,顺序表里使用动态内存开辟空间(动态数组)对数据进行存储

这里就开始写代码了,我们分成三个文件:SeqList.h(顺序表头文件),SeqList.c(顺序表函数实现文件)以及test.c(测试文件)

//头文件SeqList.h 包含顺序表类型的定义(即到底是一支由什么数据组成的,有多少数据,现在空间大小几何的数组) 以及相关组织数据方法声明

typedef int SLDatatype;//此处int只为举例
struct SeqList
{
    SLDataType* arr;
    int size;//表示顺序表里有效数据个数
    int capacity;//表示当前顺序表里已开辟空间大小,表示现有capacity个数据的空间,为了后续开辟空间有依据可言
}
//既然是动态数组,我们无法在开辟数组空间无法得知需要的空间大小,但是又得用数组,我们使用指针(数组首元素地址即数组名),表示数组。同时,对于不同数组招募的数据不同,我们直接对int进行重命名,最后如果需要的数据类型不同,可以把typedef后的int改成所需的数据。 

 定义好顺序表的雏形后,可以预见,在今后代码创建顺序表时,我们的代码无疑就是创建一个结构体。

提醒一下“创建顺序表即创建一个结构体”,顺序表本质还是数组,实现出来的样子就是数组。只是在组织数据时需要size和capacity这个变量。

怎么理解呢? 好理解。 这军队里打仗上战场的是数组(军队);这把握局势,统揽全局,出谋划策、有助带兵的是谁?当然是军师(size和capacity)。在刚刚那张图里虽然看不见size和capacity,但是对于组织军队,起到重要作用的便是这俩。

所以定义顺序表,包含数组和重要的两个变量

//test.c文件

int main()
{
   struct SeqList sl;//名字很长啊,为了方便定义,我们在顺序表定义对结构体重命名。
   return 0;
}

故最后的顺序表定义代码:

typedef int SLDataType;
//定义一个动态顺序表
typedef struct SeqList
{
	SLDataType* arr;
	int size;//数组里有效数据个数—元素个数
	int capacity;//数组当前大小
}SL;

综上,为了方便存储不同类型的数据,灵活地调整存储数据的空间,我们分别使用重命名typedef成SLDataType 和 动态开辟内存先只给一个指针表示数组和两个变量方便后续操作) 

组织数据

现在有了存储数据的方式,在没有新来数据时,我们得做些准备工作(初始化)。

一、顺序表(动态数组)的初始化 

//SeqlList.c 

void SLInit(SL sl)
{
    sl.arr = NULL;
    sl.size = sl.capacity = 0;
}

//test.c来检验测试写的实现方法是否有bug

#include "SeqList.h" 
int main()
{
   SL sl;//这里使用SeqList.h定义的类型,不包含就找不到这个顺序表,后续一些函数也无法调用
   SLInit(sl);
   return 0;
}

易错:传值调用和传址调用

经测试,如果这里传SL sl,会报错:使用了未初始化的局部变量sl

解:顺序表在SLInit方法里,形参拷贝了一份sl的值,但是传过去的sl本身就没有值,更不能实现拷贝值一说。这就是为何报错说使用了未初始化的局部变量。

再者,使用了这样一份拷贝的顺序表,在SLInit方法里就算初始化成功,最后被初始化的复印件也会被删除,再看原件,毫无变化——故也没能达到初始化我们原本创建顺序表的目的。

所以,使用传址调用,地址在计算机里,具有唯一性。从不同维度解决问题,精准找到对的顺序表,而不是长得像的,临时的顺序表。

故顺序表的初始化方法代码:

void SLInit(SL* psl)
{
	psl->arr = NULL;
	psl->capacity = psl->size = 0;
}

二、顺序表的销毁

顺序表的销毁,对应我们的将士带兵论即为,在必要情况下,将士将每位士兵遣散回家,这遣散过程还包括扎营驻寨的空间的归还,军师也得衣锦还乡不是?。

拢共三个方面,那代码实现:

void SLDestroy(SL* psl)
{
    if(psl->arr != NULL)
     {
       //既然数组不为空,意味着存着这一支军队
       free(psl->arr);//可见数组的统一,不光组织起来方便,解散也方便
     }
     //数组为空||释放了数组后,说明数组已经不存在
     psl->arr = NULL;//这个数组已经不存在,不能再存地址了
     psl->size = psl->capacity = 0;//有效数据个数也为0,空间大小也为0

}

释放顺序表,考察的是对于各个部分安排是否妥当,切记考虑全面。

三、顺序表的增删查改

这才是真正体现组织数据技术含量的部分,不同的数据结构对于实现:增删查改,有着其独到之处。顺序表的增删查改。

两种基本增加数据方法,头插和尾插。头插即在数组下标为0位置处插入我们安排的数据,原来的数据都往后面挪一位(这位新来的数据,可见实力不小,能排在首位,后面的都得挪窝) 

尾插即在原来数组的最后一位有效数字后添加我们安排的数据,原来的数据不用挪位置。(这位新来的数据被安排到最后一位)

这数据招进来,类型统一是统一,可别忘了顺序表

还有size和capacity两个变量。新来一个数据,size++就好,capacity呢?万一空间不够了就安排不进了,这就是在增之前,我们需要考虑的——是否需要此时动态开辟内存

  • 是否需要动态开辟内存 

代码:

void SLCheckCapacity(SL* psl)
{
     if(psl->size == psl->capacity)//“扩容”就在此刻
     {
        //说明有效数字已经满了数组已经开辟的空间,也满足刚开始size和capacity同为0的情况
        //扩容操作如下:
        SLDataType* tmp = realloc(psl->arr,2 * psl->capacity * sizeof(SLDataType));//这句为什么用reallo? 因为我们是扩容,realloc函数就是实现在原有空间上继续开辟空间,有增容的概念。第一个参数是你所指定的空间,第二个参数是你所申请开辟的空间大小,因为以存储空间(内存)以字节为单位,故第二个参数要乘上一个数据的字节大小,2*capacity意思是2倍:我们常常2倍增容,这样到后期增容速率会变慢,且2倍增容浪费空间数不会太多。
        //左边为什么用tmp? tmp(暂时的),realloc函数的返回类型是一个数组首地址(指针),因为申请空间可能不成功,一旦不成功,返回NULL,那原有的数据就会丢失,保险起见,先用一个tmp接收。
        if(tmp == NULL)
        {
         // 意味着没申请成功
         perror("realloc:failed");//报错,提醒自己
        }     
     }
}

 realloc函数有些细节以及2倍增容真正数学原理,我没细说,不过这里的解释倒是够用。有兴趣的同学可以去详细了解。

void SLCheckCapacity(SL* psl)
{
     if(psl->size == psl->capacity)
     {
        SLDataType* tmp = realloc(psl->arr,2 * psl->capacity * sizeof(SLDataType)); //注意这里有个坑,先往后看,马上就说
        if(tmp == NULL)
        {
         // 意味着没申请成功
         perror("realloc:failed");//报错,提醒自己
        }
        else
        {
         // 申请成功了
          psl->arr = tmp;
          psl->capacity = 2 * psl->capacity;
        
        }

     }

}

 易错:psl->capacity==0时,开辟了0个空间

所以以后写一长串参数运算时,注意一下参数为0的情况。

真正的代码实现:

void SLCheckCapacity(SL* psl)
{
     
     if(psl->size == psl->capacity)
     {
        int NewCapacity = psl->capacity == 0 ? 4 : 2 * psl->capacity//创建一个新变量,判断原空间大小:如果为0,默认给它4个空间成为新空间,如果不为0,则在原有基础上2倍成为新空间数
        SLDataType* tmp = realloc(psl->arr,NewCapacity * sizeof(SLDataType)); 
        if(tmp == NULL)
        {
         // 意味着没申请成功
         perror("realloc:failed");//报错,提醒自己
        }
        else
        {
         // 申请成功了
          psl->arr = tmp;
          psl->capacity = NewCapacity;
        
        }

     }

}
  • 头插

判断完空间需要扩容后,现在的空间大小已经满足要求。我们开始实现插入数据,最后再size++。

先来看头插:根据刚刚的描述,安排首位为新数据,后面的都要向后移一位。真正逻辑是,先有得首位空了,才能安排新数据;但是要让首位空,所有数据都得先后移一位。故先循环,让后面的数据先移位,循环结束后,下标为0的元素就赋上新数据,再size++;

void SLPushFront(SL* psl,SLDataType x)
{
    assert(psl);//断言:文章最后一部分有解释
    SLCheckCapacity(psl);
    for(int i = psl->size;i ;i--)//移位得从后往前,好好想想:如果从前往后移,首先0下标数字一移就把下标为1的数字就覆盖了,这一改就再也找不到了。及时止损,换个思路:从后往前挪,后面的先后挪总会留下一个空位,画个图就明白了
   {
       psl->arr[i] = psl->arr[i - 1];//循环的最后,就是arr[1] = arr[0];由此得出i的取值范围:i > 0

   }
    psl->arr[0] = x;
    psl->size++;
}
  •  尾插

代码思路:先照例检查是否有足够空间,再尾插,这个尾插,插在有效数字的最后,不用移位那也用不上for循环。核心就一句arr[ ] = x

void SLPushBack(SL* psl,SLDataType x)
{
   assert(psl);
   SLCheckCapacity(psl);
   psl->arr[] = x;
   psl->size++;
}

 那【】中到底是什么?

 size本来记录的就是数组里面有效的数字,还是随着数组元素增加依次增加,数组下标从0开始,到size下标时,刚好对应没有数组元素。

补全合并代码,最终的尾插方法:

void SLPushBack(SL* psl,SLDataType x)
{
   assert(psl);
   SLCheckCapacity(psl);
   psl->arr[psl->size++] = x;//size后++,对于安排数字不影响,优雅
}

和“增”一样,删这种组织数据的方法,也分头删和尾删  

  • 头删 

模拟一下头删场景,借此来窥探代码思路。

头删,头就是下标为0的元素。

 

“好图不嫌多用”,我们在删除此图的26时,最后达成的效果是

对比很鲜明啊,最明显的也是在刚开始写代码容易忽略的就是size--。下标为1及后面的元素都往前了一位。无疑是有循环的,被我们忽略的还有一个26,我们需要单独对它做什么吗?似乎看来,所有数据覆盖后,就达成了这个效果,单独操作不合理也费劲

代码:

void SLPopFront(SL* psl)
{
    assert(psl);
    for(int i = 0;i < psl->size - 1;i++)
    {

     psl->arr[i] = psl->arr[i + 1];//最后一次就是arr[size - 2] = arr[size - 1];由此确定i < size - 1(看移动前那张图分析)
    }
    psl->size--;
}
  •  尾删

 同样模拟一下尾删场景,借此来摸索代码思路。

尾删,就是删掉当前数组的最后一位有效数字。在这里就是3

删完后,就是这个效果:

无疑size--,元素并没有发生移位,不用for循环。

代码:

void SLPopBack(SL* psl)
{
   assert(psl);
   psl->size--;//没错,size--就可以,不用对3进行什么操作
}
//为什么?size--后,我们在增加数字时,(头插、尾插),3都会被覆盖,不影响;在删除数字时(头删,我们for循环也只会关心size下标内的数据,3不在有效范围内,不影响;尾删不影响,继续size--),也只关心size内的数据。查找,修改数据更是,不然我们刚开始为何要定义一个变量叫当前数组的有效个数,因为我们只关心下标size之前的数据。
 查

我们这里的查,是根据已给的数据在原数组里判断是否存在。

思路很简单,将数组元素遍历,每个元素与x比较,如果相等就找到了;反之,循环结束后若还没找到,说明当前数组不存在该数据

代码:

int FindByX(SL* psl,SLDataType x)
{
      assert(psl);
      for(int i = 0;i < psl->size;i++)
      {
        if(psl->arr[i] == x)
        {
           return i;//这里先不考虑x有多个的情况,先掌握思路
        }
      }
      return -1;//都直到循环结束,也没找到对应的下标,说明数组里面是没有x这个元素的,故返回一个无效下标


}

//test.c
  int main()
{
 //...
 // 测试“查”方法
    int find = FindByX(psl,x)
      if(find < 0)
      {
       printf("没找到\n");
       
     }
      else{
      printf("找到了,下标为%d\n",find);
     }
 //... 
  return 0;
}

两种情况:一种指定下标更改:一步到位arr[指定下标] = x;

还有一种情况,改数组里面的某个数字,那第一步得找到那个数字的对应下标不是?再加上arr[find] = x;

这个没什么坑,直接上代码:

void SLModify(SL* psl,SLDataType x,SLDataType Newx)//为了方便,在参数里面我直接规定了新数据,其实还可以用scanf通过键盘录入新数据
{
   assert(psl);
   int find = FindByX(psl,x);
   if(find < 0)
   {
  
   }
   else
   {
    psl->arr[find] =  Newx;
}

}

//情况二
void SLModify2(SL* psl,int pos,SLDataType Newx)
{
   psl->arr[pos] = Newx
}

四、特殊的增和删

在增加或者删除数据时,我们只介绍了头增和尾增、头删和尾删;不具有增加的任意性:万一所增的数据偏偏非头非尾,所删的数据也在中间某一个位置。

顺序表的指定位置之前插入数据

我们还是通过那张“好图”来模拟场景实现,借此摸索代码思路。

给出这样一个顺序表,现在我们要在数组下标为pos的地方即“1”之前增加一个数据“55”。最后这个顺序表长这样:

上下对比,先看数组外,变量size++了(增加一个数据,有效数据个数可不就得++),再来看数组内元素是否发生移位:发生了,发生的范围在pos及pos后面的所有数据,arr[pos] = 1数据都发生了移位;我们因此能确定使用for循环;并且i的起点应该是pos。

按照之前的理解,思路都是先移位,让arr[pos]空出来,最后arr[pos] = 55,size++,同理可得代码:

void SLInsert(SL* psl,int pos,SLDataType x)
{
   assert(psl);
   for(int i = size;i > pos;i--)
   {
     psl->arr[i] = psl->arr[i - 1];//可以看到这里仍然是从后向前移动数据,循环结束后达到在目的位置为空
   }
   pal->arr[pos] = x;
   psl->size++;
}
//发现没,与头插代码相比,变的就是循环的元素个数(与pos有关)

你会发现,这个方法里,一旦pos==0 就是我们写的头插,pos==size就是我们写的尾插;简而言之,我们对下标任意化了,这就是前文所说的任意性。

顺序表的指定位置删除数据

同样,数据结构要的就是一个代码“跃然纸上”的效果。

这次我们要删除arr[pos] = 1这个数据了。最后的效果:

size变没有?变了——size--;元素移位没有?移了,for循环。有了pos,可以确定循环变量的范围。

代码实现:

void SLDel(SL* psl,int pos)
{
  assert(psl);
  for(int i = pos;i < psl->size - 1;i++)
  {
     psl->arr[i] = psl->arr[i - 1];//最后一步是arr[size - 2] = arr[size - 1]
  }
  psl->size--;
}

 你会发现,这个方法里,一旦pos==0 就是我们写的头删,pos==size就是我们写的尾删;简而言之,我们对下标任意化了,这也对应前文所说的任意性。

代码优化——断言处理:

参数只说了传指针,没说不能传空。

比如:

调试结果:

 究竟原因其实是:对空指针进行解引用,为何不能对空指针进行解引用:操作系统和硬件通常不会将物理内存地址0分配给任何实际的内存块,因此尝试访问这个地址会导致硬件异常,比如段错误或访问违规。操作系统捕获到这种异常后,通常会终止程序的执行。

空指针往往被分配的是NULL代表着0x00,也同时意味着这是个抽象地址,既然底层没有任何物理内存支持,尝试访问是违法的,况且里面也不能存储什么有效数据。(我知道有些小伙伴会忘记之前学过的指针知识,特意再详说)

其实在传的参数为指针时,当用户传来无效指针(空指针)野指针,代码里一旦对空指针解引用(例如:psl->arr[i],psl->size,其中psl是NULL,而->是对指针的解引用),程序就直接崩溃了;为了提高代码的健壮性,我们在函数的第一步对传过来的指针进行断言assert(指针),如果为空,程序会直接终止,报错;并提醒我们发生何处断言错误。——可见,检验传参(指针)有效性,是很有必要的。

总结

Q&A: 数据结构是做什么的? 计算机存储、组织数据的方式

            为什么有顺序表?为了方便管理一堆相同类型、数量且不一定的数据。

            方便体现在何处?动态数组和一些现成(现在方法都得自己敲出来实现,才能叫现成)的“增删查改”方法;与以前定长的数组相比,简单灵活得多

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

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

相关文章

docker部署seata 2.0.0

环境准备 当前使用的环境&#xff1a; MySQL&#xff1a;8.0 nacos&#xff1a;2.2.3 关于如何在docker中部署nacos 2.2.3&#xff0c;请参考之前文章&#xff1a; docker部署nacos 2.2.3 拉取镜像 docker pull seataio/seata-server:2.0.0查看nacos、mysql、宿主机的ip d…

探索Facebook:数字社交的魔力源泉

在当今信息爆炸和全球互联的时代&#xff0c;社交媒体平台成为了人们生活中不可或缺的一部分。而在这些平台中&#xff0c;Facebook无疑是最具影响力和创新性的代表之一。自2004年成立以来&#xff0c;Facebook不仅改变了人们的沟通方式&#xff0c;更通过不断的技术创新和用户…

Oracle查询超时问题,聊聊思路!

&#x1f3c6;本文收录于《CSDN问答解答》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&…

【系统架构设计师】九、软件工程(面向对象方法|逆向工程)

目录 六、面向对象方法 6.1 基本概念 6.2 面向对象的分析 6.2.1 用例关系 6.2.2 类之间的关系 6.3 面向对象的设计 6.4 面向对象设计原则与设计模式 6.5 面向对象软件的测试 七、逆向工程 历年真题练习 六、面向对象方法 面向对象的分析方法 (Object-Oriented Analys…

C++初阶:类和对象(二)

✨✨所属专栏&#xff1a;C✨✨ ✨✨作者主页&#xff1a;嶔某✨✨ 类的默认成员函数 默认成员函数就是用户没有显式实现&#xff0c;编译器会⾃动⽣成的成员函数称为默认成员函数。⼀个类&#xff0c;我们不写的情况下编译器会默认⽣成以下6个默认成员函数&#xff0c;需要注…

Verilog基础:简单标识符和转义标识符

相关阅读 Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482 标识符(identifier)是一个为了引用而给一个对象起的名字。一个标识符可以是一个简单标识符&#xff0c;也可以是一个转义标识符。本文将对两者进行详细阐述。 简…

ctfshow-web入门-php特性(web109-web115)

目录 1、web109 2、web110 3、web111 4、web112 5、web113 6、web114 7、web115 1、web109 正则匹配要求 v1 和 v2 都包含字母&#xff0c;eval 函数将字符串作为 PHP 代码执行&#xff1a;new $v1 创建一个名为 v1 的类的实例&#xff0c;($v2()) 调用 v2 方法&#xff…

使用Nginx OpenResty与Redis实现高效IP黑白名单管理

1、引言 在当今数字化时代&#xff0c;网络安全已成为企业和个人用户关注的焦点。IP黑白名单作为一种有效的网络安全策略&#xff0c;允许我们精确控制对Web资源的访问权限。通过白名单&#xff0c;我们可以确保只有可信的IP地址能够访问敏感资源&#xff1b;而黑名单则可以阻…

gptpdf:使用大模型(如 GPT-4o)将 PDF 解析为 markdown。

今天给大家分享一个开源的项目&#xff0c; 使用视觉大语言模型&#xff08;如 GPT-4o&#xff09;将 PDF 解析为 markdown。 方法非常简单(只有293行代码)&#xff0c;但几乎可以完美地解析排版、数学公式、表格、图片、图表等。 使用 GeneralAgent lib 与 OpenAI API 交互。…

链接追踪系列-08.mac m1安装logstash-番外

下载地址&#xff1a;https://elasticsearch.cn/download/ 配置es相关&#xff1a; #安装plugin&#xff1a; jelexbogon bin % ./logstash-plugin install logstash-codec-json_lines启动&#xff1a;指定配置文件运行 jelexbogon bin % nohup ./logstash -f ../config…

破解YouTube限制:保姆级教程,轻松查看博主邮箱

近期YouTube取消了博主的邮箱展示&#xff0c;这造成了不小的轰动&#xff0c;给想要联系博主的粉丝和想要寻求网红合作的品牌都带来了极大的不便。但这难不倒万能的网友&#xff01;最新发现&#xff0c;通过一串神秘代码可以在YouTube上查看到博主的邮箱&#xff0c;这里Nox聚…

微信小游戏 彩色试管 倒水游戏 逻辑 (二)

最近开始研究微信小游戏&#xff0c;有兴趣的 可以关注一下 公众号&#xff0c; 记录一些心路历程和源代码。 定义一个 Water class 1. **定义接口和枚举**&#xff1a; - WaterInfo 接口定义了水的颜色、高度等信息。 - PourAction 枚举定义了水的倒动状态&#xff0c;…

Gil-Pelaez inversion

一、特征函数 A.随即变量的特征函数定义与性质 B.特征函数与PDF的关系 傅里叶变换:C.特征函数与矩函数关系 二、Gil-Pelaez反演定理 输入功率 P i n P_{in}

Kotlin标准函数(语法糖)let with run also apply快速讲解

目录 1、知识储备——扩展函数 原理 定义扩展函数 调用扩展函数 2、返回值为上下文对象的标准函数 apply also 3、返回值为Lambda表达式结果 let run with 4、一表总结 1、知识储备——扩展函数 原理 Kotlin 在不继承父类或实现接口下&#xff0c;也能扩展一个类的…

Linux进程通信--共享内存

文章目录 概述共享内存基本原理共享内存的操作创建共享内存函数接口形成key--fotk创建共享内存代码演示补充指令集--ipc的指令key和shmid区别创建并获取共享内存代码 删除共享内存函数接口删除共存内存函数代码演示 共享内存段连接到进程地址空间函数接口代码演示 取消关联代码…

真空油炸机的特点是什么?

真空油炸机的特点&#xff0c;如同一位技艺精湛的厨师&#xff0c;不仅确保了食材的完美呈现&#xff0c;更在科技与传统工艺之间找到了完美的平衡。 首先&#xff0c;真空油炸机以其独特的真空环境&#xff0c;为食材打造了一个低氧、低压的烹饪空间。在这样的环境中&#xff…

LabVIEW比例压力控制阀自动测试系统

开发了一套基于LabVIEW编程和PLC控制的比例控制阀自动测试系统。该系统能够实现共轨管稳定的超高压供给&#xff0c;自动完成比例压力控制阀的耐久测试、流量滞环测试及压力-流量测试。该系统操作简便&#xff0c;具有高精度和高可靠性&#xff0c;完全满足企业对自动化测试的需…

vue3中谷歌地图+外网申请-原生-实现地址输入搜索+点击地图获取地址回显 +获取国外的geoJson实现省市区级联选择

一. 效果&#xff1a;输入后显示相关的地址列表&#xff0c;选中后出现标示图标和居中定位 1.初始化谷歌地图 在index.html加上谷歌api请求库 <script src"https://maps.googleapis.com/maps/api/js?key申请到的谷歌地图密钥&vweekly&librariesgeometry,place…

指针!!C语言(第一篇)

指针1 指针变量和地址1.取地址操作符(&)2.指针变量和解引用操作符(*) 指针变量的大小和类型指针的运算特殊指针1.viod*指针2.const修饰指针3.野指针 assert断言指针的使用和传址调用1.strlen的模拟实现2.传值调用和传址调用 指针变量和地址 在认识指针之前&#xff0c;我们…

鸿蒙实训笔记

第一天 #初始化一个新的NPM项目(根据提示操作) npm init #安装TSC、TSLint和NodeJS的类型声明 npm install -s typescript tslint types/node 在根目录中新建一个名为tsconfig.json的文件&#xff0c;然后在代码编辑器中打开&#xff0c;写入下述内容&#xff1a; {"co…