1.双向链表的定义
单向链表特点:
1.我们可以轻松的到达下一个节点, 但是回到前一个节点是很难的.
2.只能从头遍历到尾或者从尾遍历到头(一般从头到尾)
双向链表特点
1.每次在插入或删除某个节点时, 需要处理四个节点的引用, 而不是两个. 实现起来要困难一些
2.相对于单向链表, 必然占用内存空间更大一些.
3.既可以从头遍历到尾, 又可以从尾遍历到头
双向链表的定义:
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。下图为双向链表的结构图。
从上中可以看到,双向链表中各节点包含以下 3 部分信息:
指针域:用于指向当前节点的直接前驱节点;
数据域:用于存储数据元素。
指针域:用于指向当前节点的直接后继节点;
双向循环链表的定义:
双向链表也可以进行首尾连接,构成双向循环链表,如下图所示
在创建链表时,只需要在最后将收尾相连即可(创建链表代码中已经标出)。其他代码稍加改动即可。
2.双向链表的实现以及基本操作
1.双向链表的结构体定义
2.双向链表的初始化
3.双向链表之创建新结点
4.双向链表的打印
5.双向链表的尾插
6.双向链表的尾删
7.双向链表的头插
8.双向链表的头删
9.双向链表的查找
10.双向链表之在ptr位置之前插入
11.双向链表之删除ptr位置
12.双向链表的销毁
//双链表
#include<stdio.h> //scanf
#include<stdlib.h> //malloc
#include<assert.h>
//malloc时动态内存分配函数,用于申请一块连续的指定大小的
//内存块区域以void*类型返回分配的内存区域地址
//typedef为一种数据类型定义一个新名字,这里的数据类型包括内部数据类型
//(int,char等)和自定义的数据类型struct
typedef int ElemType;
typedef struct DuNode //双
{
struct DuNode* prev; //前驱
struct DuNode* next; //后继
ElemType data; //数据
}DuNode, * PDuNode; //结点类型 指针类型
typedef struct
{
PDuNode head; //指向头节点
int cursize; //整个链表里面的数据结点的个数
}DuLinkList; //双循环链表结构
DuNode* Buynode(DuNode* parg = nullptr, DuNode* narg = nullptr) //购买一个结点 parg代表前驱 narg代表后继
{
DuNode* s = (DuNode*)malloc(sizeof(DuNode*));
if (nullptr == s) exit(1); // .c NULL
s->prev = parg == nullptr ? s : parg;
s->next = narg == nullptr ? s : narg;
return s;
}
void InitList(DuLinkList* plist)
{
assert(plist != nullptr);
plist->cursize = 0;
plist->head = Buynode();
}
void Insert(DuLinkList* plist, DuNode* ptr, ElemType val) //购买一个结点S 在PTR之前插入一个结点s
{
assert(plist != nullptr && ptr != nullptr);
ptr->prev = Buynode(ptr->prev, ptr);
DuNode* s = ptr->prev;
s->prev->next = s;
s->data = val;
plist->cursize += 1;
#if 0
DuNode* s = Buynode();
s->data = val;
s->next = ptr; //s的next指向ptr 1
s->prev = ptr->prev; //s的prev指向12(原ptr的prev 23的prev) 2
ptr->prev = s; // ptr的prev指向s 3
s->prev->next = s; // 4
plist->cursize++;
#endif
}
void Push_Front(DuLinkList* plist, ElemType val)
{
assert(plist != nullptr);
Insert(plist,plist->head->next, val);
}
void Push_Back(DuLinkList* plist, ElemType val)
{
assert(plist!= nullptr);
Insert(plist, plist->head, val);
}
void Earse(DuLinkList* plist, DuNode* ptr) { //删除指定结点 ptr
assert(plist != nullptr && ptr != nullptr);
ptr->next->prev = ptr->prev;
ptr->prev->next = ptr->next;
free(ptr);
plist->cursize -= 1;
}
void Pop_Front(DuLinkList* plist)
{
assert(plist != nullptr);
Earse(plist, plist->head->next);//删掉第一个数据结点 plist->head->next
}
void Pop_Back(DuLinkList* plist) //尾删
{
assert(plist != nullptr);
Earse(plist, plist->head->prev);
}
int GetSize(DuLinkList* plist) //获取链表的结点个数
{
assert(plist != nullptr);
return plist->cursize;
}
bool IsEmpty(DuLinkList* plist) //判空操作
{
assert(plist != nullptr);
return GetSize(plist) == 0;
}
void Clear(DuLinkList* plist) {
while (plist != nullptr) {
Earse(plist, plist->head->next);
free(plist->head->next);
}
}
void DestoryList(DuLinkList* plist)
{
assert(plist != nullptr);
Clear(plist);
free(plist->head);
plist->head = nullptr;
}
void Print(DuLinkList* plist) //打印链表
{
assert(plist != nullptr);
DuNode* p = plist->head->next;
while (p != plist->head)
{
printf("%d", p->data);
p = p->next;
}
printf("/n");
}
DuNode* FindValue(DuLinkList* plist, ElemType val) //找value
{
assert(plist != nullptr);
DuNode* p = plist->head->next;
while (p != plist->head && p->data != val);
{
p = p->next;
}
if (p == plist->head)
{
p = nullptr;
}
return p;
}
int main()
{
DuLinkList mylist;
InitList(&mylist); //初始化
for (int i = 0; i < 10; ++i)
{
Push_Back(&mylist, i);
Print(&mylist);
}
while (!IsEmpty(&mylist))
{
Pop_Back(&mylist);
Print(&mylist);
}
return 0;
}
注:
1).insert插入
2)删除指定结点