数据结构复习(1)线性表(上)
一.线性表的定义和基本操作
1.线性表的定义
1)文字定义:具有相同数据类型的n(n>=0)个数据元素的有限序列。
2)相关术语:n表长;n=0空表;a1表头元素;an表尾元素;除了表头元素,线性表中每个元素有且仅有一个直接前驱;除了表尾元素,线性表中每个元素有且仅有一个直接后继。
3)代数表示:L = (a1,a2,…,an)
4)特点:元素个数有限/元素具有逻辑上的顺序/表中每一个元素都是单一的数据元素/数据元素的数据类型都相同,占用相同大小的存储空间/元素具有抽象性,所谓线性只是讨论元素间的逻辑关系
2.线性表的基本操作
名称 | 声明 | 功能 | 备注 |
---|---|---|---|
初始化 | InitList(&L) | 构造一个空的线性表 | |
插入 | Insert(&L,i,e) | 在表L 中的第i个位置插入值为e的元素 | |
查找1 | Locate(L,e) | 在表L中查找值为e的元素 | |
查找2 | GetElem(L,i) | 在表L中查找第i个位置上的元素 | |
删除 | Delete(&L,i,&e) | 在表L中删除第i个位置上的元素并返回值 | |
判空 | Empty(L) | 判断表是否为空表 | |
求表长 | Length(L) | 求表L中元素的个数 | |
销毁 | Destroy(&L) | 销毁线性表,释放其占用的内存空间 | |
说明
:
- 上述基本操作基于逻辑结构的定义,具体实现取决于物理结构(顺序、链表(指针/数组))
- 上述声明中的“&”符号表示C++中的引用,表示在函数内部要对传入的值本身进行改变
二.线性表的顺序表示
1.顺序表的相关概念
- 定义:顺序表——线性表的顺序存储,用一组地址连续的存储单元,依次存储线性表中的数据元素,使得逻辑上相邻的元素在物理存储上也是相邻的。
-
顺序表的特点:
优点 缺点 ①随机访问,可以通过首地址和元素序号在O(1)时间内找到某一元素
②存储密度高,每个节点值存储数据元素插入和删除操作需要移动大量的元素,时间复杂度较高 -
顺序表的程序定义(*)
-
静态分配(C/C++)
#define max 50 typedef struct{ ElemType data[max]; int length; }SqList;
-
动态分配(C)
#define initsize 50 typedef struct{ ElemType *data; int max;//最大容量 int length;//当前容量 }SqList; SqList L; L.data = (ElemType*)malloc(sizeof(ElemType)*initsize)
-
动态分配(C++)
//结构体构造同上,也可以采用stl容器 SqList L; L.data = new ElemType[initsize];
-
2.顺序表上基本操作的实现
-
插入操作
-
描述:若在L中位序i处插入某元素,需要将位序为i到length的所有元素均向后移位,再插入元素。
-
复杂度分析:
最好情况,表尾插入(i = n+1),O(1)
最坏情况,表头插入(i = 1),O(n)
平均情况:因为选取到每一个下标是等可能性的,设选到i的概率Pi = 1/n,位序为i需移动n-i+1次,故平均次数为n/2,O(n)
-
-
代码:
bool ListInsert(SqList &L,int i,ElemType e){ if(i<1||i>L.length+1) return false;//位序描述 if(L.length>=maxsize) return falses; for(int j = L.length-1;j>=i-1;j--){ L.data[j+1] = L.data[j];//物理下标处理 } L.data[i-1] = e; L.length++; retrn true; }
-
删除操作
-
描述:要删除表中位序为i的元素,先将位序为i的元素返回(先返回),再把位序为i+1到length的元素依次向前移位。
-
复杂度分析:
最好情况,删除表尾,O(1)
最坏情况,删除表头,O(n)
平均情况,分析同上,次数为(n-1)/2,O(n)
-
代码:
bool Delete(SqList &L,int i,ElemType &e){ if(i<1||i>L.length) return false;//i>length也承包了判空功能 e = L.data[i-1] for(int j = i;j<L.length;j++){ L.data[j-1] = L.data[j]; } L.length--; return true; }
-
-
按值查找操作
-
描述:依次遍历当前顺序表中的每个元素,若判断当前元素等于查找的值,则返回这个元素在线性表中的位序;因此若有多个符合条件的值,返回的是最小位序。
-
复杂度分析:
最好情况,表头元素符合,O(1)
最坏情况,表尾元素符合或没有符合的元素,O(n)
平均情况,同上分析,O(n)
-
代码:
int LocateElem(SqList L,ElemType e){ for(int i = 0;i<L.length;i++){ if(L.data[i] == e) return i+1; } return 0; }
-
3.顺序表相关算法设计题记录
-
题型罗列
-
位序变化:全部(整个顺序表元素倒置),部分(指定前后两部分位置交换),归并
-
基于值的元素查找+删除
值:单值、区间、最值、重复元素
顺序表本身特性:有序、无序
-
-
题解说明
-
基本考虑:
①写算法对应的完整代码的时候要注意先考虑题目中提到的或者前面所说的基本的异常处理(数据结构满/空,传入参数的合法性)
②考虑函数的返回类型,需要传回一些值的时候,可以考虑值返回或是参数返回(引用参数),后者可以返回多个值
-
[经典/有序顺序表的合并]
bool Merge(SqList A,SqList B,SqList &C){ if(A.length+B.length>C.maxsize) return false; int i = 0,j = 0,k = 0; while(i<A.length&&j<B.length){ if(A.data[i]<=B.data[j]){ C.data[k++] = A.data[i++]; } else{ C.data[k++] = B.data[j++]; } } while(i<A.length) C.data[k++] = A.data[i++]; while(j<B.length) C.data[k++] = B.data[j++]; C.length = k+1;//为什么我觉得长度应该是k呢,答案给的k+1 return true; }
-
[逆序]将存储在一维数组中的两个线性表进行位置互换:思路就是将两个线性表先一起逆序,再分别对两个线性表进行逆序
typedef int DataType; void Reverse(DataType A[],int left,int right,int arraySize){ if(left>=right||right>=arraySize) return; int mid = (left+right)/2; for(int i = 0;i<mid-left;i++){ DataType tmp = A[left+i]; A[left+i] = A[right-i]; A[right-i] = tmp; } } void Exchange(DataType A[],int m,int n,int arraySize){ Reverse(A,0,m+n-1,arraySize); Reverse(A,0,n-1,arraySize); Reverse(A,n,m+n-1,arraySize); }
-
[有序/区间/查找+删除]在有序顺序表L中删除给定值s与t之间(s<t)的所有元素,若s或t不合理或者顺序表为空则显示出错信息并退出运行。
-
for(;j<L.length;i++,j++){
L.data[i] = L.data[j];
}
[^记录]:从有序顺序表中删除所有其值重复的元素,使表中所有元素的值均不同。
> 我的思路:我先看过了无序的三种题解方法,然后认为这道题也是一个套路题,采用一个下标k记录所有非重复元素的个数,从头到尾为依次遍历,用一个临时变量记录当前查找的元素值,如果下一个元素和当前元素值不同,则放进数组,同时更新下标k和临时变量。
>
> 题解:题解的思路也是一致的,但是它说到了不需要单独用一个临时变量存储元素值,因为整理出的不含重复元素的线性表的最后一个元素就是当前查找的元素值;而且题解的代码更优美!!
>
> ```C++
> bool Delete_Same(SqList& L){
> if(L.length == 0)
> return false;
> int i,j;//这里的i就相当于我们前面说的k,这里的j是循环控制的下标
> for(i = 0,j = 1;j<L.length;j++){
> if(L.data[i]!=L.data[j]){
> L.data[++i] = L.data[j];
> }
> L.length = i+1;
> }
> return true;
> }
> ```
>
>
-
[无序/区间/查找+删除]
建议先看下面的无序/单值的模型对应的三类解法,无序/区间的处理和其差不多相同的,只不过删除的条件用两个不等式来判断
-
[无序/单值/查找+删除]删除顺序表L中所有值为x的数据元素
a.采用变量k记录不满足删除条件的元素数目
void del_x(SqList &L,ElemType x){ int k = 0; for(int i = 0;i<L.length;i++){ if(L.data[i]!=x){ L.data[k] = L.data[i]; k++; } } L.length = k; }
b.采用变量k记录满足删除条件的元素数目,则每个元素非删除元素需要向前移动k个位置,因为其前面出现了k个被删除的元素(k随着遍历一直在增加)
void del_x(SqList &L,ElemType x){ int k = 0; for(int i = 0;i<L.length;i++){ if(L.data[i] == x){ k++; } else{ L.data[i-k] = L.data[i]; } } L.length -= k; }
c.设置头尾两个指针,从两端向中间移动,每次在左端遇到待删除的元素,就用最右端非删除的元素来补充,直到左右指针相遇。**!!**但是这种方法改变了原顺序表中元素的相对位置。
void del_x(SqList &L,ElemType x){ int i = 1,j = L.length; while(i<j){ if(L.data[i-1] == x){ while(L.data[j-1]==x) j--; L.data[i-1] = L.data[j-1]; i++; j--; } else{ i++; } } if(L.data[j-1] == x) L.length = j-1; else L.length = j; }
思维导图:
参考资料:《王道2019年数据结构考研指导》
本文系自己在复习专业课知识整理留作自用