定义:
线性表的基本定义::
- 线性表(Linear List)是具有相同数据类型的n(n≥0)个数据元素的有限序列,其中n为表长,当n = 0时线性表是一个空表。
- 若用L命名线性表,则其一般表示为L = (a1, a2, … , ai, ai+1, … , an)。
- a1是表头元素。
- an是表尾元素。
- 除第一个元素外,每个元素有且仅有一个直接前驱。
- 除最后一个元素外,每个元素有且仅有一个直接后继。
线性表的基本操作:
- InitList(&L):初始化表。构造一个空的线性表L,分配内存空间。
- DestroyList(&L):销毁操作。销毁线性表,并释放线性表L所占用的内存空间。
- ListInsert(&L,i,e):插入操作。在表L中的第i个位置上插入指定元素e。
- ListDelete(&L,i,&e):删除操作。删除表L中第i个位置的元素,并用e返回删除元素的值。
- LocateElem(L,e):按值查找操作。在表L中查找具有给定关键字值的元素。
- GetElem(L,i):按位查找操作。获取表L中第i个位置的元素的值。
- Length(L):求表长。返回线性表L的长度,即L中数据元素的个数。
- PrintList(L):输出操作。按前后顺序输出线性表L的所有元素值。
- Empty(L):判空操作。若L为空表,则返回true,否则返回false。
- 什么时候要传入参数的引用“&”: 对参数的修改结果需要“带回来”,是引用类型而不是值类型
顺序表的基本定义:
用顺序存储的方式实现线性表顺序存储,把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。
如果“数组”存满了怎么办:
顺序表的表长刚开始确定后就无法更改(存储空间是静态的),同时如果提前初始化太多的空间而不用,又会造成资源的浪费。可以用指针等方法,或者用vector等stl的数据结构。
顺序表和单链表实现线性表的区别
顺序表:
- 每个结点中只存放数据元素
- 优点:可随机存取,存储密度高
- 缺点:要求大片连续空间,改变容量不方便
单链表:
- 每个结点除了存放数据元素外,还要存储指向下一个结点的指针
- 优点:不要求大片连续空间,改变容量方便
- 缺点:不可随机存取,要耗费一定空间存放指针
循环单链表与单链表的区别:
单链表:
- 表尾结点的next指针指向NULL
- 从一个结点出发只能找到后续的各个结点
循环单链表:
- 表尾结点的next指针指向头结点
- 从一个结点出发可以找到其他任何一个结点
循环双链表与双链表的区别:
双链表:
- 表头结点的prior指向NULL
- 表尾结点的next指向NULL
循环双链表:
- 表头结点的prior指向表尾结点
- 表尾结点的next指向头结点
顺序表和链表的比较
逻辑结构:
都属于线性表,都是线性结构
存储结构:
顺序表:
- 优点:支持随机存取、存储密度高
- 缺点:大片连续空间分配不方便,改变容量不方便
链表:
- 优点:离散的小空间分配方便,改变容量方便
- 缺点:不可随机存取,存储密度低
基本操作:
顺序表:
-
创建
-
-
需要预分配大片连续空间。
-
若分配空间过小,则之后不方便拓展容量;
-
若分配空间过大,则浪费内存资源
-
静态分配:静态数组实现,容量不可改变
-
动态分配:动态数组(malloc、free)实现,容量可以改变但需要移动大量元素,时间代价高
-
-
销毁
-
-
修改Length = 0
-
静态分配:静态数组,系统自动回收空间
-
动态分配:动态数组(malloc、free),需要手动free
-
-
增删
-
-
插入/删除元素要将后续元素都后移/前移
-
时间复杂度O(n),时间开销主要来自移动元素
-
若数据元素很大,则移动的时间代价很高
-
-
查
-
-
按位查找:O(1)
-
按值查找:O(n)若表内元素有序,可在O(log2n)时间内找到
-
链表:
-
创建
-
-
只需分配一个头结点(也可以不要头结点,只声明一个头指针),之后方便拓展
-
-
销毁
-
-
依次删除各个结点(free)
-
-
增删
-
-
插入/删除元素只需修改指针即可
-
时间复杂度O(n),时间开销主要来自查找目标元素
-
查找元素的时间代价更低
-
-
查
-
- 按位查找:O(n)
- 按值查找:O(n)
用哪个:
- 表长难以预估、经常要增加/删除元素——链表
- 表长可预估、查询(搜索)操作较多——顺序表
常用的c和c++函数
c:
printf("%s", s):用%s来输出一个字符串(字符数组)。scanf("%s", &s):用%s来读入一个字符串(字符数组)。sscanf(const char *__source, const char *__format, ...):从字符串__source里读取变量,比如sscanf(str,"%d",&a)。sprintf(char *__stream, const char *__format, ...):将__format字符串里的内容输出到__stream中,比如sprintf(str,"%d",i)。strlen(const char *str):返回从str[0]开始直到'\0'的字符数。注意,未开启 O2 优化时,该操作写在循环条件中复杂度是的。
strcmp(const char *str1, const char *str2):按照字典序比较str1 str2若str1字典序小返回负值,两者一样返回0,str1字典序更大则返回正值。请注意,不要简单的认为返回值只有0、1、-1三种,在不同平台下的返回值都遵循正负,但并非都是0、1、-1。strcpy(char *str, const char *src): 把src中的字符复制到str中,strsrc均为字符数组头指针,返回值为str包含空终止符号'\0'。strncpy(char *str, const char *src, int cnt):复制至多cnt个字符到str中,若src终止而数量未达cnt则写入空字符到str直至写入总共cnt个字符。strcat(char *str1, const char *str2): 将str2接到str1的结尾,用*str2替换str1末尾的'\0'返回str1。strstr(char *str1, const char *str2):若str2是str1的子串,则返回str2在str1的首次出现的地址;如果str2不是str1的子串,则返回NULL。strchr(const char *str, int c):找到在字符串str中第一次出现字符c的位置,并返回这个位置的地址。如果未找到该字符则返回NULL。strrchr(const char *str, char c):找到在字符串str中最后一次出现字符c的位置,并返回这个位置的地址。如果未找到该字符则返回NULL。
c++:
- 访问函数
data()/c_str()返回一个const char*指针,内容与该string相同。 - 容量函数
size()返回字符串字符个数。 find(ch, start = 0)查找并返回从start开始的字符ch的位置;rfind(ch)从末尾开始,查找并返回第一个找到的字符ch的位置(皆从0开始)(如果查找不到,返回-1)。substr(start, len)可以从字符串的start(从0开始)截取一个长度为len的字符串(缺省len时代码截取到字符串末尾)。append(s)将s添加到字符串末尾。append(s, pos, n)将字符串s中,从pos开始的n个字符连接到当前字符串结尾。replace(pos, n, s)删除从pos开始的n个字符,然后在pos处插入串s。erase(pos, n)删除从pos开始的n个字符。insert(pos, s)在pos位置插入字符串s。
参考
[1] <<2023王道数据结构>>
[2] cppreference