数据结构
数据结构的基本概念
基本的数据结构
线性结构
线性表
定义与特点
线性表是具有相同特性的数据元素的一个有限序列
专业术语:直接前驱、直接后继、线性起点、终端终点
线性表(a1, a2, a3, … ,ai,…an)
其中a(i-1)是ai的直接前驱,a(i+1)是直接后继,a1是线性表的线性起点,an是线性表的终端终点
n为元素总个数,即表长
n=0时称为空表
例如:字母表:数据元素都是字母,元素间关系是线性的;学生情况登记表;12星座
特点:同一线性表中的元素必定具有相同特性,数据元素间的关系是线性关系
案例引入
eg1:
一元多项式的运算P(x) = 4 + 3x + 5x ^2
相加可找到R = (p0+q0, p1+q1, … pn+qn)
eg2:
稀疏多项式S(x) = 1 + 3x ^10000 + 2x ^20000
若是按照eg1存储则将造成浪费
线性表P = ((p1, e1), …, (pn,en))pn:系数 en:指数
eg3:稀疏多项式的和
线性表A = ((7,0), (3,1), (9, 8), (5,17))
线性表B = ((8,1), (22,7), (-9,8))
- 创建一个新数组c
- 从头遍历比较a和b的每一项
- 指数相同 2)指数不相同
- 遍历完毕,将另一个剩余项一次复制到c中
eg4:图书信息管理系统
顺序表类型定义
基本操作
InitList(&L);
DestoryList(&L)(内存都无);
ClearList(&L)(为空表);
ListEmpty(L);
ListLength(L);
GetElem(L,i,&e);
LocateElem(L,e,compare())返回线性表L中第一个与e满足compare()的数据元素的位序,若无,返回0;
PriorElem(L,cur_e, &pre_e)cur_e是L的数据元素,且不是第一个,用pre_e返回它的前驱;
NextElem(L,cur_e, &next_e);
ListInsert(&L,i,e) 1<=i<=ListLength(L)+1,在L的第i个位置插入新数据元素e,L的长度加1;
ListDelete(&L,i,&e);
ListTraverse(&L,visited())
顺序存储的表示和实现
定义:把逻辑上相邻的数据元素存储在物理上相邻的存储单元中
依次存储,没有空出的存储单元(随机存取)
-
线性表第一个数据元素的存储位置,称为线性表的起始位置或基地址
-
顺序表:地址连续、依次存放、随机存放、类型相同
-
与数组相似,但是线性表长可变,数组长度不可动态定义,可以是int a[10];int a[]; int a[SIZE];int a[2+2];
-
顺序表的定义
①typedef struct {
ElemType elem[MaxSize]; //存放空间的基地址,静态分配
int length;
}SqList; //顺序表类型②typedef struct {
ElemType * elem; //存放空间的基地址,动态分配
int length;
}SqList; //顺序表类型 -
平均查找长度ASL,即期望值
-
实现LinkInsert的ASL = n/2,为O(n),因为每个数据元素插入的概率总为1/(n+1),插入在位置L.length时,移动0次;插入在L.length-1,移动1次,…,因此ASL = 1/(n+1)*(1+2+…+n) = n/2
-
实现ListDelete的ASL = n-1/2,因为每个数据元素被删除的概率总为1/n
-
优点:任一元素均可随意存取
缺点:在插入、删除元素时,需要移动大量元素;浪费存储空间;属于静态存储形式,数据元素的个数不能随意扩充
类C语言
引用变量:int &j = i;//j是一个引用类型,代表i的一个替代名,i改变时,j也跟着改变
可实现的代码;int i = 5; int &j = i; int k = j;//5 5 5
内存中是没有为j分配空间的,而是直接对i进行操作
链式存储的表示和实现
用一组物理位置任意的存储单元来存放线性表的数据元素,逻辑顺序和物理顺序不一定相同(顺序存取)
有关术语:节点、链表
- 单链表:结点只一个指针域的链表
- 双链表:结点有两个指针域的链表
- 循环链表:首尾相接的链表
- 头指针:是指向链表中第一个结点的指针
- 首元结点:链表中第一个数据元素的结点
- 头结点:首元结点之前的结点,不是存储的第一个元素
链表的存储结构:带头结点(头指针指向头结点)+不带头结点(头指针指向首元结点) - 表示空表:①无头结点的:头指针为空②带头结点的:头结点的指针域为空
- 头结点的优点:便于对首元结点的处理;便于空表、非空表的处理
- 访问和操作时只能通过头指针进入链表
- 单链表的定义:
typedef struct Lnode {
ElemType data;
struct Lnode * next;
}Lnode, *LinkList;
typedef struct {
char num[8];
char name[8];
int score;
}ElemType; - 变量的定义:①头结点LinkList L; ②其他结点:Lnode *p;
三种链表(单链表,双向链表,循环链表)
顺序表和链式表的比较
- 链式存储结构
优点:1. 结点空间可以动态申请和释放 2.插入和删除数据元素不需要移动数据元素
缺点:1. 存储密度小(存储数据本身占用的空间/结点占用的空间容量) 2. 非随机存取,对任一结点的操作需要依照指针找到该结点
线性表的应用
线性表的合并
如La=(7,5,3,11)Lb=(2,6,3) ——→ La = (7,5,3,11,2,6),即A∪B
算法步骤:union(&La,Lb)
依次取出Lb的每个元素
- 在La中查找该元素
- 如果找不到,则将其插入La的末尾
有序表的合并
将已知两个非递减的序列La和Lb,要求Lc的数据元素为La和Lb按照非递减有序排列
顺序表算法步骤:
- 创建一个空表Lc
- 依次从La和Lb中选择元素值较小的结点插入到Lc的后面,直至其中一个表变空为止
- 继续将剩下的另一个顺序表的剩余结点插入在Lc的最后
顺序表的实现:时间复杂度O(ListLength(La)+ListLength(Lb)) 空间复杂度Lc 即O(ListLength(La)+ListLength(Lb))
链表实现:
pa = La->next; pb = Lb->next;
pc = Lc = La;
while (pa && pb) {
if(pa->data <= pb->data) {
pc->next = pa;
pc = pa;
pa = pa->next;
}
else {
pc->next = pb;
pc = pb;
pb = pb->next;
}
}
pc->next = pa?pa:pb; //(?前面的作为条件而赋值运算符优先级数最低)
delete Lb;
时间复杂度O(ListLength(La)+ListLength(Lb)),空间复杂度O(1)
稀疏多项式
线性表A = ((7,0), (3,1), (9, 8), (5,17))
线性表B = ((8,1), (22,7), (-9,8))
- 创建一个新数组c
- 从头遍历比较a和b的每一项
- 指数相同 2)指数不相同
- 遍历完毕,将另一个剩余项一次复制到c中
顺序存储结构存在问题:1.存储空间分配不灵活;运算的空间复杂度高(要为c分配不确定长的空间)
链式存储结构较好