【数据结构】双链表的相关操作(声明结构体成员、初始化、判空、增、删、查)

双链表

  • 双链表的特点
  • 声明双链表的结构体成员
  • 双链表的初始化
    • 带头结点的双链表初始化
    • 不带头结点的双链表初始化
    • 调用双链表的初始化
  • 双链表的判空
    • 带头结点的双链表判空
    • 不带头结点的双链表判空
  • 双链表的插入(按值插入)
  • 头插法建立双链表
    • 带头结点的头插法
      • 每次调用头插法只能插入一个结点
      • 每次调用头插法可插入任意个结点
      • 调用头插法建立带头结点的双链表的完整代码(可以直接赋值运行的)
    • 不带头结点的头插法
      • 每次调用只能插入一个结点
      • 每次调用可以插入任意个结点
      • 调用头插法建立不带头结点的双链表的完整代码(可以直接赋值运行的)
  • 尾插法建立双链表
    • 带头结点的尾插法
      • 每次调用只能插入一个结点
        • 我自己能理解的实现尾插法的思路
        • 我的思路的完整代码,可以运行的
      • 教材用的实现尾插法的代码(我看不懂)
    • 不带头结点的尾插法
      • 我自己的思路
  • 打印双链表
    • 二级目录
      • 三级目录

双链表的特点

1. 双链表在创建的过程中,每个结点都需要一个数据域和两个指针域,所以它的存储密度比之单链表更低一些。
2. 可进可退,解决了单链表无法逆向搜索的问题。

声明双链表的结构体成员

typedef struct DNode {ElemType data;struct DNode *prior, *next;
}DNode, *DLinklist;// DNode *就等价于DLinklist			之前文章有解释过

DNode *就等价于DLinklist

双链表的初始化

双链表的每个结点都有两个指针域,一个指向前驱结点,一个指向后继结点。头结点的prior永远指向NULL

带头结点的双链表初始化

有几个需要注意的地方:1. DLinklist&L是头指针,而不是头结点,这是两东西。2. 头结点不存储数据,只起到一个标记作用,方便对链表进行操作。3. 头指针指向头结点,用于记录链表的起始位置。4. 给&L分配内存空间,是因为这是带头结点的双链表,而头指针指向头结点,所以实际上是给头结点分配内存空间,创建头结点。5. 不带头结点的双链表,不用创建头结点,自然也不用分配内存空间,直接创建第一个结点即可。6. 注意:原先我一直疑惑的地方是,为啥带头结点就要分配内存空间,不带头结点的双链表哪怕不需要头结点,也要给第一个结点分配内存空间,但是初始化代码的时候并没有写,后来我发现,不是没写,而是不应该在初始化链表的时候给链表的第一个实际结点分配内存空间,此时是为了初始化一个空链表,后续给链表插入结点的时候,才会开始分配内存空间。而此时分配的,仅仅只是头结点的内存空间,并不是第一个实际结点的,带就分配,不带就不分配,就这么个意思。
bool initDList(DLinklist&L) {L=(DNode *)malloc(sizeof(DNode));/*** L本身是头指针,带头结点时,它指向头结点的指针域,所以可以把L看作是头结点,指针域为NULL,说明这是个空指针。* 注意:空指针本身不占用内存空间。* 指针变量在内存中占用一定的空间,但当指针被赋值为NULL时,它指向的内存地址为空,不指向任何有效的内存空间。* 因此,空指针并不占用额外的内存空间*/if(L==NULL) {		// 空指针不占用内存空间,可用此来判断内存不足,给头结点分配存储空间失败return false;}// 头结点的prior永远指向NULL// 头指针L指向头结点,存储头结点的地址;L->prior指向头结点的前指针域,存储前指针域的地址,为NULL。L->prior=NULL;		// 头结点之后还没有结点,初始化链表这句必须写,因为要创建个空表,以防被内存中残留数据污染		L->next=NULL;		//  头指针L指向头结点,存储头结点的地址;L->next指向头结点的后指针域,存储后指针域的地址,为NULL。		return true;
}

不带头结点的双链表初始化

void initDList(DLinklist&L) {// 由于不带头结点,所以头指针L直接指向双链表的第一个实际结点,此时链表为空,所以指向NULL,可看作是个空指针,空指针的前、后指针不指向任何有效的内存空间,自然不用再区分next、priorL=NULL;
}

调用双链表的初始化

int main() {DLinklistL;// 初始化双链表initDList(L);
}

双链表的判空

带头结点的双链表判空

1. 带头结点的双链表,由于头结点的前指针永远指向NULL,所以不用管。
2. 主要是头结点的后指针,看它后面有没有结点,没结点时它应该指向NULL。

在这里插入图片描述

bool Empty(DLinklist L) {if(L->next==NULL) {return true;}else {return false;}// 这个if判断可以简写成:return L->next==NULL;
}

不带头结点的双链表判空

bool Empty(DLinklist L) {return L==NULL;
}

双链表的插入(按值插入)

1. 这是按值插入,不是按位插入,只要在找到链表中某个已存在的结点,在其后面分配存储空间,创建一个新结点,改变前、后指针指向,以及
新结点的数据域,就可以实现插入了,这算是后插。
2. 此处只是说明了插入的逻辑,代码并不完整,也还没有分带不带头结点
1. DNode *p表示声明一个指针变量p,它指向下一结点的指针域。
2. p=NULL表示p指针所在的结点的指针域为NULL,它不指向任何有效的地址,说明p是个空指针。- 为什么要判断指针p和指针s是否是空指针?1. 因为该函数是在找到p结点之后,通过改变p结点原先的指向,来实现插入的。2. 如果p是个空指针,它的后指针域没有任何指向,那我在它后面插入一个新结点,新结点的后指针域就没有赋值指向了。3. 并且,一般链表的最后一个结点的后面是NULL,肯定不能在NULL后面再插入新结点,要插也最后是在最后一个实际结点后面插4. 参数s表示指针s,它所指向的结点是我要插入的新结点,一个新结点,都要插入一个新结点了,肯定不能是个空指针啊,如果是个空指针,那这个链接岂不是断链了。
3. p->next=NULL:p->next表示指针p所在的结点的指针域,它存储下一结点的地址,指向下一结点的指针域。p-next=NULL就表示指针p指向
的结点的下一结点是NULL。
4. 把指针变量p指向的结点看作是p结点,那它的下一结点就是紧跟其后的一个结点,即p结点—NULL。
5. 首先明确,p是用DNode *去声明的,所以p一定是个指针,那一个指针,它会指向一个结点,就可以把p看作是p结点,p=NULL就是说p结点本
身是NULL。
6. 其次,p->next就可以看作是p结点的下一结点是NULL
bool InsertNextNode(DNode *p, DNode *s) {if(p==NULL || s==NULL) {return false;}/*** s指针所指向的结点(简称s结点)的指针域指向p指针所指向的结点(简称p结点)的指针域指向的结点,即:s结点指向p结点的下一个结点,* 此时顺序是p—p下一结点,s—p下一结点*/s->next=p->next;		/***  由于是双链表,每个结点都有前指针和后指针,p结点如果是最后一个结点,说明它后面没有结点了,指向NULL。* 它的后指针指向NULL,这没问题,这样子的双链表才是完整的。* 但是NULL没有前、后指针啊,所以在操作结点的前指针指向的时候,要加个判断,当前结点下一结点不是NULL才能操作其前指针*/if(p->next!=NULL) {/*** p->next表示p结点的下一结点(简称q结点),* p->next->prior表示q结点的前指针域指向的结点,* 赋值s就表示q结点的指针域指向指针s所指向的结点,即s结点。* 此时顺序为:p—q,s—q*/p->next->prior=s;		}// 现在只要把p和s再连上就好了p->next=s;s->prior=p;return true;
}

头插法建立双链表

需求:把一个数组中的数据,插入到初始的双链表中,如果当前双链表带头结点,那么每次都是插在头结点的后面,这样每次插入的新结点都是
链表的第一个结点。
如果当前双链表不带头结点,那么每次插入都应该让头指针L直接指向当前新插入的这个结点。
这便是头插法。
带头:
1. 带头结点的初始双链表:头指针L—头结点—NULL
2. 插入1个需要的参数:要建立的链表&L,要插入的元素e
3. 插入n个需要的参数:要建立的链表&L,要插入的多个元素e[],要插入的结点个数n
不带头:头指针L—NULL
4. 带头结点的初始双链表:头指针L—NULL
5. 需要的参数:要建立的链表&L,要插入的元素e

带头结点的头插法

每次调用头插法只能插入一个结点

DLinklist head_insert(DLinklist &L, int e) {// 创建新结点,分配内存空间DLinklist *s = (DLinklist *)malloc(sizeof(DLinklist));/*** 头指针L指向头结点,L->next指向头结点的指针域,头结点的指针域指向头结点的下一结点,* 即L可看作是头结点,L->next就是头结点的下一结点。* 1、头结点指向创建的新结点s,即在头结点之后插入新结点,* 2、此时头结点原指向的结点就要变成s结点的下一结点* 3、此时后指针的指向已全部完成,双链表还要再操作一下前指针的指向:* 		- s结点的前指针指向头结点,头结点原下一结点的前指针指向s结点* 4、但是要注意一个问题,如果是第一次插入,双链表此时是空的,头结点指向NULL,插入的新结点s是链表中最后一个结点,* 只要操作其后指针即可,而其下一结点的无前指针无需操作,所以为了代码的健壮性要加个判断*/s->data=e;s->next=L->next;if(L->next!=NULL) {L->next->prior=s;}L->next=s;s->prior=L;	
}

每次调用头插法可插入任意个结点

1. 首先要给分配内存空间,有多少个n,就分配多少个。
2. 由于插入的n个,每个的前指针和后指针还要链接起来,所以最好当作1个来操作,然后通过循环的方式,变成n个。
3. i是数组的下标,从0开始表示数组中第一个元素,假设n=3那就是插入3个结点,必须从i=0开始循环,那就要i<n或者i<=n-1才能正好取3个。
DLinklist head_insert_mult(DLinklist &L, int e[], int n) {for(int i=0;i<n;i++) {		DNode *s=(DNode *)malloc(sizeof(DNode));s->next=L->next;s->data=e[i];if(L->next!=NULL) {L->next->prior=s;}s->prior=L;L->next=s;}return L
}

调用头插法建立带头结点的双链表的完整代码(可以直接赋值运行的)

在这里插入图片描述

#include<stdio.h> 
#include<stdlib.h>// 声明结构体成员
typedef struct DNode {ElemType data;struct DNode *prior, *next;
}DNode, *DLinklist;// 初始化一个带头结点的双链表 
DLinklist InitList(DLinklist &L) {L=(DNode *)malloc(sizeof(DNode));if(L==NULL) {return NULL;}L->next=NULL; L->prior=NULL;return L;
}// 头插法建立双链表(单个)
DLinklist head_insert(DLinklist &L,int e) {DNode *s=(DNode *)malloc(sizeof(DNode));s->data=e;s->next=L->next;if(L->next!=NULL)L->next->prior=s;L->next=s;s->prior=L;return L;
}// 头插法建立双链表(多个) 
DLinklist head_insert_mult(DLinklist &L, int e[], int n) {for(int i=0;i<=n-1;i++) {DNode *s=(DNode *)malloc(sizeof(DNode));s->next=L->next;s->data=e[i];if(L->next!=NULL) {L->next->prior=s;}s->prior=L;L->next=s;}return L;
}// 打印双链表 
void printDLinkList(DLinklist L)
{DNode* p = L;while(p){printf("%d ", p->data);p = p->next;}
}int main() {DLinklist L;InitList(L);head_insert(L,2);	head_insert(L,3);int arr[5]={1,2,3,4,5};head_insert_mult(L,arr,3); // 顺序为 头结点 3 2 1 3 2 printDLinkList(L); return 0;
}

不带头结点的头插法

由于没有头结点了,所以操作头指针L直接指向双链表第一个实际结点

每次调用只能插入一个结点

DLinklist head_insert_NoT(DLinklist &L, int e) {DNode *s=(DNode *)malloc(sizeof(DNode));s->next=L;s->data=e;if(L!=NULL) {L->prior=s;}L=s;	s->prior=L;return L;
}

每次调用可以插入任意个结点

DLinklist head_insert_NoT_mult(DLinklist &TL, int e[], int n) {for(int i=0;i<n;i++) {DNode *s=(DNode *)malloc(sizeof(DNode));s->data=e[i];		// 此时双链表只有一个NULL,由TL指向s->next=TL;			// s指针所在的结点(简称s结点)的指针域为头指针TL的指向,也就是说s结点下一个是NULL:s—NULLif(TL!=NULL) {		TL->prior=s;	// 由于是头茬,所以每次TL原先指向的那个结点的前指针都要指向新插入的s}TL=s;		// TL是头指针,每次都要指向新插入的s指针所指向的结点s->prior=NULL;		// 由于s指针是以头插的形式新插入的,它就是第一个结点,前面没有任何东西,前指针指向NULL}return TL;}

调用头插法建立不带头结点的双链表的完整代码(可以直接赋值运行的)

在这里插入图片描述

#include<stdio.h> 
#include<stdlib.h>// 声明结构体成员
typedef struct DNode {ElemType data;struct DNode *prior, *next;
}DNode, *DLinklist;// 初始化一个带头结点的双链表 
DLinklist InitList(DLinklist &L) {L=NULL;return L;
}DLinklist head_insert_NoT(DLinklist &L, int e) {DNode *s=(DNode *)malloc(sizeof(DNode));s->next=L;s->data=e;if(L!=NULL) {L->prior=s;}L=s;	s->prior=L;return L;
}DLinklist head_insert_NoT_mult(DLinklist &TL, int e[], int n) {for(int i=0;i<n;i++) {DNode *s=(DNode *)malloc(sizeof(DNode));s->data=e[i];		s->next=TL;		if(TL!=NULL) {		TL->prior=s;	}TL=s;		s->prior=NULL;		}return TL;}// 打印双链表 
void printDLinkList(DLinklist L)
{DNode* p = L;while(p){printf("%d ", p->data);p = p->next;}
}int main() {DLinklist L;InitList_NoT(L);head_insert_NoT(L,2); head_insert_NoT(L,21); head_insert_NoT(L,22);int arr1[5]={2,3,4,7,8};head_insert_NoT_mult(L,arr1,5);printDLinkList(L); return 0;
}

尾插法建立双链表

尾插就是:每次都在双链表的末尾再插入一个结点,新结点后面不会再有其他结点,应该是指向NULL的。带头:
1. 插入一个需要的参数:双链表L、要插入的元素e
不带头:
2、

带头结点的尾插法

每次调用只能插入一个结点

我自己能理解的实现尾插法的思路
带头结点:那就是头结点—新结点—NULL
尾插法思路:
1. 要在双链表的最后一个结点后面插入,那肯定要找到最后一个结点。而最后一个结点后面没结点,指向NULL,所以可以根据这个来判断。DNode *tail;		// 这是我声明的指向尾结点指针变量tailif(tail->next==NULL)		// 说明tail指针目前已经是指向尾结点
2. 双链表是个链表,它不具有随机存取的特点,所以要找到尾结点,只能从第一个结点开始按序遍历tail=L;				// tail指针要指向头指针L指向的结点,即头结点,由此开始遍历while(tail->next!=NULL)tail=tail->next;				// 如果tail指针当前指向的结点的下个结点不是NULL,那就指向下一个,循环遍历 // 此处应该是跳出循环的操作了,而跳出循环就说明tail指针已经指向尾结点了
3. 要插入新结点,肯定要创建并分配存储空间,先让新结点指向尾结点的下一结点,再让尾结点指向新结点,然后让新结点的前指针指向尾结点s->next=tail->next;tail->next=s;s->prior=tail;
DLinklist tail_insert(DLinklist &L, int e) {DNode *tail=L;		// 声明一个指针变量tail表示尾指针,赋值L表示初始值指向头结点while(tail->next!=NULL) {tail=tail->next;}DNode *s=(DNode *)malloc(sizeof(DNode));s->next=tail->next;s->data=e;tail->next=s;s->prior=tail;// 因为是尾插法,所以最后s一定指向NULL,tail->next-prior是不存在的不用考虑 
}
我的思路的完整代码,可以运行的

在这里插入图片描述

#include<stdio.h> 
#include<stdlib.h>typedef struct DNode {int data;struct DNode *prior;struct DNode *next;
}DNode,*DLinklist;DLinklist InitList(DLinklist &L) {L=(DNode *)malloc(sizeof(DNode));if(L==NULL) {return NULL;}L->next=NULL; L->prior=NULL;return L;
} DLinklist tail_insert(DLinklist &L, int e) {DNode *tail=L;		while(tail->next!=NULL) {tail=tail->next;}DNode *s=(DNode *)malloc(sizeof(DNode));s->next=tail->next;s->data=e;tail->next=s;s->prior=tail;
}// 打印双链表 
void printDLinkList(DLinklist L)
{DNode* p = L;while(p){printf("%d ", p->data);p = p->next;}
}int main() {DLinklist L;InitList(L);tail_insert(L,2); tail_insert(L,21); tail_insert(L,22);printDLinkList(L); return 0;
}
DLinklist tail_insert(DLinklist &L, int e) {DNode *s=(DNode *)malloc(sizeof(DNode));s-next=L->next;
}

教材用的实现尾插法的代码(我看不懂)

不带头结点的尾插法

我自己的思路

由于此处是双链表不带头结点,那么此时头指针L指向双链表的第一个实际结点,所以当双链表为空时,L=NULL,但如果双链表不为空,L自然也不会是NULL,就要通过遍历去获取双链表的尾结点,所以要做个if判断
DLinklist tail_insert_NoT(DLinkList &L,int e) {DNode *s=(DNode *)malloc(sizeof(DNode));s->data=e;// 这两句没有就无法运行s->next = NULL;s->prior = NULL;/*** 初始化的时候L=NULL,头结点是NULL,所以tail指针指向NULL,* 但是只要插入过结点,再调用该函数进行尾插,头指针L就不再是指向NULL,而是指向一个实际结点,* 此时就要通过遍历去获取该链表的尾结点,过程同上*/DNode *tail;		if(L==NULL) {		// 不带头的双链表为空时,L指向的就是第一个实际结点,插入的新结点s就应该是由L指向的:L=s,第一个结点的前指针用于指向NULLL=s;s->prior=NULL;}else {while(tail->next!=NULL) {tail=tail->next;}s->next=tail->next;tail->next=s;s->prior=tail;}return L;		
}

打印双链表

插入结点之后,想看看当前双链表的结点顺序,可以用以下代码打印输出
void PrintDLinkList(DLinklist L) {	/*** 声明一个指针变量p让它指向头指针L指向的结点,* 如果是带头结点的双链表,就指向头结点;* 如果是不带的,就指向双链表的第一个实际结点。* 此处我写L是为了区分终端打印效果,如果不想要头结点,写成:DNode *p=L->next*/DNode *p=L;while(p!=NULL) {printf("%d", p->data);p=p->next;}
}

二级目录

三级目录

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

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

相关文章

机器视觉在自动驾驶汽车中的应用与挑战

机器视觉在自动驾驶汽车中扮演着至关重要的角色&#xff0c;它使车辆能够感知和理解周围环境&#xff0c;以便自主驾驶。以下是机器视觉在自动驾驶汽车中的应用以及相关挑战&#xff1a; 应用&#xff1a; 障碍物检测与避让&#xff1a; 机器视觉系统可以检测和识别路上的障碍…

2023-10-16 itoa函数的局限以及实现

点击 <C 语言编程核心突破> 快速C语言入门 itoa函数的局限以及实现 前言一、功能描述二、具体实现对于第一版, 实现如下:第二版实现:测试用例: 总结 前言 把一个数用某种进制打印, 是一个很有用的功能, 值得庆幸的是, C语言有这么一个函数itoa(), 它可以把一个数转换为…

前端代码优化之从系统区分处理的业务场景看如何优化代码中的if判断

最近有个三端统一的技术场景&#xff0c;主要是以前移动端的 hybrid 网页在不考虑 UI 适配的情况下、期望能够直接在 PC 客户端投放。在评估修改面的时候发现了一段可以深思的代码&#xff1a; if (platform iphone) {location.href iphoneClientUrl; } else {location.href…

Webpack和JShaman相比有什么不同?

Webpack和JShaman相比有什么不同&#xff1f; Webpack的功能是打包&#xff0c;可以将多个JS文件打包成一个JS文件。 JShaman专门用于对JS代码混淆加密&#xff0c;目的是让JavaScript代码变的不可读、混淆功能逻辑、加密代码中的隐秘数据或字符&#xff0c;是用于代码保护的…

LED显示屏高刷新率和低刷新率有什么区别

LED显示屏的刷新率是指图像在LED显示屏上更新的速度&#xff0c;也即屏幕上的图像每秒钟出现的次数&#xff0c;它的单位是赫兹&#xff08;Hz&#xff09;。LED显示屏的刷新率越高&#xff0c;图像闪烁感就越小&#xff0c;稳定性也就越高&#xff0c;换言之对视力的保护也越好…

图片批处理工具 PhotoMill X直装 for mac

PhotoMill X是一款强大的图像处理软件&#xff0c;它可以帮助用户快速地对照片进行编辑、调整和转换。它支持在单个或批量模式下处理大量的图像文件&#xff0c;并具有直观的用户界面和易于使用的工具。 PhotoMill X具有的功能有&#xff1a; 裁剪、缩放、旋转、调整明暗度、…

python+django学生选课管理系统_wxjjv

1&#xff09;前台&#xff1a;首页、课程信息、校园论坛、校园公告、个人中心、后台管理。 &#xff08;2&#xff09;管理员&#xff1a;首页、个人中心、学生管理、教师管理课、程信息管理、课程分类管理、选课信息管理、作业信息管理、提交作业管理、学生成绩管理、校园论…

国际伦敦银点差费值得吗?

伦敦银是国际轨技术属市场上广受追捧的白银保证金交易品种&#xff0c;具有交易时长、交易制度灵活、资金利用率高等诸多的优点。 国际伦敦银的优势主要来自它所实行的是保证金交易制度。目前香港平台一般执行的保证金比例标准是2%&#xff0c;以目前22美元/盎司左右的白银价格…

epiiAdmin框架注意事项

1&#xff0c;epiiAdmin文档地址&#xff1a; 简介/安装 EpiiAdmin中文文档 看云 2&#xff0c;项目性想新建模块 composer.json文件——autoload选项——psr-4下增加模块名称&#xff0c;然后执行composer update命令。 "autoload": {"psr-4": {"…

代理现货白银有什么手续

成为现货白银代理商的好处有很多&#xff0c;一方面打理依然可以像普通投资者那样&#xff0c;采用平台的交易服务&#xff0c;直接在市场上通过交易&#xff0c;赚取高杠杆所带来的高回报&#xff0c;另一方面还可以根据自己客户的交易量&#xff0c;从平台获得一定的返佣&…

【剑指Offer】28.对称的二叉树

题目 给定一棵二叉树&#xff0c;判断其是否是自身的镜像&#xff08;即&#xff1a;是否对称&#xff09; 例如&#xff1a;下面这棵二叉树是对称的 下面这棵二叉树不对称。 数据范围&#xff1a;节点数满足 0≤n≤1000&#xff0c;节点上的值满足 0∣val∣≤1000 要求&am…

sql注入(5), sqlmap工具

sql注入, sqlmap工具 请注意&#xff0c;在实际操作中使用sqlmap测试和利用SQL注入等安全漏洞应始终符合法律法规和道德准则&#xff0c;并且需要在拥有明确授权的情况下进行。在没有获得适当授权的情况下对任何系统或网络进行渗透测试都是非法的。 sqlmap是由python开发的测…

C++引用(起别名)

0.引用的概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;从语法的角度来说编译器不会为引用变量开辟内存空间&#xff0c;它和它引用的变量共用同一块内存空间。比如说你的名字和外号指的都是你本人。 void Test() {int a 10;int& ra …

【MAC】升级 Mac os 后报错

背景 17 年买的 mac&#xff0c;发现很多软件都无法安装&#xff0c;于是升级 mac os 到 10.13&#xff0c;从官网下载 10.13 版本&#xff0c;之后升级&#xff0c;升级还算顺利。但使用 git 的时候发现出现问题了。 问题 使用 git 出现如下错误 xcrun: error: invalid ac…

第二证券:市净率高好还是低好?

市净率是一个衡量公司股票投资价值的指标&#xff0c;通过比较公司股票价格和公司每股净资产的比值来评估公司股票的估值水平。市净率高好还是低好这个问题并没有一个简单的答案&#xff0c;取决于具体的市场环境和投资者的需求。本文将从多个角度分析市净率高好还是低好。 首…

头文件 <cstdarg> 的使用

进行函数可变参数的实现。 va_list&#xff1a; 适用于 va_start()、va_arg() 和 va_end() 这三个宏存储信息的类型。void va_start(va_list ap, last_arg)&#xff1a; 初始化 ap&#xff0c;其中 last_arg 是最后一个传递给函数的已知固定参数。type va_arg(va_list ap, typ…

How to install mysql 8.0 based on podman

创建配置目录 mkdir -p ~/data/podman/mysql-8.0/etc创建数据存储目录 mkdir -p ~/data/podman/mysql-8.0/var临时启动一个实例 docker run --detach \ --restart always \ --publish 23306:3306 \ --name mysql-8.0 \ --volume /usr/share/zoneinfo/Asia/Shanghai:/etc/lo…

安装运行vue-element-admin的报错问题-解决办法

文章目录 1.第一处2.第二处3.安装运行 在使用vue-element-admin时&#xff0c;使用命令安装&#xff1a; npm install -registryhttps://registry.npm.taobao.org会报错&#xff0c;不通过。需要修改两处。 1.第一处 在目录vue-element-admin-master\src\components\Markdown…

【监督学习】基于合取子句进化算法(CCEA)和析取范式进化算法(DNFEA)解决分类问题(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Docker SpringBoot项目连接本地数据库

要让本地的容器运行的应用程序连接到本机上的PostgreSQL数据库&#xff0c;可以使用以下步骤&#xff1a; 确保本机上已经安装并运行了PostgreSQL数据库。可以使用psql命令行工具或其他可视化工具来管理和连接到本机上的数据库。 在应用程序的配置文件中&#xff0c;将数据库连…