深入理解线性表--顺序表

目录

顺序表- Seqlist -> sequence 顺序 list 表

顺序表的概念

 问题与解答

 顺序表的分类

 静态顺序表

动态顺序表

问题与解答(递进式)

 动态顺序表的实现

 尾插

 头插

尾删

 头删

 指定位置插入

 指定位置删除

 销毁

总结


前言:线性表是具有相同特性的一类数据结构的集合。它是一种在实际中广泛使用的数据结构,常见的线性表顺序表、链表、栈、队列、字符串......

线性表在逻辑上是线性结构,指的是人为想出来的;但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

当然,当学习到数据结构时,需要具备结构体、指针(一级、二级)、内存动态管理这些知识。


顺序表- Seqlist -> sequence 顺序 list 表

顺序表的概念

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数据存储。 

总结

  1. 逻辑结构:一定是线性。因为这是认为想出来的
  2. 物理结构:一定也是线性的。因为它的底层结构是数组

 问题与解答

问题:既然顺序表的底层是数组,那么顺序表和数组的区别是什么?

回答顺序表的底层结构是数组,对数组的封装,实现了常用的增删改查等接口


 就像同样是饭馆,高级餐厅就会对菜品进行包装,加上一些饰品,就把菜名换一个名称。同样的顺序表其实就是数组多加上了一些功能。


 顺序表的分类

我们实现动态顺序表需要创建三个文件:Seqlist.h 头文件(声明结构和方法), Seqlist.c 源文件(实现声明的方法), test.c 测试文件

 静态顺序表

概念:使用定长数组存储元素

//静态顺序表
#define N 100
typedef int SLdatatype;把静态顺序表重命名,创建的时候更容易
typedef struct SeqList
{SLdatatype arr[N]; //定长数组int size; // 有效的个数
}SL;typedef struct SeqList SL; // 单独给他拉出来命名也可以

我们需要使用typedef对顺序表的名称进行重定义,这样使用起来更方便。

使用define定义常量N,这样只要改上面的数字,就可以进行更改。

静态顺序表的缺陷:空间给少了不够用,给多了造成空间浪费。 


动态顺序表

动态顺序表需要自己手动申请空间。

问题与解答(递进式)

问题1:动态顺序表需要申请空间,那么一次申请一个空间可以吗?

回答:一次增加一个,这是增容频繁,会导致程序效率低下。 


问题2:既然一次申请一个空间会效率低下,那么一次多申请空间呢? 

回答:一次多申请空间,增加大空间,会造成空间浪费


问题3:那么应该怎么申请空间呢?

回答3:通常,增容是按倍数增加的,如2倍。既能避免频繁增容,又能保证最大程度的减少空间的浪费。

总结:

一般增容是按2倍增容的,要插入的数据跟增量是成正相关的。


 动态顺序表的实现

Seqlist.h

typedef int SLdatatype;
typedef struct SeList
{SLdatatype* arr;int size; //有效的个数int capacity; // 容量大小
}SL;

 构建动态顺序表。


接下来是要对动态顺序表进行初始化

Seqlist.h

//初始化
void SLInit(SL* ps);

实现函数声明

Seqlist.c(源文件)

#include "SeqList.h"void SLInit(SL* ps)
{ps->arr = NULL;ps->size = ps->capacity = 0;
}

 测试函数

test.c

#include "SeqList.h"void SLTest()
{SL s1;SLInit(&s1);
}int main()
{SLTest();return 0;
}

 我们可以看到初始化成功了,并且值也成功给了s1。

 想必大家看到这里肯定会有疑惑,为什么这初始化函数里面传的是&s1的地址,而不是s1,

 因为s1是传值,这是把值传给Seqlist.c函数中的形参 SL  ps(这个写法是值传递的写法),虽然接收了值,但是它还会再内存中重新开辟一块地址,所以值传递,是不会改变原有的s1.

大家可以在自己的IDE里面试一下。


 尾插

 接下来是尾插

Seqlist.h

//尾插void SLPushBack(SL* ps, SLdatatype x);

 Seqlist.c(源文件)

void SLPushBack(SL* ps, SLdatatype x)
{assert(ps);int Newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//三目表达式if (ps->size == ps->capacity){int Newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;SLdatatype* newarr = (SLdatatype*)realloc(ps->arr, Newcapacity*sizeof(SLdatatype));if (newarr == NULL){perror("realloc");exit(1);}ps->arr = newarr;ps->capacity = Newcapacity;}

 首先你要判断传过来的是不能为NULL的,否则是不能进行尾插的。

三目表达式,如果等于0的话,初始化的值给4个字节。因为我们一开始初始化顺序表的时,给capacity值为0,这样不管扩容几倍,相乘都为0,所以这边要有这样的操作。

接下来,如果空间不够,给顺序表里的数组扩容(不会的查文档realloc函数的使用),当判断扩容成功时,把开辟的空间给数组,然后再对容量进行改变。

最后,进行插入尾插。


 经行测试函数

 test.c

#include "SeqList.h"void SLTest()
{SL s1;SLInit(&s1);SLPushBack(&s2, 1);SLPushBack(&s2, 2);SLPushBack(&s2, 5);SLPushBack(&s2, 10);SLPushBack(&s2, 12);
}int main()
{SLTest();return 0;
}

我们可以看到代码的返回值为0,这样子就是说明这个尾插函数是没有问题的。 

 从监视窗口也可以看到,当空间不够时,也可以扩容。


 头插

接下来是头插

Seqlist.h

//头插void SLPushFront(SL* ps, SLdatatype x);

  Seqlist.c(源文件)

void SLPushFront(SL* ps, SLdatatype x)
{assert(ps);CheckSpace(ps);//插入for (int i = ps->size; i > 0; i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[0] = x;//有效的个数增加++ps->size;}

头插,从后往前覆盖,然后把首个位置空出来,最后把需要插入的元素放进去,把有效的个数往后挪一个。


 因为扩容这段代码,在头插 和 尾插 里面都有,所以我们给他封装成一个函数。这样要使用就比较方便。

void CheckSpace(SL* ps)
{//容量不够,要扩容if (ps->size == ps->capacity){int Newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;SLdatatype* newarr = (SLdatatype*)realloc(ps->arr, Newcapacity * sizeof(SLdatatype));if (newarr == NULL){perror("realloc");exit(1);}ps->arr = newarr;ps->capacity = Newcapacity;}
}

 经行测试函数

 test.c

#include "SeqList.h"void SLTest()
{SL s1;SLInit(&s1);SLPushFront(&s1, 2);SLPushFront(&s1, 3);SLPushFront(&s1, 4);SLPushFront(&s1, 5);SLPushFront(&s1, 6);}int main()
{SLTest();return 0;
}


尾删

接下来是尾删

Seqlist.h

//尾删
void SLPopBack(SL* ps);

   Seqlist.c(源文件)

void SLPopBack(SL* ps)
{assert(ps && ps->size);--ps->size;
}

 当要尾删时,你传入的不能是NULL空指针 和 0个有效个数(里面需要有元素)。

因为 --ps->size;使有效的个数往前减一个,往后在增加的时候会直接覆盖。


 这边写一个打印函数给大家理解一下

void SLPrint(SL* ps)
{for (int i = 0; i < ps->size; i++){printf("%d ", ps->arr[i]);}
}

  经行测试函数

 test.c

#include "SeqList.h"void SLTest()
{SL s1;SLInit(&s1);SLPushFront(&s1, 2);SLPushFront(&s1, 3);SLPushFront(&s1, 4);SLPushFront(&s1, 5);SLPushFront(&s1, 6);SLPopBack(&s1); //6 5 4 3 SLPrint(&s1);printf("\n");SLPopBack(&s1); //6 5 4 SLPrint(&s1);
}int main()
{SLTest();return 0;
}


 头删

接下来是头删

Seqlist.h 

//头删
void SLPopFront(SL* ps);

   Seqlist.c(源文件)

void SLPopFront(SL* ps)
{assert(ps && ps->size);for (int i = 0; i < ps->size-1; i++){ps->arr[i] = ps->arr[i + 1];}--ps->size; //有效个数减1
}

当进行头删的时候,需要把元素往前挪。然后把有效的个数减少一个。


 进行测试函数

 test.c

#include "SeqList.h"void SLTest()
{SL s1;SLInit(&s1);SLPushFront(&s1, 2);SLPushFront(&s1, 3);SLPushFront(&s1, 4);SLPushFront(&s1, 5);SLPushFront(&s1, 6);SLPopFront(&s1);SLPrint(&s1);printf("\n");SLPopFront(&s1);SLPrint(&s1);printf("\n");
}
int main()
{SLTest();return 0;
}


 指定位置插入

接下来是指定位置插入

Seqlist.h 

//指定位置插入
void SLInsert(SL* ps, int pos, SLdatatype x);

  Seqlist.c(源文件)

void SLInsert(SL* ps, int pos, SLdatatype x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);for (int i = ps->size; i > pos; i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[pos] = x;//增加++ps->size;
}

当你要插入元素时,你传的pos是要大于等于0(相当于头插),然后要小于等于有效个数(相当于尾插) 。最后有效个数加一。

 

 进行测试函数

 test.c

#include "SeqList.h"void SLTest()
{SL s1;SLInit(&s1);SLPushBack(&s2, 1);SLPushBack(&s2, 2);SLPushBack(&s2, 5);SLInsert(&s2, 2, 10); //1 2 10 5
}
int main()
{SLTest();return 0;
}


 指定位置删除

接下来是 指定位置删除

Seqlist.h 

//指定位置删除
void SLErase(SL* ps, int pos);

  Seqlist.c(源文件)

void SLErase(SL* ps, int pos)
{assert(ps);assert(pos >= 0 && pos < ps->size);for (int i = pos; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}--ps->size;
}

 指定位置删除,把pos之后的数据往前挪,pos要在size之前,因为在size这个位置没有数据,是不能够删除的。最后再把有效个数减去一个。

 进行测试函数

 test.c

#include "SeqList.h"void SLTest()
{SL s1;SLInit(&s1);SLPushBack(&s2, 1);SLPushBack(&s2, 2);SLPushBack(&s2, 5);SLErase(&s2, 1); //1 5
}
int main()
{SLTest();return 0;
}


 销毁

最后是进行销毁动态开辟的内存

 Seqlist.h 

//销毁
void SLDestroy(SL* ps);

  Seqlist.c(源文件)

void SLDestroy(SL* ps)
{if (ps->arr){free(ps->arr);}ps->arr = NULL;ps->size = ps->capacity = 0;
}

 需要判断是否数组初始化过,不然释放空间会出错。

 进行测试函数

 test.c

#include "SeqList.h"void SLTest()
{SL s1;SLInit(&s1);SLPushBack(&s2, 1);SLPushBack(&s2, 2);SLPushBack(&s2, 5);SLInsert(&s2, 2, 10); SLDestroy(&s1);}
int main()
{SLTest();return 0;
}

最后我们也是成功的看到动态空间被释放掉了。 

正所谓有借有还,再借不难。


总结

 编写代码过程中要勤测试,避免写出⼤量代码后再测试⽽导致出现问题,问题定位⽆从下 ⼿。

 最后来看一下整体的代码

  Seqlist.h 

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>//静态顺序表
#define N 100
typedef int SLdatatype;//把静态顺序表重命名,创建的时候更容易
//typedef struct SeqList
//{
//	SLdatatype arr[N];
//	int size; // 有效的个数
//}SL;//typedef struct SeqList SL; // 单独给他拉出来命名也可以//动态顺序表typedef struct SeList
{SLdatatype* arr;int size; //有效的个数int capacity; // 容量大小
}SL;//初始化
void SLInit(SL* ps);//销毁
void SLDestroy(SL* ps);//尾插void SLPushBack(SL* ps, SLdatatype x);//头插void SLPushFront(SL* ps, SLdatatype x);//打印void SLPrint(SL* ps);//尾删
void SLPopBack(SL* ps);//头删
void SLPopFront(SL* ps);//查找
int SLFindData(SL* ps, SLdatatype x);//指定位置插入
void SLInsert(SL* ps, int pos, SLdatatype x);//指定位置删除
void SLErase(SL* ps, int pos);

  Seqlist.c(源文件) 

#define _CRT_SECURE_NO_WARNINGS
#include "SeqList.h"void SLInit(SL* ps)
{ps->arr = NULL;ps->size = ps->capacity = 0;
}void SLDestroy(SL* ps)
{if (ps->arr){free(ps->arr);}ps->arr = NULL;ps->size = ps->capacity = 0;
}void CheckSpace(SL* ps)
{//容量不够,要扩容if (ps->size == ps->capacity){int Newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;SLdatatype* newarr = (SLdatatype*)realloc(ps->arr, Newcapacity * sizeof(SLdatatype));if (newarr == NULL){perror("realloc");exit(1);}ps->arr = newarr;ps->capacity = Newcapacity;}
}void SLPushBack(SL* ps, SLdatatype x)
{assert(ps);CheckSpace(ps);//插入ps->arr[ps->size++] = x;
}void SLPushFront(SL* ps, SLdatatype x)
{assert(ps);CheckSpace(ps);//插入for (int i = ps->size; i > 0; i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[0] = x;//有效的个数增加++ps->size;}void SLPrint(SL* ps)
{for (int i = 0; i < ps->size; i++){printf("%d ", ps->arr[i]);}
}void SLPopBack(SL* ps)
{assert(ps && ps->size);--ps->size;
}void SLPopFront(SL* ps)
{assert(ps && ps->size);for (int i = 0; i < ps->size-1; i++){ps->arr[i] = ps->arr[i + 1];}--ps->size;
}int SLFindData(SL* ps, SLdatatype x)
{assert(ps && ps->size);for (int i = 0; i < ps->size; i++){if (ps->arr[i] == x){return i;}}return -1;
}void SLInsert(SL* ps, int pos, SLdatatype x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);for (int i = ps->size; i > pos; i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[pos] = x;//增加++ps->size;
}void SLErase(SL* ps, int pos)
{assert(ps);assert(pos >= 0 && pos < ps->size);for (int i = pos; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}--ps->size;
}

  test.c测试文件

#define _CRT_SECURE_NO_WARNINGS
#include "SeqList.h"void SLTest()
{SL s1;SLInit(&s1);/*SLPushBack(&s1, 1);SLPushBack(&s1, 2);SLPushBack(&s1, 5);SLPushBack(&s1, 10);SLPushBack(&s1, 12);*///SLPushFront(&s1, 2);/*SLPrint(&s1);printf("\n");*///SLPushFront(&s1, 3);/*SLPrint(&s1);printf("\n");*//*SLPushFront(&s1, 4);SLPushFront(&s1, 5);SLPushFront(&s1, 6);*//*SLPopBack(&s1);SLPrint(&s1);printf("\n");SLPopBack(&s1);SLPrint(&s1);*//*SLPopFront(&s1);SLPrint(&s1);printf("\n");SLPopFront(&s1);SLPrint(&s1);printf("\n");*/SLPushBack(&s1, 1);SLPushBack(&s1, 2);SLPushBack(&s1, 5);SLErase(&s1, 1);SLPrint(&s1);printf("\n");SLDestroy(&s1);
}void SLTest2()
{SL s2;SLInit(&s2);SLPushBack(&s2, 1);SLPushBack(&s2, 2);SLPushBack(&s2, 5);SLPushBack(&s2, 10);SLPushBack(&s2, 12);SLPrint(&s2);printf("\n");int ret = SLFindData(&s2, 5);if (ret > 0){printf("找到了\n");}else{printf("未找到\n");}SLInsert(&s2, 2, 1);SLPrint(&s2);printf("\n");SLErase(&s2, 4);SLPrint(&s2);printf("\n");}int main()
{SLTest();//SLTest2();return 0;
}

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

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

相关文章

2024 年 04 月编程语言排行榜,PHP 排名创新低?

编程语言的流行度总是变化莫测&#xff0c;每个月的排行榜都揭示着新的趋势。2024年4月的编程语言排行榜揭示了一个引人关注的现象&#xff1a;PHP的排名再次下滑&#xff0c;创下了历史新低。这种变化对于PHP开发者和整个技术社区来说&#xff0c;意味着什么呢&#xff1f; P…

现代数字信号处理I-P3 MVUE学习笔记

目录 1. 参数估计问题的提出与本质 2. 估计的性质 2.1 Ancillary&#xff08;多余估计&#xff09; 例1&#xff0c;Ancillary估计量 2. Uniformly Optimal 3. Sufficiency充分性 3.1 统计量充分性定义 例2&#xff1a;利用充分统计量定义获取伯努利分布的充分统计量 …

Anaroute - 理论学习(一)

一、贡献&#xff1a; 框架能够在考虑特定约束的同时&#xff0c;高效地完成复杂AMS设计的布线&#xff0c;并实现签署质量的性能。 提出了一种对称性约束的分配算法&#xff0c;根据引脚位置分配合适的网络匹配要求新的引脚聚类策略&#xff0c;以实现规律性的布线模式&…

微知-Bluefield DPU使用flint烧录固件报错MFE_NO_FLASH_DETECTED是什么?MFE是什么?

文章目录 背景一些报错场景MFE是什么&#xff1f;有哪些MFE 背景 在DPU的fw操作flint的时候&#xff0c;很多命令都会报这个错误&#xff1a;MFE_NO_FLASH_DETECTED&#xff0c;早期很疑惑并且猜测MFE是Mellanox Firmware Engine。实际并不是&#xff0c;具体还得走到mellanox…

2014年国赛高教杯数学建模B题创意平板折叠桌解题全过程文档及程序

2014年国赛高教杯数学建模 B题 创意平板折叠桌 某公司生产一种可折叠的桌子&#xff0c;桌面呈圆形&#xff0c;桌腿随着铰链的活动可以平摊成一张平板&#xff08;如图1-2所示&#xff09;。桌腿由若干根木条组成&#xff0c;分成两组&#xff0c;每组各用一根钢筋将木条连接…

2024 第一次周赛

A: 题目大意 骑士每连续 i 天每天会得到 i 个金币&#xff0c;&#xff08;i 1&#xff0c; 2&#xff0c; 3 &#xff0c; …&#xff09;,那么展开看每一天可以得到的金币数&#xff1a;1 2 2 3 3 3 4 4 4 5 5 5 5 5 … 可以发现就是1个1 &#xff0c;2个2, 3个3…,那么我…

php 生成随机数

记录:随机数抽奖 要求:每次生成3个 1 - 10 之间可重复(或不可重复)的随机数,10次为一轮,每轮要求数字5出现6次、数字4出现3次、…。 提炼需求: 1,可设置最小数、最大数、每次抽奖生成随机数的个数、是否允许重复 2,可设置每轮指定数字的出现次数 3,可设置每轮的抽奖…

一维数组的引用

#define SIZE 5 int main(void) { int i 0; int arr[SIZE] { 86,85,85,896,45 };//同理五个数据只是偶然&#xff0c;可能会更多 //输入 for (i 0;i < SIZE;i) { printf("请输入你的第%d个值&#xff1a;",i1); scanf_s(&…

Spark常用RDD算子:transformation转换算子以及action触发算子

文章目录 1. 算子&#xff08;方法&#xff09;介绍2. 常用transformation算子2.1 map 2.2 flatMap2.3 filter2.4 distinct2.6 groupBy2.7 sortBy()2.8 k-v数据[(k,v),(k1,v1)] 3. 常用action算子 1. 算子&#xff08;方法&#xff09;介绍 rdd中封装了各种算子方便进行计算&a…

【Linux网络编程】网络基础 | Socket 编程基础

&#x1f308;个人主页&#xff1a; 南桥几晴秋 &#x1f308;C专栏&#xff1a; 南桥谈C &#x1f308;C语言专栏&#xff1a; C语言学习系列 &#x1f308;Linux学习专栏&#xff1a; 南桥谈Linux &#x1f308;数据结构学习专栏&#xff1a; 数据结构杂谈 &#x1f308;数据…

【动手学深度学习】6.3 填充与步幅(个人向笔记)

卷积的输出形状取决于输入形状和卷积核的形状在应用连续的卷积后&#xff0c;我们最终得到的输出大小远小于输入大小&#xff0c;这是由于卷积核的宽度和高度通常大于1导致的比如&#xff0c;一个 240 240 240240 240240像素的图像&#xff0c;经过10层 5 5 55 55的卷积后&am…

自然语言处理问答系统:技术进展、应用与挑战

自然语言处理&#xff08;NLP&#xff09;问答系统是人工智能领域的一个重要分支&#xff0c;它通过理解和分析用户的提问&#xff0c;从大量的文本数据中提取相关信息&#xff0c;并以自然语言的形式回答用户的问题。随着深度学习技术的发展&#xff0c;尤其是预训练语言模型&…

MATLAB智能优化算法-学习笔记(4)——灰狼优化算法求解旅行商问题【过程+代码】

灰狼优化算法(Grey Wolf Optimizer, GWO)是一种基于灰狼社会行为的元启发式算法,主要模拟灰狼群体的捕猎行为(包括围攻、追捕、搜寻猎物等过程)。多旅行商问题(Multi-Traveling Salesman Problem, mTSP)是旅行商问题(TSP)的扩展,它涉及多个旅行商(车辆)从一个起点城…

深度学习:循环神经网络—RNN的原理

传统神经网络存在的问题&#xff1f; 无法训练出具有顺序的数据。模型搭建时没有考虑数据上下之间的关系。 RNN神经网络 RNN&#xff08;Recurrent Neural Network&#xff0c;循环神经网络&#xff09;是一种专门用于处理序列数据的神经网络。在处理序列输入时具有记忆性…

动态规划的优化与高级应用

姊妹篇&#xff1a; 动态规划基础与经典问题-CSDN博客 贪心算法&#xff1a;原理、应用与优化_最优解-CSDN博客​​​​​​贪心算法&#xff1a;原理、应用与优化_最优解-CSDN博客 一、动态规划的优化策 动态规划在提高时间效率的同时&#xff0c;往往会占用较多的空间。因…

【汇编语言】寄存器(CPU工作原理)(七)—— 查看CPU和内存,用机器指令和汇编指令编程

文章目录 前言1. 预备知识&#xff1a;Debug的使用1.1 什么是Debug&#xff1f;1.2 我们用到的Debug功能1.3 进入Debug1.3.1 对于16位或者32位机器的进入方式1.3.2 对于64位机器的进入方式 1.4 R命令1.5 D命令1.6 E命令1.7 U命令1.8 T命令1.9 A命令 2. 总结3. 实操练习结语 前言…

grpc的python使用

RPC 什么是 RPC &#xff1f; RPC&#xff08;Remote Procedure Call&#xff09;远程过程调用&#xff0c;是一种计算机通信协议&#xff0c;允许一个程序&#xff08;客户端&#xff09;通过网络向另一个程序&#xff08;服务器&#xff09;请求服务&#xff0c;而无需了解…

笔试算法总结

文章目录 题目1题目2题目3题目4 题目1 使用 StringBuilder 模拟栈的行为&#xff0c;通过判断相邻2个字符是否相同&#xff0c;如果相同就进行删除 public class Main {public static String fun(String s) {if (s null || s.length() < 1) return s;StringBuilder builde…

前端开发基础NodeJS+NPM基本使用(零基础入门)

文章目录 1、Nodejs基础1.1、NodeJs简介1.2、下载安装文件1.3、安装NodeJS1.4、验证安装2、Node.js 创建第一个应用2.1、说明2.2、创建服务脚本2.3、执行运行代码2.4、测试访问3、npm 基本使用3.1、测试安装3.2、配置淘宝npm镜像3.3.1、本地安装3.3.2、全局安装3.4、查看安装信…