双向链表所需要头文件
首先重定义类型名 意义我前几篇讲过几次了,这里就不在赘述了,(顺序表,单链表的开头都有说明)
然后我们需要一个结构体
结构体包含 : 存储数据的 a 指向一个节点的指针 next 指向上一个节点的指针 prve
双向链表实现的函数功能有 :1.申请动态内存空间 2.初始化 3.双向链表的头,尾的插入与删除,在合法的任意位置进行插入和删除 4.释放链表,5.打印链表
1.申请动态内存空间
因为进行插入和初始化需要用到此函数 所以我们要先实现此功能
1.先申请一个动态内存,并名为newnode
2.把x存入newnode存放数据的a中
3.把newnode的上,下指针都置为空
4.最后再返回此申请的空间
2.初始化
我们先创建一个链表的头 并且把上,下指针置空,并且返回链表的头
并且我们再命名一个plist,用来接收,之后穿参数的时候,就可以直接用pilst
3.双向链表的尾部插入
1.先创建一个指针 指向头的一个节点
2.申请一块内存 用来插入
因为双向链表的优势 ,我们不需要像单链表那样依次遍历找到最后一个节点,双向链表,表头,的上一个就是链表的最后一个节点
所以我们可以一步就找到表尾
尾插入原理:
找到表尾后,1.在表尾后再插入一个新数据(用next指针指向newnode(新数据)),2.把新数据指向原先的表尾(用prve指向原先的表尾)就完成了一次的连接
1.再让现在的表尾 (就是newnode(新数据))指向表头(newnode->next=phead(表头)) 2.(再让表头指向上一个的指针(就是prve)指向newnode(新数据))
这里可能会有点绕,需要读者慢慢理解,但是原理是简单的 ,就是让1个数据与它的上一个数据和下一个数据进行对接
4.双向链表的尾部删除
原理:
1.找到表尾,再找到表尾的上一个数据(倒数第二个数据)
2.然后让倒数第二个数据与表头对接,倒数第二个数据就成为了表尾数据,
3.再把原先的表尾数据进行释放
end就是最后一个节点,cur就是倒数第二个节点
1.让cur指向下一个的指针指向表头
2.把表头的指向上一个的指针指向cur
3.释放原先的表尾,并且把指针置空
读者要搞清楚 next 和prve的作用 再搞清楚end和cur的位置,最后运用他们进行对接
5.双向链表的头部插入
我们先找到两个位置 一个是表头 一个是表头的下一个节点(这个节点才是真正存放数据的节点,表头只是其带头作用,里面只是存放0,并且打印链表都是从表头的一个节点开始打印)
找到两个位置之后,创建一个动态内存 进行插入
接下来大致原理就是 让插入的数据与表头和表头的下一个节点,在他们两个中间插入(就是对接)
表头原先指向的是表头的下一个节点,1.现在我们让表头的指向下一个节点的指针指向插入的数据
2.让插入的数据指向上一个节点的指针指向表头 3.让插入的数据指向下一个节点的指针指向原先表头的下一个节点(进行对接)
6.双向链表的头部删除
头部删除也是删除表头的下一个节点
那么我们要找到两个位置 1.表头,2.表头的下一个节点的下一个节点(就是要删除的位置的下一个节点)
这里我们把表头命名为cur 把表头的下一个节点的下一个节点(就是要删除的位置的下一个节点)命名为end
1.然后我们让 cur和end进行对接
2.然后释放要删除数据
把cur指向下一个节点的指向指向end 把end指向上一个节点的指针指向cur
7.双向链表的查找
这里我们只需要从表头的下一个节点开始,一直到表尾,进行遍历,如果找到就返回改位置。如果知道表尾都没找到(就是循环结束的时候)那么我们就返回空(NULL)
然后我们用pos来接收
7.对pos的前一个位置进行插入数据
我们首先需要先对pos进行判断,如果找到了,才开始插入,若找不到,就不执行函数
因为是插入数据 所以我们要先申请一个内存
又因为我们已经得到了pos的位置,所以我们可以直接找到pos的上一个数据
原理在于找两个位置 一个是pos 一个是pos的上一个数据
pos我们已经有了它的位置 pos的上一个位置我们只需要用指针指向它就可以了
pos的上一个位置命名为end
然后让新数据在pos的上一个数据与pos中间进行插入(就是对接)
8.对pos这个位置进行删除
因为我们有了pos的位置
那么我们用指针 next和prve找到 pos的上一个位置(命名为cur),和pos的下一个位置(end)进行对接
然后再释放掉pos,为把pos置空
9.链表的打印
从表头的下一个数据开始,进行遍历,并依次打印
10.链表的释放
因为链表的内存是一块块申请的,所以我们要遍历,并且释放
先从找到表头的下一个节点(命名为end),再找到表头的下一个节点的下一个节点(命名为cur)
先释放表头的下一个节点,再把cur赋值给end,就此循环。就把表头以后的数据全释放完了,
最后把表头给释放,并且置空,就可以了。
双向链表的全部函数功能就实现好了!
之所以双向链表传参不用(&)取地址 是因为SL* plist=ListCreate(); 这里是用指针接收,指针接收的就是地址,我们传参数的时候就是用地址传入了。
不用二级指针是因为我们不需要改变这个双向链表的哨兵位,只是改变它的指向,这个哨兵位是不会变的(不管头插、尾插、头删、尾删)
而单链表的头插,头删是会改变这个头的,比如(头删)释放这个头,让下一个节点成为头,这些都是会改变头的,所以我们要传二级指针。
接下来是代码段 ,需要自取。
#include
#include
#include
typedef int STDataType;
typedef struct linode
{
STDataType a;
struct linode* next;
struct linode* prve;
}SL;
SL* Bylist(STDataType x)//申请动态内存
{
SL* newnode = (SL*)malloc(sizeof(SL));
newnode->a = x;
newnode->next = NULL;
newnode->prve = NULL;
return newnode;
}
SL* ListCreate()//初始化
{
SL* phead = Bylist(0);
phead->next = phead;
phead->prve = phead;
return phead;
}
void ListDestory(SL* pHead)//释放链表
{
SL* end = pHead->next;
while (end != pHead)
{
SL* cur = end->next;
free(end);
end = cur;
}
free(pHead);
pHead = NULL;
}
void ListPrint(SL* pHead)//打印链表
{
SL* end = pHead->next;
while (end != pHead)
{
printf("%d->", end->a);
end = end->next;
}
printf("phead");
}
void ListPushBack(SL* pHead, STDataType x)//尾插
{
SL* end = pHead->prve;
SL* newnode = Bylist(x);
//因为双向链表的优势 所以我们不需要依次遍历,头的上一个就是最后一个节点
end->next = newnode;
newnode->prve = end;
newnode->next = pHead;
pHead->prve = newnode;
}
void ListPopBack(SL* pHead)//尾删
{
SL* end = pHead->prve;//找到最后一个节点
SL* cur = end->prve;//最后一个节点的上一个
cur->next = pHead;
pHead->prve = cur;
free(end);
end = NULL;
}
void ListPushFront(SL* pHead, STDataType x)//头插
{
SL* end = pHead;
SL* cur = end->next;
SL* newnode = Bylist(x);
end->next = newnode;
newnode->prve = end;
newnode->next = cur;
cur->prve = newnode;
}
void ListPopFront(SL* pHead)//头删
{
SL* cur = pHead->next;
SL* end = cur->next;
pHead->next = end;
end->prve = pHead;
free(cur);
}
SL* ListFind(SL* pHead, STDataType x)//查找
{
SL* cur = pHead->next;
while (cur != pHead)
{
if (cur->a == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void ListInsert(SL* pos, STDataType x)//插入
{
SL* newnode = Bylist(x);
SL* end = pos->prve;
end->next = newnode;
newnode->prve = end;
newnode->next = pos;
pos->prve = newnode;
}
void ListErase(SL* pos)//删除
{
SL* cur = pos->prve;
SL* end = pos->next;
cur->next = end;
end->prve = cur;
free(pos);
pos - NULL;
}
void Intenode()
{
// 创建返回链表的头结点.
SL* plist= ListCreate();
// 双向链表尾插
ListPushBack(plist, 1);
ListPushBack(plist, 2);
ListPushBack(plist, 3);
ListPushBack(plist, 4);
// 双向链表尾删
ListPopBack(plist);
// 双向链表头插
ListPushFront(plist, 5);
ListPushFront(plist, 6);
ListPushFront(plist, 7);
ListPushFront(plist, 8);
// 双向链表头删
ListPopFront(plist);
// 双向链表查找
SL* pos=ListFind(plist, 5);
if (pos)
{
// 双向链表在pos的前面进行插入
ListInsert(pos, 50);
}
// 双向链表删除pos位置的节点
ListErase(pos);
// // 双向链表打印
ListPrint(plist);
// // 双向链表销毁
ListDestory(plist);
}