双向链表所需要头文件
首先重定义类型名 意义我前几篇讲过几次了,这里就不在赘述了,(顺序表,单链表的开头都有说明)
然后我们需要一个结构体
结构体包含 : 存储数据的 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 <stdio.h> #include <stdlib.h> #include <assert.h> 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); }