绪论
数据
数值性数据,非数值性数据
输入,输出,存储数据
数据元素(数据的基本单位)
有时一个数据元素可以由若干数据项(Data Item)组成。数据项是具有独立含义的最小标识单位。
数据对象(数据的子集)
具有相同性质的数据成员(数据元素)的集合
数据结构 Data_Structure = { D, R, M }
D 是某一数据元素的集合;
R 是该集合中所有数据成员之间的关系的有限集合;
M 是作用在该集合所有数据成员之上的方法(或操作)。
数据的逻辑结构
数据的逻辑结构抛弃了数据元素及其关系的实现细节,只反映了系统的需求(从用户使用层面上)而不考虑如何实现。
数据的逻辑结构从逻辑关系上描述数据,与数据的存储无关。
数据的的逻辑结构与数据元素的内容无关。
数据的逻辑结构与它所包含的数据元素个数无关。
数据的逻辑结构与数据元素所处位置无关。
分类
线性结构
线性表
非线性结构
多维数组
广义表
树
图(或网络)
无结构
集合
数据的存储结构(物理结构)
内容存储:数据元素的内容或值
关系存储:各数据元素之间的逻辑关系
附加存储:为使施加于数据结构上的运算得以实现而附加的存储空间
顺序存储表示:连续空间存储
链接存储表示:动态分配内存空间
索引存储表示:用于对索引文件的实现(索引表)
散列存储表示:用于直接存取文件的实现(函数求出的关键码)
数据类型
基本数据类型
构造数据类型
抽象数据类型
信息隐藏
数据封装
使用与实现相分离
算法: 算法+数据结构=程序
输入:0个或多个输入
输出:1个或多个输出
确定性
有穷性
有效性
评价(复杂度,完善)
正确性
可读性
健壮性
高效性
简单性
基本方法
穷举型(素数)
迭代法(求根)
递推法(斐波那契数列)
递归法(同上)
动态规划法
time() 后期测试
double start, stop;
time (&start);
int k = seqsearch (a, n, x);
time (&stop);
double runTime = stop - start;
printf ( ”%d%d\n " , n, runTime );
时间复杂度
c < log2n < n < nlog2n < n^2 < n^3 < 2^n < 3^n < n!
for(int i = 0;i < m;i++)
语句频度:m+1
递归的复杂度(递推后去计算)
2.线性表
定义 n(≥0)个数据元素的有限序列,记作(a1, a2, …, an)
ai 是表中数据元素,n 是表长度。
特点:线性排列
除第一个元素外,其他每一个元素有一个且仅有一个直接前趋。
除最后一个元素外,其他每一个元素有一个且仅有一个直接后继。
线性表key point
1.表中元素具有逻辑上的顺序性,在序列中各元素排列有其先后次序,有唯一的首元素和尾元素。
2.表中元素个数有限。
3.表中元素都是数据元素。即每一表元素都是原子数据,不允许“表中套表”。
4.表中元素的数据类型都相同。这意味着每一表元素占有相同数量的存储空间。
顺序表
定义
将线性表中的元素相继存放在一个连续的存储空间中,即构成顺序表。
存储
它是线性表的顺序存储表示,可利用一维数组描述存储结构。
特点
元素的逻辑顺序与物理顺序一致。
访问方式
可顺序存取,可按下标直接存取。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FmPB80jr-1631361136971)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20210103155834911.png)]
LOC是元素存储位置,l是元素大小
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6kHUpfhB-1631361136973)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20210103155928887.png)]
静态结构:表一旦装满,不能扩充
int n;
T data[MAXN];
动态结构:可以扩充
int maxsize;
int n;
T *data;
查找
从顺序表的头开始顺序查找
查找成功:n*(n+1)/2n = (n+1)/2
查找不成功:n
插入
for(int j = L.n-1;j >= i-1;j--)// 第i个位置插入
{
L.data[j+1] = L.data[j];
}
L.data[i-1] = x;
插入时平均移动元素个数:n*(n+1)/2(n+1) = n/2
删除
for(int j = i;j < L.n;j++) // 删除第i个元素
{
L.data[j-1] = L.data[j];
}
删除时平均移动元素个数:n*(n-1)/2n = (n-1)/2
链表(连接存储)
链表中第一个元素结点称为首元结点,最后一个元素称为尾结点。首元结点不是头结点。
单链表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j25WW0ML-1631361136987)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20210103161822923.png)]
线性结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3b6A1cSW-1631361136993)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20210103161843031.png)]
结点可以连续,可以不连续存储
结点的逻辑顺序与物理顺序可以不一致
表可扩充
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gikqYGIq-1631361136996)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20210103162055240.png)]
定义
struct node{
T data;
struct node *link;
};
插入
newnode->link = first;
first = newnode; // 在第一个节点前插入
//首先定位到插入位置p后面
newnode->link = p->link;
p->link = newnode;
删除
q = first;
first = first->link;
delete q; // 删除表中第一个元素
//删除p后面的节点
q = p->link;
p->link = q->link;
delete q;
带头结点的单链表
头结点位于表的最前端,本身不带数据,仅标志表头。
设置头结点的目的是
统一空表与非空表的操作
简化链表操作的实现。
前插法建立单链表
从一个空表开始,重复读入数据:
生成新结点
将读入数据存放到新结点的数据域中
将该新结点插入到链表的前端
直到读入结束符为止。
输入顺序与链接顺序相反
while(val != endtag)
{
LinkNode *s = (LinkNode*)malloc(sizeof(LinkNode));
s->data = val;
s->link = first->link;
first->link = s;
}
后插法建立单链表
输入顺序与链接顺序相同
rear->link = s;
s = rear;
清空,计算长度,按值查找,定位
循环链表(约瑟夫环)
循环单链表是单链表的变形。链表尾结点的 link 指针不是 NULL,而是指向了表的前端。
为简化操作,在循环单链表中往往加入头结点。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x9cuJLtr-1631361136997)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20210103164435110.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8SrvqP42-1631361136999)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20210103164837602.png)]
循环单链表的判空条件是
first -> link == first;
循环单链表的特点是:只要知道表中某一结点的地址,就可搜寻到所有其他结点的地址。
在搜寻过程中,没有一个结点的 link 域为空。
循环单链表的所有操作的实现类似于单链表,差别在于检测到链尾,指针不为NULL,而是回到链头。
在表尾可直接插入新结点,时间复杂性 O(1);
在表尾删除时要找前趋,时间复杂性 O(n);
在表头插入相当于在表尾插入;
在表头可直接删除,时间复杂性 O(1)。
双向链表
双向链表是指在前趋和后继方向都能遍历的线性链表。双向链表每个结点的结构为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pTQeMidO-1631361137000)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20210103165317973.png)]
双向链表通常采用带头结点的循环双链表形式。每一个结点处于两个链中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XfyhjXyY-1631361137003)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20210103165343965.png)]
p->lLink 指示结点 p 的前趋结点
p->rLink 指示结点 p 的后继结点
p->lLink->rLink指示结点 p 的前趋结点的后继结点,即结点 p 本身
p->rLink->lLink指示结点 p 的后继结点的前趋结点,即结点 p 本身
定位
插入
p的前驱结点为新插入结点
newnode->llink = p->llink;
p->llink = newnode;
newnode->rlink = p;
newnode->llink->rlink = newnode;
p的后继节点为新插入节点
newnode->rlink = p->rlink;
p->rlink= newnode;
newnode->llink = p;
newnode->rlink->llink = newnode;
删除
p->rlink->llink = p->llink;
p->llink->rlink = p->rlink;
顺序表与链表的比较
基于空间的比较
存储分配的方式
顺序表的存储空间可以是静态分配的,也可以是动态分配的。
链表的存储空间是动态分配的。
存储密度 = 结点数据本身所占的存储量/结点结构所占的存储总量
顺序表的存储密度 = 1
链表的存储密度 < 1
基于时间的比较
存取方式
顺序表可以随机存取,也可以顺序存取。
链表只能顺序存取。
插入/删除时移动元素个数
顺序表平均需要移动近一半元素。
链表不需要移动元素,只需要修改指针。
若插入/删除仅发生在表的两端,宜采用带尾指针的循环链表。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MWvVQ0ZV-1631361137004)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20210103172524706.png)]
一元多项式
静态数组表示
int degree; // 实际阶数
int coef[Maxdegree+1]; // 系数数组
优点:简化操作
缺点:系数多项式不经济
只保存非0项
struct data{
float coef; // 系数
int exp; // 指数
};
struct polynomial{
int maxsize; // 数组最大保存项数
int n; //实际项数
data* Term;
}
链表
typedef struct node { //多项式数据定义
float coef; //系数
int exp; //指数
struct node * link; //链接指针
};
多项式的项数可以动态地增长,不存在存储溢出问题。
插入、删除方便,不移动元素。
加法(类似于归并排序)
?静态链表
3.栈和队列
栈(后进先出)
只允许在一端插入和删除(栈顶)
另一端(栈底)
顺序栈
struct seqstack{
int *elem;
int top, maxsize; // top为栈顶指针
};
//初始化时S.top = -1
//判断栈空为判断S.top == -1是否为true
//判断栈满是否为S.top == S.maxsize-1
void push(SeqStack& S, int x)
{
//栈满判断,栈满处理(建立新栈,将原来的栈拷贝)
S.elem[++S.top] = x;
}
bool pop(SeqStack& S, int& x)
{
//判断栈是否为空
x = S.elem[S.top--];
return true;
}
bool getTop(SeqStack& S, int& x)
{
// 判断是否栈空
x = S.elem[S.top];
return true;
}
双栈共享一个栈空间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FgYWFiXw-1631361137006)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20210106095937669.png)]
两个栈共享一个数组空间V[MaxSize],设立栈顶指针数组t[2]和栈底指针数组b[2]
t[i]和b[i]指定栈i的栈顶和栈底
初始
t[0] = b[0] = -1; // 一开始未存元素
t[1] = b[1] = maxSize;
栈满条件
t[0] + 1 = t[1];
栈空条件
t[0] = b[0];
t[1] = b[1];
链式栈
顺序栈有栈满问题,一旦栈满需要做溢出处理,扩充栈空间,时间和空间开销较大。
链式栈无栈满问题,只要存储空间还有就可扩充。
链式栈的栈顶在链头,插入与删除仅在栈顶处执行。(前插法建立栈)
栈的混洗
当进栈元素的编号为1, 2, …, n时,可能的出栈序列有多少种
卡特兰数:1/(n+1)*C(2n,n)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vv4BxP3C-1631361137007)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20210106103722470.png)]
队列
队列是只允许在一端删除,在另一端插入的线性表
允许删除的一端叫做队头 (front),允许插入的一端叫做队尾 (rear)。
顺序队列
struct{
int elem[maxsize];
int rear, front;
};
队列与栈的共性在于它们都是限制了存取位置的线性表;区别在于存取位置有所不同。
进出队列的方案
1.先加元素再动指针(队尾指针指示实际队尾的后一位置,队头指针指示实际队头的位置)