【玩转408数据结构】线性表——线性表的顺序表示(顺序表)

news2025/1/22 15:55:07

知识回顾

        通过前文,我们了解到线性表是具有相同数据类型的有限个数据元素序列;并且,线性表只是一种逻辑结构,其不同存储形式所展现出的也略有不同,那么今天我们来了解一下线性表的顺序存储——顺序表。

顺序表的定义

        顺序表指的是将逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。所以顺序表的特点就是其逻辑顺序与其物理顺序相同。

        我们不妨将设线性表L存储的起始位置为LOC(A),那么其顺序表L相对应的顺序存储如图所示:(这里sizeof是计算括号内数据元素所占用存储空间的大小)

        通过图我们也不难观察出其顺序表的特点。这里每个数据元素的存储位置都与线性表的起始位置相差该数据元素的位序个(n个)数据元素内存大小。所以我们的顺序存储结构是随机存取的存储结构。在接下来高级程序设计语言的实现中,我们决定使用数组来实现该内容(不过需要注意的是,线性表中元素的位序是从1开始的而数组中的元素下标是从0开始的)。

元素类型初始化

静态分配 

        既然我们了解了顺序表,那么接下来我们就要尝试着去实现顺序表。首先,我们需要思考的是 我们应该怎么定义出顺序表中每个元素的类型呢?这并不是一个困难的问题,由于顺序表的特点,我们这里可以使用一个数组去存放顺序表中元素;不过仅仅使用数组是不行的,因为我们很难去判断我们顺序表存储了多少个元素(顺序表的长度);那么这时,我们就需要一个附加的值(length)去记录我们顺序表的当前长度,由于我们需要两个值同时存在,这里就需要用到我们之前C语言学习时的一个关键字(struct)了。通过我们的思考,我们就可以尝试写出顺序表中的顺序存储类型了。

#define MaxSize 50 


typedef struct {
	int data[MaxSize];	//	定义元素 
	int length;		//表示当前长度 
}SqList;

        于是我们不难写出上述的代码(需要注意的是,此时data[]为int类型,这里的int可以根据我们存储元素的类型去进行更改)这里我们使用数组去存储顺序表中的元素,使用length去记录当前的长度。

        可以,使用该方法(静态分配)去分配时会出现一种问题,由于我们数组的大小和空间是固定的,我们在分配数组时,若数组的空间开的过大会导致其内存的浪费;若空间开的过小,又有可能导致空间占满,进而导致存入新数据时产生溢出、程序崩溃;这也就是我们进行静态分配的缺点。

 

思考:第三步的Length设为0,可不可以省略?  这当然是不可以的,如果我们没有对Length的值进行初始化,那么这个值在分配的时候将是随机的,这样就会导致长度计算的错误;当然写过一些代码的小伙伴可能会疑惑,我们平时也是没有初始化,他的值一直是0呀,这里主要是由于编译器的原因,我们使用的编译器自动的将其设为0了,但在考试中为了严谨性,还是建议将Length值进行初始化的。

        既然静态分配有那么多缺点,那么我们能不能使用一个更好些的办法,去尽可能的避免这些问题呢?答案当然是可以的,这里我们可以采用动态分配。 

 

动态分配

         在动态分配中,存储数组的空间实在程序执行的过程中通过动态存储分配语句分配的,一旦该数组的空间占满,就另外开辟出一块更大的存储空间,用来替换掉之前的存储空间,这样可以有效的解决上面的问题。

#define InitSize 50     //顺序表初始长度


typedef struct {
	int *data;	//	指向动态分配数组的指针 
	int MaxSize,length;		//分别表示最大容量和当前长度
}SqList;

        在进行动态的申请和释放空间时,我们可以利用下面这些关键字:

C —— malloc、free 函数

        L.data = (ElemType *) malloc (sizeof(ElemType) *InitSize) ;

C++ —— new、delete 函数

        L.data = new ElemType (InitSize) ;

 

顺序表特点:

  1. 随机存储,我们可以通过首地址和元素序号在时间复杂度为O(1)内找到指定的元素。
  2. 其存储密度高,每个节点只需存储数据元素。
  3. 顺序表逻辑上相邻的元素物理上也相邻,所以插入和删除就需要移动大量的元素。

顺序表的基本操作实现

顺序表的插入

顺序表的插入操作:

        ListInsert(&L,i,e):插入操作。在表L中的第i个位置上插入指定元素e。

         我们的实现思路主要就是,首先,判断输入的第i个位置是否合法;若不合法则插入失败,若合法则将第i个元素及其后面的元素依次向后移动一个位置,然后腾出一个空位置插入新元素e,顺序表的长度增加1,及插入成功。 

//插入 
bool ListInsert(SqList &L, int i, int e){	//传入顺序表 以及从第i个位置插入一个值e 
	if(i<1 || i>L.length+1) //注:这里的i是表中的第几个元素,并非其数组下标
		return false ;
	
	if(L.length >= MaxSize)	//表满 无法插入
		return false ;
		
	 //后移
	 for(int j=L.length; j>=i; j--){	//此时j表示的是位数  
	 	L.data[j] = L.data[j-1];
	 } 
	 /*
	 for(int j=L.length-1; j>=i-1; j--){	j表示的为数组下标 
	 	L.data[j+1] = L.data[j];
	 }
	 */	
	 
	L.data[i-1] = e ;
	L.length++;
	return true ;  
}

思考:为什么代码中if语句中用length+1,而for语句中只用length呢?通过对代码的观察我们不难发现,这里if语句和for语句中的元素代表的含义并不相同,if语句中代表的是顺序表元素的位序而for语句中代表的是数组下标。

时间复杂度分析

        最好情况:直接在表尾插入元素( i=n+1 ),元素直接后移即可,时间复杂度为O(1)。

        最坏情况:在表头插入元素( i=1 ),元素需要后移n次,时间复杂度为O(n)。

        平均情况:假设p_{i}为在第 i 个位置上插入一个结点的概率,则在一个长度为n的线性表中插入一个结点时,需要移动节点的平均次数为:

\sum_{i=1}^{n+1}p_{i}(n-i+1) = \sum_{i=1}^{n+1}\frac{1}{n+1}(n-i+1) = \frac{n}{2}

        因此,顺序表插入算法的时间复杂度为O(n)。 

顺序表的删除

 顺序表的删除操作:

        ListDelete(&L,i,&e):删除操作。删除表L中第i个位置的元素,并用e返回删除元素的值。

        删除元素我们主要的实现思路就是,我们在删除第i个位置之后,需要将其后面的位置全部向前移动一位,这样就可以完成删除操作了。 

//删除
bool ListDelete(SqList &L, int i, int &e){
	if(i<1 || i>L.length+1)
		return false ;
	
	e = L.data[i-1] ;	//第i个元素 在数组的i-1
	
	for(int j=i; j<L.length; j++){
		L.data[j-1] = L.data[j] ;
	} 
	
	L.length = L.length-1 ;
	return true ;
	
} 
时间复杂度分析

        最好情况:直接在表尾删除元素( i=n+1 ),元素删除即可,时间复杂度为O(1)。

        最坏情况:在表头删除元素( i=1 ),元素需要前移n次,时间复杂度为O(n)。

        平均情况:假设p_{i}为在第 i 个位置上删除一个结点的概率,则在一个长度为n的线性表中删除一个结点时,需要移动节点的平均次数为:

\sum_{i=1}^{n}p_{i}(n-i) = \sum_{i=1}^{n}\frac{1}{n}(n-i) = \frac{n-1}{2}

        因此,顺序表插入算法的时间复杂度为O(n)。 

        

        由此可见,插入操作删除操作的时间主要消耗在移动元素上,而移动元素的个数与我们插入或者删除元素的位置有关,不同的插入删除位置所移动的元素个数是不同的。

顺序表的查找

按位查找

        GetElem(L,i):按位查找操作。获取表L中第i个位置的元素的值。

         对于按位查找,由于我们的数组下标可以很好的表示出元素的顺序,这里我们就可以直接利用数组下标与元素位序的映射关系去完成返回第i个元素的值操作。

//查找第i个位置的元素值
int GetElem(SqList L, int i) {
	return L.data[i-1];	//数组下标从0开始 
} 
时间复杂度分析

        由于是直接返回数组值的,所以不需要什么中间的计算,其时间复杂度是稳定的 O(1) 。

按值查找

        LocateElem(L,e):按值查找操作。在表L中查找具有给定关键字值的元素。 

        对于按值查找,我们可以使用循坏,去遍历一遍我们的顺序表,这样就可以找到需要返回的值了;如果遍历一遍之后仍没有发现需要查找的值,那么就返回false,证明查找失败。

//查找
//查找第一个是e的元素 返回其位序 
int LocateElem(SqList &L, int e){
	
	for(int i=0; i<L.length; i++){	//i为数组下标 
		if(L.data[i] == e)
			return i+1 ;
	}
	
	return 0; 
}
时间复杂度分析

        最好情况:查找的元素在表头,只需要查找一次即可,时间复杂度为O(1)。

        最坏情况:查找的元素不存在或者在表尾,需要查找n次,时间复杂度为O(n)。

        平均情况:假设p_{i}为查找元素在第 i 个位置上结点的概率,则在一个长度为n的线性表中查找一个结点时,需要比较节点的平均次数为:

\sum_{i=1}^{n}p_{i}i = \sum_{i=1}^{n}\frac{1}{n}i = \frac{n+1}{2}

        因此,顺序表按值查找算法的时间复杂度为O(n)。 

        

        到这里,顺序表的功能也基本完成了,当然对于这些操作,我们动态分配和静态分配的操作代码相差并不大,只是动态分配时需要多出一个增加数组长度的函数,这里在下面的完整代码展示中会体现出来,本文就不做过多描述。

顺序表完整代码

静态分配代码 

//2.2 顺序表 
#include<bits/stdc++.h>
#define MaxSize 50 

using namespace std;

typedef struct {
	int data[MaxSize];	//	定义元素 
	int length;		//表示当前长度 
}SqList;

int ex = -1 ;

//插入 
bool ListInsert(SqList &L, int i, int e){	//传入顺序表 以及从第i个位置插入一个值e 
	if(i<1 || i>L.length+1) //注:这里的i是表中的第几个元素,并非其数组下标
		return false ;
	
	if(L.length >= MaxSize)	//表满 无法插入
		return false ;
		
	 //后移
	 for(int j=L.length; j>=i; j--){	//此时j表示的是位数  
	 	L.data[j] = L.data[j-1];
	 } 
	 /*
	 for(int j=L.length-1; j>=i-1; j--){	j表示的为数组下标 
	 	L.data[j+1] = L.data[j];
	 }
	 */	
	 
	L.data[i-1] = e ;
	L.length++;
	return true ;  
}

//删除
bool ListDelete(SqList &L, int i, int &e){
	if(i<1 || i>L.length+1)
		return false ;
	
	e = L.data[i-1] ;	//第i个元素 在数组的i-1
	
	for(int j=i; j<L.length; j++){	
		L.data[j-1] = L.data[j] ;
	} 
	
	L.length = L.length-1 ;
	return true ;
	
} 

//查找
//查找第一个是e的元素 返回其位序 
int LocateElem(SqList &L, int e){
	
	for(int i=0; i<L.length; i++){	//i为数组下标 
		if(L.data[i] == e)
			return i+1 ;
	}
	
	return 0; 
}

//查找第i个位置的元素值
int GetElem(SqList L, int i) {
	return L.data[i-1];	//数组下标从0开始 
} 

int main(){
	
	SqList L ;
	for(int i=0; i<=5; i++){
		L.data[i] = i+1 ;
	}
	
	L.length = 6 ;
	
	for(int i=0; i<=L.length-1; i++)	cout << L.data[i] << "  " ;
	cout << endl;
	
	ListInsert(L, 3, 3) ;
	for(int i=0; i<=L.length-1; i++)	cout << L.data[i] << "  " ;
	cout << endl;
	
	ListDelete(L, 3, ex) ;
	cout << ex << endl ;
	for(int i=0; i<=L.length-1; i++)	cout << L.data[i] << "  " ;
	cout << endl;
	
	cout << GetElem(L, 3) << endl ; 
	cout << LocateElem(L, 3) << endl;
	
	return 0;
}

 

动态分配代码

//2.2 顺序表 
#include<bits/stdc++.h>
#define InitSize 50     //顺序表初始长度

using namespace std;

typedef struct {
	int *data;	//	指向动态分配数组的指针 
	int MaxSize,length;		//分别表示最大容量和当前长度
}SqList;

int ex = -1 ;

//初始化
void InitList(SqList &L) {
	L.data = (int *)malloc(sizeof(int));
	L.length = 0;
	L.MaxSize = InitSize;
} 

//动态增长数组
void IncreaseSize(SqList &L, int len) {	//len为需要增加长度 
	int *p = L.data;	//p记录之前数组地址 方便后期释放 
	L.data = (int *)malloc(sizeof(int) * (L.MaxSize+len)) ;	//申请一片新的区域 
	
	for(int i=0; i<L.length; i++) {
		L.data[i] = p[i];	//将值复制到新的区域 
	}
	
	L.MaxSize = L.MaxSize+len;	//更新最大的容量 
	free(p);	//释放之前动态申请的空间 
} 

//插入 
bool ListInsert(SqList &L, int i, int e){	//传入顺序表 以及从第i个位置插入一个值e 
	if(i<1 || i>L.length+1) //注:这里的i是表中的第几个元素,并非其数组下标
		return false ;
	
	if(L.length >= L.MaxSize)	//表满 无法插入
		return false ;
		
	 //后移
	 for(int j=L.length; j>=i; j--){	//此时j表示的是位数  
	 	L.data[j] = L.data[j-1];
	 } 
	 /*
	 for(int j=L.length-1; j>=i-1; j--){	j表示的为数组下标 
	 	L.data[j+1] = L.data[j];
	 }
	 */	
	 
	L.data[i-1] = e ;
	L.length++;
	return true ;  
}

//删除
bool ListDelete(SqList &L, int i, int &e){
	if(i<1 || i>L.length+1)
		return false ;
	
	e = L.data[i-1] ;	//第i个元素 在数组的i-1
	
	for(int j=i; j<L.length; j++){	
		L.data[j-1] = L.data[j] ;
	} 
	
	L.length = L.length-1 ;
	return true ;
	
} 

//查找
//查找第一个是e的元素 返回其位序 
int LocateElem(SqList &L, int e){
	
	for(int i=0; i<L.length; i++){	//i为数组下标 
		if(L.data[i] == e)
			return i+1 ;
	}
	
	return 0; 
}

//查找第i个位置的元素值
int GetElem(SqList L, int i) {
	return L.data[i-1];	//数组下标从0开始 
} 

int main(){
	
	SqList L ;
	InitList(L) ;
	IncreaseSize(L, 10); 
	
	for(int i=0; i<=5; i++){
		L.data[i] = i+1 ;
	}
	
	L.length = 6 ;
	
	for(int i=0; i<=L.length-1; i++)	cout << L.data[i] << "  " ;
	cout << endl;
	
	ListInsert(L, 3, 3) ;
	for(int i=0; i<=L.length-1; i++)	cout << L.data[i] << "  " ;
	cout << endl;
	
	ListDelete(L, 3, ex) ;
	cout << ex << endl ;
	for(int i=0; i<=L.length-1; i++)	cout << L.data[i] << "  " ;
	cout << endl;
	
	cout << GetElem(L, 3) << endl ; 
	cout << LocateElem(L, 3) << endl;
	
	return 0;
}

        两个完整代码的内容大同小异,主要就是在顺序表定义初始化时会产生些许不同,我们主要理解其产生逻辑即可,其代码的运行结果图如下:

         由代码可知,我们对其顺序表初始化为(1,2,3,4,5,6)就是我们第一行所展示的数字;之后我们在第三个位置插入3,所以第二行展示的就是插入后的结果;第三行则是输出我们在删除时需要删除的位置,紧接着我们将第三个位置的数字删除,所以第四行显示的是其删除后的结果;最后两行就是输出的为第三个位置和查找值为3的元素在第几个位置并输出。

        顺序表的内容到这里也就结束了,我们在下面尽量可以独立的去实现一下代码,这样可以更好的帮助我们理清其内部的逻辑。

 

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

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

相关文章

2024春晚纸牌魔术原理----环形链表的约瑟夫问题

一.题目及剖析 https://www.nowcoder.com/practice/41c399fdb6004b31a6cbb047c641ed8a?tabnote 这道题涉及到数学原理,有一般公式,但我们先不用公式,看看如何用链表模拟出这一过程 二.思路引入 思路很简单,就试创建一个单向循环链表,然后模拟报数,删去对应的节点 三.代码引…

Stable Diffusion 模型下载:DreamShaper XL(梦想塑造者 XL)

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八案例九案例十 下载地址 模型介绍 DreamShaper 是一个分格多样的大模型&#xff0c;可以生成写实、原画、2.5D 等…

前端工程化面试题 | 07.精选前端工程化高频面试题

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

springboot183基于java的公寓报修管理系统

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

AES加密后的密码可以破解吗

AES&#xff08;高级加密标准&#xff09;是一种广泛使用的对称加密算法&#xff0c;设计用来抵御各种已知的攻击方法。AES使用固定块大小的加密块和密钥长度&#xff0c;通常是128、192或256位。它被认为是非常安全的&#xff0c;到目前为止&#xff0c;没有已知的可行方法能够…

视觉开发板—K210自学笔记(六)

视觉开发板—K210 本期我们继续来遵循其他控制器的学习路线&#xff0c;在学习完GPIO的基本操作后&#xff0c;我们来学一个非常重要的UART串口通信。为什么说这个重要呢&#xff0c;通常来说我们在做一个稍微复杂的项目的时候K210作为主控的核心可能还有所欠缺&#xff0c;另…

基于微信小程序的校园失物招领小程序

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

统一数据格式返回,统一异常处理

目录 1.统一数据格式返回 2.统一异常处理 3.接口返回String类型问题 1.统一数据格式返回 添加ControllerAdvice注解实现ResponseBodyAdvice接口重写supports方法&#xff0c;beforeBodyWrite方法 /*** 统一数据格式返回的保底类 对于一些非对象的数据的再统一 即非对象的封…

【开源】SpringBoot框架开发数字化社区网格管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、开发背景四、系统展示五、核心源码5.1 查询企事业单位5.2 查询流动人口5.3 查询精准扶贫5.4 查询案件5.5 查询人口 六、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的数字化社区网格管理系统&#xf…

【Linux进阶之路】网络——“?“(上)

文章目录 一、历史发展1. 独立形态2. 互联形态3. 局域网 二、网络协议1.OSI七层协议2.TCP/IP四&#xff08;五&#xff09;层模型 三、网络通信1.封装与解包2.数据的传输1.局域网2.广域网 总结尾序 本篇文章的目的是带大家初步认识网络&#xff0c;为后面的网络编程打下基础&am…

three.js 细一万倍教程 从入门到精通(二)

目录 三、全面认识three.js物体 3.1、掌握几何体顶点_UV_法向属性 3.2、BufferGeometry设置顶点创建矩形 3.3、生成酷炫三角形科技物体 四、详解材质与纹理 4.1、初识材质与纹理 4.2、详解纹理偏移_旋转_重复 偏移 旋转 重复 4.3、设置纹理显示算法与mipmap mapFil…

第二篇【传奇开心果微博系列】Python微项目技术点案例示例:成语接龙游戏

传奇开心果微博系列 系列微博目录Python微项目技术点案例示例系列 微博目录一、微项目目标二、雏形示例代码三、扩展整体思路四、玩家输入示例代码五、成语判断示例代码六、回答判断示例代码七、电脑判断示例代码八、游戏结束示例代码九、界面优化示例代码十、扩展成语库示例代…

给你介绍一款适合教培行业的手机软件,很好用,关键还是免费的

给你介绍一款适合教培行业的手机软件&#xff0c;很好用&#xff0c;关键还是免费的&#xff0c;DT浏览器不同于普通意义上的浏览器&#xff0c;DT的含义就是数据资料的意思&#xff0c;更专注于资料的收集和管理&#xff0c;是一款资料管理类的浏览器&#xff0c;也是一款面向…

【好玩AI】【Prompt】情人节了,用GPT写个【骂醒恋爱脑】的机器人跟自己对话吧

情人节了&#xff0c;让我们用GPT写个【骂醒恋爱脑】的机器人跟自己对话吧。 通过本文&#xff0c;你能学到&#xff1a; 1. 如何零代码搭建一个自己的机器人Bot 2. 骂醒恋爱脑的高级Prompt 通过本文&#xff0c;你还能得到&#xff1a; 恋爱脑可能被骂醒 为了白嫖&#xff0c;…

从计算机恢复已删除文件的 6 种方法!

如果您的重要文件之一已从计算机中删除怎么办&#xff1f;如果不小心从硬盘中删除了文件怎么办&#xff1f; 如今的公司通常将重要数据存储在云或硬盘中。但最重要的是&#xff0c;您必须考虑这样一个事实&#xff1a;您可能会丢失计算机上的数据。数据丢失的原因有多种&#x…

校招扫盲篇:写一份让HR过目不忘的简历

嵌入式简历怎么写 一、简历构成二、各部分写法的主要事项1.个人的基本信息2.教育背景3.专业技能4.实习经历5.项目经历6.荣誉经历7.校园经历8.自我评价 Last but not least 一、简历构成 首先我们要清楚写一份简历有哪些部分组成。按内容来分的话可以分为个人信息、教育背景、专…

分享83个jQuery特效,总有一款适合您

分享83个jQuery特效&#xff0c;总有一款适合您 83个jQuery特效下载链接&#xff1a;https://pan.baidu.com/s/15rS5EqE2JZPktI_yZGkzxQ?pwd8888 提取码&#xff1a;8888 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理…

STM32 cubemx配置DMA+空闲中断接收不定长数据

文章目录 前言一、串口空闲中断二、DMA空闲中断接收不定长数据实现思路三、STM32Cubemx配置DMA空闲中断接收不定长数据四、代码编写总结 前言 本篇文章给大家讲解一下DMA串口空闲中断接收串口不定长数据&#xff0c;之前我们也是讲解过串口接收不定长数据的&#xff0c;那么本…

【Linux】进程概念(冯诺依曼体系结构、操作系统、进程)-- 详解

一、冯诺依曼体系结构 1、概念 &#xff08;1&#xff09;什么是冯诺伊曼体系结构&#xff1f; 数学家冯诺伊曼于 1946 年提出存储程序原理&#xff0c;把程序本身当作数据来对待&#xff0c;程序和该程序处理的数据用同样的方式储存。 冯诺伊曼理论的要点是&#xff1a;计算…

第六篇:MySQL图形化管理工具

经过前五篇的学习&#xff0c;对于数据库这门技术的理解&#xff0c;我们已经在心中建立了一个城堡大致的雏形&#xff0c;通过命令行窗口&#xff08;cmd&#xff09;快速上手了【SQL语法-DDL-数据定义语言】等相关命令 道阻且长&#xff0c;数据库技术这一宝藏中还有数不清的…