线性表:
零个或多个数据元素的有限序列(注意:有限)
元素之间是有顺序的,第一个元素无前驱,最后一个元素没有后继,其他元素都由一个直接前驱和直接后继。
顺序存储结构:
用一段地址连续的存储单元依次存储线性表的数据元素。
#define MAXSIZE 20 // 数组的长度(固定不变)
typedef int ElemType // 将ElemType定义为int类型的
typedef struct
{
ElemType data[MAXSIZE]; // 存放元素的数组
int length; // 线性表的长度(表中数据元素的个数)
}Sqlist;
数据的获取:
ElemType getElem(Sqlist L, int i, ElemType *e)
{
if(L.length ==0 || i < 1 || i > L.length)
{
return null;
}
*e = L.data[i-1];
return *e;
}
数据的插入:
思想:插入位置后面的元素向后移动一位,然后插入新元素
insert(SqList L, int i, ElemType e)
{
int k;
if(L->length == MAXSIZE)
{
// 顺序线性表已经满了
}
if(i < 1 || i > L->length+1)
{
// i超出线性表范围
}
if(i<=L->length)
{
// 将元素后移一位
for(k = L->length; k >= i-1; k --)
{
L->data[k+1] = L->data[k];
}
}
L->data[i-1] = e;
L->length ++; // 注意:此时线性表的长度加1
}
数据的删除:
思想:返回删除位置的元素,并且后面的元素前移一位
delete(SqList L, int i, ElemType e)
{
int k;
if(L->length == 0)
{
// 顺序线性表为空
}
if(i < 1 || i > L->length)
{
// 删除位置不正确
}
*e = L->data[i-1];
if(i<L->length)
{
// 将元素前移一位
for(k = i; k < L->length; k ++)
{
L->data[k-1] = L->data[k];
}
}
L->length --; // 注意:此时线性表的长度减1
}
缺点:
- 顺序结构线性表的删除和插入需要移动大量的元素,耗费时间
- 线性表长度变化较大时,难以确定存储空间的容量
- 造成存储空间的“碎片”
优点:
- 无须为表中的元素间逻辑关系增加额外的存储空间
- 可以快速的存取表中任何位置的元素
链式存储结构:
用一组任意的存储单元存储线性表的数据元素,这组单元可以连续,也可以不连续
每个数据元素,存储数据元素信息和后继元素的存储地址
存储数据元素信息的域称为 数据域
存储直接后继位置的域称为 指针域
头指针:链表中第一个结点的存储位置
指向链表中第一个结点的指针,若有头节点,则是指向头结点的指针
头结点:链表的第一个结点前另外设置一个结点(数据域可以不存储信息,也可以存储链表的长度等其他信息)
typedof struct Node
{
ElemType data; // 数据域
struct Node *next; // 指针域
} Node;
typedof struct Node *LinkList;
单链表:只有一个指针域
单链表的读取:
思想:遍历链表找到指定元素
getElem(LinkList L, int i, ElemType *e)
{
int j;
LinkList p;
p = L->next;
j = 1; // 类似指针的存在
while(p && j < i) // 移动指针查找元素
{
p = p->next;
++ j;
}
if(!p || j > i)
{
// 不存在
}
*e = p->data;
}
单链表的插入和删除:
插入例如:结点a和b相连,插入结点c
打断a和b直接的联系,使得a.next=c, c.next=b
删除例如:结点a,b,c三个结点相连
删除b结点,使得a.next=c,b,next=null
insert(LinkList L, int i, ElemType *e)
{
int j;
LinkList p, s;
p = *L;
j = 1; // 类似指针的存在
while(p && j < i) // 移动指针查找元素
{
p = p->next;
++ j;
}
if(!p || j > i)
{
// 不存在
}
s = (LinkList) malloc(sizeof(Node)); // 生成新结点
// 插入结点
s->data = e;
s->next = p->next;
p->next = s;
}
delete(LinkList L, int i, ElemType *e)
{
int j;
LinkList p, q;
p = *L;
j = 1; // 类似指针的存在
while(p->next && j < i) // 移动指针查找元素
{
p = p->next;
++ j;
}
if(!(p->next) || j > i)
{
// 不存在
}
// 删除p后面的结点
q = p->next;
p->next = q->next;
*e = q->data;
free(q);
}
单链表和顺序存储的比较:
线性表需要频繁查找时,使用顺序存储
需要频繁插入时,使用单链表
如果表的容量不固定时,使用单链表
静态链表:
用数组描述的链表
数组的元素存放两个数据域data和cur
data:存放数据
cur:相当于next指针,存放后继元素的下标
循环链表:
单链表的终结点的指针指向头节点
双向链表:
单链表的每个结点中再设置一个指向其前驱结点的指针