静态链表、循环链表和双向链表
1.静态链表
早期语言如Basic,由于没有指针,链表结构无法实现,就有人想出来用数组代替指针描述单链表。
数组的元素都是两个数据域组成,data和cur,即数组的每个下表都对应一个data和一个cur。数据域data,用来存放数据元素;cur相当于单链表中的next指针,用来存放该元素的后继在数组中的下标,cur又叫游标。
我们把这种用数组来描述的链表称为静态链表。
1.1静态链表的实现
静态链表的实现方法:
- 未被使用的数组元素称为备用链表
- 数组的第一个元素不存储数据,cur存放备用链表的第一个结点的下标
- 数组最后一个元素也不存储数据,cur存放第一个有数值元素的下标,起头结点作用。
1.2静态链表的插入操作:
如:在甲、乙、丁、戊的乙和丁中间插入丙
- 将丙插入到备用链表第一个元素,cur改为乙原来的cur值
- 乙的cur值改为数组第一个元素的cur值(即原来备用链表第一个元素的下标)
- 将第一个元素的cur值加1
1.3静态链表的删除操作
如:已有甲、乙、丙、丁、戊,现在甲要走
- 甲空出来,甲现在作为备用链表第一个元素,cur指向原来备用链表第一个元素下标
- 数组最后一个元素cur指向乙的下标。
- 数组第一个元素的cur指向原来甲的下标。
1.4静态表的优缺点
- 优点:在插入和删除时,只需要修改游标,不需要移动元素,从而改进了在顺序存储结构中插入和删除操作需要移动大量元素的缺点。
- 没有解决连续存储分配带来的表长难以确定的问题且失去了顺序存储结构随机存取的特性(即存取时间复杂度为O(1))
2.循环链表
循环链表就是单链表最后一个元素的指针不指向空而指向虚拟头节点的地址。(跟头指针一样)
循环链表和单链表的主要差异就在于循环的判断条件上,原来是判断p->next是否为空,现在是p->next是否为虚拟头节点。
3.双向链表
双向链表是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。
3.1双向链表的结构定义
跟单链表类似,双向链表的结构定义如下:
//单链表结构体
class DulList{
public:
//链表结点结构体
struct DulNode{
int val;
DulNode* prior;
DulNode* next;
DulNode(int val):val(val),next(nullptr){}
};
//默认构造函数
DulList(){
_dummyHead
}
//读取第i个元素的值,类内声明,类外实现
int get(int i);
//在第i个结点前面插入元素e
void insert(int i,int val);
//删除第i个结点
void erase(int i);
//头插法
void push_front(int val);
//尾插法
void push_back(int val);
private:
int _size; //链表长度
DulNode* _dummyHead; //虚拟头节点
};
3.2双向链表的插入
假设存储元素e的结点为s,要实现将结点s插入到结点p和p->next之间需要以下几步:
- 将前的地址赋值给中的前驱
- 将后的地址赋值给中的后继
- 将中的地址赋值给后的前驱
- 将中的地址赋值给前的后继
即:
s->prior=p;
s->next=p->next;
p->next->prior=s;
p->next=s;
3.3双向链表的删除
若要删除结点p,需要执行以下两个步骤:
- 将后的地址赋值给前的后继
- 将前的地址赋值给后的前驱
- 释放中的内存
//伪代码实现为
p->prior->next=p->next;
p->next->prior=p->prior;
free(p);