目录
一、定义;
带头双向循环列表,结构较复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环列表。虽然结构复杂,但是该结构会使程序实现简单方便。
- 下图可以看到,它有一个不存储数据的头节点,每个节点中有三个值,一个是prev指针,一个是数据域data,和一个next指针改变。
- 其中头结点的prev指向尾节点,尾结点的next指向头节点,形成了循环
- 其余节点的prev指向上一个节点,next指向下一个节点,构成了双向
- 当链表只有头结点时,头结点的prev指针与next指针都指向head本身。这一特点使得在插入与删除结点时,不会出现对空指针取值问题,且头结点始终存在,因此指向头结点的指针始终不变。不需再像单链表操作时需要改变头指针的值(传递头指针地址进入函数内)。
二、程序实现
1、结构体代码
SeqList* SeqListCreatNode(int data)//创建结点
{
SeqList* newone=(SeqList*)malloc(sizeof(SeqList));
newone->data=data;
newone->prev=NULL;//新创建结点指针设为NULL;
newone->next=NULL;
return newone;
}
2、初始化头结点
定义一个指向头结点的指针phead。
SeqList* SeqListNodeInit(int data)//头结点初始化
{
SeqList* phead=SeqListCreatNode(data);//创建一个指向头结点的指针phead
phead->next=phead; //头结点内指针指向自身
phead->prev=phead;
return phead;
}
int main()
{
//初始化头结点。先创建再初始化
SeqList* phead=SeqListNodeInit(0);
SeqListPushBack(phead,1);
SeqListPushBack(phead,2);
SeqListPushBack(phead,3);
SeqListPushFront(phead,4);
SeqListPopBack(phead);
SeqListPopFront(phead);
SeqListShow(phead);
}
3、函数定义
typedef struct SeqList SeqList;
SeqList* SeqListCreatNode(int data);//创建结点
SeqList* SeqListNodeInit(int data);//头结点初始化
void SeqListPushBack(SeqList* phead,int data);//尾插函数
void SeqListPushFront(SeqList* phead,int data);//头插函数
void SeqListPopBack(SeqList* phead);//尾删函数
void SeqListPopFront(SeqList* phead);//头删函数
void SeqListShow(SeqList* phead);//显示函数
4、尾插函数
void SeqListPushBack(SeqList* phead,int data)//尾插函数
{
SeqList* newone=SeqListCreatNode(data);
SeqList* temp=phead->prev;
temp->next=newone;
newone->prev=temp;
phead->prev=newone;
newone->next=phead;
}
5、头插函数
void SeqListPushFront(SeqList* phead,int data)//头插函数
{
SeqList* newone=SeqListCreatNode(data);
SeqList* temp=phead->next;
phead->next=newone;
newone->prev=phead;
temp->prev=newone;
newone->next=temp;
}
6、尾删函数
void SeqListPopBack(SeqList* phead)//尾删函数
{
if(phead->prev==phead){
printf("无数据可删除");
return;
}
else{
SeqList* temp=phead->prev;
phead->prev=temp->prev;
temp->prev->next=phead;
free(temp);
temp=NULL;
}
}
7、头删函数
void SeqListPopFront(SeqList* phead)//头删函数
{
if(phead->next==phead){
printf("无数据可删除");
return;
}
else{
SeqList* temp=phead->next;
phead->next=temp->next;
temp->next->prev=phead;
free(temp);
temp=NULL;
}
}
三、释放内存问题
销毁链表时,需要遍历所有要释放的结点,依次free,最后再释放头结点
最后将phead置为空指针代表链表为空,即销毁完成。
四、双向循环链表与单链表之间的差异
1、单链表
(1)单链表不能从后面往前。
(2)找不到它的前驱(上一个地址)(尾插,尾删,中插,中删)都要找到它的前一个节点。
(3)没有带头的节点:要用二级指针进行传参,不用改变传过来指针。
2、双向循环链表
(1)带头节点的好处:不存储有效数据,带哨兵位的头节点不存入链表的长度,使得尾插更加方便,每次都在头后进行连接。不需要传递头指针的地址(二级指针)。
(2)双向的好处:方便找到他的前一个节点。
(3)循环的好处:头指向尾,尾指向头,头的前一个节点就是尾,方便找尾节点。