一、线性表的定义和基本操作
①线性表的定义

②线性表的基本操作

③小结

二、顺序表的定义
①顺序表的定义

②顺序表的实现------静态分配

例:

③顺序表的实现------动态分配

例:

cpp
// 为顺序表的动态数组申请内存:
// InitSize是默认最大长度,sizeof(int)是单个int元素的字节数,两者相乘得到总申请字节数
// (int *)是将malloc返回的void*指针强制转换为int*类型,适配data的指针类型
L.data = (int *)malloc (InitSize * sizeof(int));
// 为顺序表的动态数组扩容:
// L.MaxSize是当前最大容量,len是要增加的长度,两者相加是扩容后的总容量
// 用malloc申请新的连续内存空间,大小为"新总容量 × 单个int字节数"
// 同样将void*强制转为int*,赋值给L.data以指向新内存区域
L.data = (int *)malloc ((L.MaxSize + len) * sizeof(int));
④小结


三、顺序表的插入删除
①顺序表插入

②顺序表插入的健壮性

③顺序表插入的时间复杂度

④顺序表删除

⑤顺序表删除的时间复杂度

⑥小结

四、顺序表的查找
①按位查找


②按位查找的时间复杂度

③按值查找

cpp
//结构类型数据元素
// 定义结构类型(学生:学号+姓名)
typedef struct {
int id; // 学号
char name[20];// 姓名
} Student;
// 顺序表的结构(数据元素是Student类型)
typedef struct {
Student *data; // 动态数组指针
int MaxSize; // 最大容量
int length; // 当前长度
} SeqList;
// 在顺序表L中,查找第一个与e(Student类型)匹配的元素,返回位序(无则返回0)
int LocateElem(SeqList L, Student e) {
for (int i = 0; i < L.length; i++) {
// 结构类型:需逐个比较成员(学号+姓名都相同,才认为元素相等)
if (L.data[i].id == e.id && strcmp(L.data[i].name, e.name) == 0) {
return i + 1; // 返回位序(从1开始)
}
}
return 0; // 未找到
}
④按值查找的时间复杂度

⑤小结

五、单链表的定义
①用代码定义一个单链表

cpptypedef struct LNode{ ElemType data; struct LNode *next; }LNode,*LinkList;
typedef struct LNode:给一个叫struct LNode的 "结构体" 起个别名(方便后面用)。struct LNode里包含 2 个东西:
ElemType data:存节点的数据(比如数字、字符,ElemType是个 "占位符",实际用的时候会换成具体类型,比如int);struct LNode *next:定义一个指针变量,变量名是 next,指向下一个节点(这样多个节点就能像链条一样连起来)。}LNode, *LinkList;:
- 把
struct LNode的别名定为LNode(以后写LNode就等于写struct LNode);- 同时定义
*LinkList,它是 "指向LNode的指针" 的别名(以后用LinkList可以直接表示链表的头指针)
②不带头结点的单链表

③带头结点的单链表

④小结

六、单链表的插入和删除
①按位序插入(带头结点)

②按位序插入(不带头结点)

③指定结点的后插操作

④指定结点的前插操作

⑤按位序删除(带头结点)

cpp
//不带头结点
// 链表结点结构(和带头结点一致)
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
// 不带表头结点的按位序删除函数
bool ListDelete(LinkList &L, int i, ElemType &e){
if(i < 1) // 位序i不合法(位序从1开始)
return false;
LNode *p, *q;
// 特殊情况:删除第1个结点(头指针会改变)
if(i == 1){
if(L == NULL) // 链表为空,无结点可删
return false;
q = L; // q指向要删除的第1个结点
e = q->data; // 用e返回被删结点的值
L = L->next; // 头指针指向原第2个结点(更新链表头)
free(q); // 释放被删结点的空间
return true;
}
// 处理i>1的情况:找到第i-1个结点
p = L; // p从第1个结点开始遍历
int j = 1; // j记录p当前指向的是第j个结点
// 循环找到第i-1个结点(要删第i个,需操作其前驱)
while(p != NULL && j < i-1){
p = p->next;
j++;
}
if(p == NULL) // 没找到第i-1个结点(i超过链表长度)
return false;
if(p->next == NULL) // 第i-1个结点后无结点(无法删除第i个)
return false;
q = p->next; // q指向要删除的第i个结点
e = q->data; // 用e返回被删结点的值
p->next = q->next; // 将*q从链中"断开"
free(q); // 释放被删结点的空间
return true;
}
⑥指定结点的删除

⑦小结

七、单链表的查找
①按位查找

②按值查找

③求表的长度

cpp
//不带头结点
// 链表结点结构(和带头结点一致)
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
// 不带头结点的求表长度函数
int Length(LinkList L){
int len = 0; // 统计数据结点的个数
LNode *p = L; // p从第一个数据结点开始遍历(L直接指向首个数据结点)
while (p != NULL){ // 只要当前结点存在(是数据结点),就计数
len++;
p = p->next; // 移动到下一个结点
}
return len;
}
④小结

八、单链表的建立
①尾插法

cpp
//不带头结点的单链表尾插法代码
LinkList List_TailInsert_NoHead(LinkList &L){ // 不带头结点正向建立单链表
int x;
L = NULL; // 初始为空表(不带头结点,L直接指向首节点)
LNode *s, *r = NULL; // r为表尾指针,初始无节点
scanf("%d", &x); // 输入节点的值
while(x != 9999){ // 输入9999表示结束
s = (LNode *)malloc(sizeof(LNode)); // 分配新节点空间
s->data = x; // 新节点存数据x
if(L == NULL){ // 处理第一个节点:L指向首个节点
L = s;
} else { // 非第一个节点:接在当前表尾r之后
r->next = s;
}
r = s; // 保持r指向最新的表尾节点
scanf("%d", &x); // 继续输入下一个值
}
if(r != NULL){ // 若链表非空,尾节点指针置空
r->next = NULL;
}
return L;
}
②头插法

cpp
//不带头结点的单链表头插法代码
LinkList List_HeadInsert_NoHead(LinkList &L){ // 不带头结点逆向建立单链表
int x;
L = NULL; // 初始为空表(不带头结点,L直接指向首节点)
LNode *s;
scanf("%d", &x); // 输入节点的值
while(x != 9999){ // 输入9999表示结束
s = (LNode *)malloc(sizeof(LNode)); // 创建新节点
s->data = x; // 新节点存数据x
s->next = L; // 新节点next指向"当前链表的头"(首次插入时L为NULL)
L = s; // L更新为"新的链表头"
scanf("%d", &x); // 继续输入下一个值
}
return L;
}
③小结

cpp
//带头结点的单链表逆置
LinkList ReverseList_WithHead(LinkList L){ // 带头结点的链表逆置
LNode *p = L->next; // p指向原链表的第一个节点
L->next = NULL; // 原头结点置空(作为新链表的头)
LNode *s; // 临时指针存当前节点
while(p != NULL){ // 遍历原链表所有节点
s = p; // 取出当前节点
p = p->next; // p提前后移(避免后续操作断链)
// 头插:将s插入新链表的头结点之后
s->next = L->next;
L->next = s;
}
return L; // 返回逆置后的链表头
}
//不带头结点的单链表逆置
LinkList ReverseList_NoHead(LinkList L){ // 不带头结点的链表逆置
LNode *p = L; // p指向原链表的第一个节点
LinkList newL = NULL; // 新链表初始为空
LNode *s; // 临时指针存当前节点
while(p != NULL){ // 遍历原链表所有节点
s = p; // 取出当前节点
p = p->next; // p提前后移
// 头插:将s插入新链表的头部
s->next = newL;
newL = s; // newL更新为新的链表头
}
return newL; // 返回逆置后的链表头
}
九、双链表
①初始化

②插入

③删除

④遍历

⑤小结

十、循环链表
①循环单链表

②循环双链表初始化

③循环双链表插入

④循环双链表删除

⑤小结

十一、静态链表
①什么是静态链表

②用代码定义一个静态链表


③基本操作


④小结

十二、顺序表和链表的比较
