1.双链表的概念
(1)结点
在讲双链表的结构之前,我们先来思考一个问题。我们知道,单链表的结构,使得我们可以在堆中随时分配内存来满足我们的需要,且根据头指针的值,我们可以通过每个结点的指针域遍历整个链表。但是这样的遍历也存在一定的问题,即每个结点只包含指向下一结点的指针域,这就导致我们只能从前往后遍历链表,为了解决这个问题,我们便可以使用双向链表。
与单链表类似,双链表的基本单位也是结点,但是每个结点比起单链表多增加了一个指针域,即指向前一结点的指针域,这也就解决了单链表无法倒序遍历的问题。
如下所示,是双链表的结点结构示意图:
图1. 双链表的结点结构
(2)双链表
与单链表类似,双链表同样也是通过每个结点的前驱指针和后继指针将这些结点进行连接。但是与单链表不同的是,每个结点都存有指向其前驱结点的指针。
打个比方,单链表好比n个散开的房间,每个房间有指向下一房间的指示牌,且方向不可逆。双链表则在每个房间增加了一个指向前一房间的指示牌,这也就表示我们通过指示牌可以前往任意房间。
如下为双链表的结构示意图:
图2. 双链表的结构
2.双链表的操作
(1)结点和头指针的定义
typedef struct NODE
{
int BufferData; //数据域
struct NODE* Previous; //前驱指针
struct NODE* Next; //后继指针
}Node,*PNode;
PNode __ListHead = NULL; //头指针
(2)插入结点
① 头插法
图3. 双链表的头插法
void FrontInsert(int BufferData)
{
//申请结点,并初始化内存
PNode v1 = new Node;
v1->BufferData = BufferData;
v1->Next = NULL;
v1->Previous = NULL;
//如果链表为空
if (__ListHead == NULL)
{
__ListHead = v1;
}
else
{
//执行头插操作
v1->Next = __ListHead;
__ListHead->Previous = v1;
__ListHead = v1;
}
}
② 尾插法
图4. 双链表的尾插法
void TailInsert(int BufferData)
{
//申请内存并初始化
PNode v1 = new Node;
v1->BufferData = BufferData;
v1->Next = NULL;
v1->Previous = NULL;
//如果链表为空,直接插入
if (__ListHead == NULL)
{
__ListHead = v1;
}
else
{
//执行尾插操作
PNode v2 = __ListHead;
while (v2->Next != NULL)
{
v2 = v2->Next;
}
v2->Next = v1;
v1->Previous = v2;
}
}
③ 随机插入
图5. 双链表的随机插入
void MediumInsert(int Position, int BufferData)
{
//申请内存并初始化
PNode v1 = new Node;
//获得链表的长度
int ListLength = GetLength();
v1->BufferData = BufferData;
//如果插入位置不正确,返回错误,并释放该结点
if (Position<=0 || Position>ListLength + 1)
{
delete v1;
printf("The Insert Position Is illegal!\r\n");
}
else
{
//如果插在第一个位置,调用头插法
if (Position == 1)
{
FrontInsert(BufferData);
}
//调用尾插法
else if (Position == ListLength + 1)
{
TailInsert(BufferData);
}
//在随机两个结点中间插入
else
{
//首先获得待插入位置的前一结点,方便插入操作
PNode v2 = GetPreNode(Position);
v1->Next = v2->Next;
v1->Next->Previous = v2;
v2->Next = v1;
v1->Previous = v2;
}
}
}
(3)查找结点
int FindData(int BufferData)
{
PNode v1 = __ListHead;
int Position = 1;
while (v1 != NULL)
{
//遍历该链表,查找该值是否存在
if (v1->BufferData == BufferData)
{
//查找成功,返回位置
printf("Find %d Success!The Position is %d\r\n", BufferData, Position);
return Position;
}
else
{
//后移,同时位置递增
v1 = v1->Next;
Position++;
}
}
if (v1 == NULL)
{
//表示遍历完该链表,并未查找到此值,返回失败
printf("Find %d Fail!\r\n", BufferData);
return 0;
}
}
(4)删除结点
void DeleteNode(int BufferData)
{
//获得待删结点的位置
int Position = FindData(BufferData);
if (Position == 0)
{
printf("Delete %d Fail!", BufferData);
}
//通过位置获得待删结点的前一结点
else
{
PNode v1 = GetPreNode(Position);
PNode v2 = v1->Next;
//删除结点
v1->Next = v2->Next;
v2->Next->Previous = v1;
delete v2;
printf("Delete %d Success!\r\n", BufferData);
}
}
(5)遍历链表`
void TravelList()
{
PNode v1 = __ListHead;
while (v1!= NULL)
{
printf("%d ", v1->BufferData);
v1 = v1->Next;
}
printf("\r\n");
}
3.参考代码
#include<Windows.h>
#include<tchar.h>
#include <iostream>
using namespace std;
typedef struct NODE
{
int BufferData;
struct NODE* Previous;
struct NODE* Next;
}Node,*PNode;
PNode __ListHead = NULL;
//获得指定位置的前一个结点,用于插入结点和删除结点等操作
PNode GetPreNode(int Position)
{
PNode v1 = __ListHead;
for (int i = 1; i < Position -1 ; i++)
{
v1 = v1->Next;
}
return v1;
}
//获得链表长度(结点个数)
int GetLength()
{
PNode v1 = __ListHead;
int Length = 1;
while (v1->Next != NULL)
{
v1 = v1->Next;
Length++;
}
return Length;
}
//头插法
void FrontInsert(int BufferData)
{
//申请结点,并初始化内存
PNode v1 = new Node;
v1->BufferData = BufferData;
v1->Next = NULL;
v1->Previous = NULL;
//如果链表为空
if (__ListHead == NULL)
{
__ListHead = v1;
}
else
{
//执行头插操作
v1->Next = __ListHead;
__ListHead->Previous = v1;
__ListHead = v1;
}
}
//尾插法
void TailInsert(int BufferData)
{
//申请内存并初始化
PNode v1 = new Node;
v1->BufferData = BufferData;
v1->Next = NULL;
v1->Previous = NULL;
//如果链表为空,直接插入
if (__ListHead == NULL)
{
__ListHead = v1;
}
else
{
//执行尾插操作
PNode v2 = __ListHead;
while (v2->Next != NULL)
{
v2 = v2->Next;
}
v2->Next = v1;
v1->Previous = v2;
}
}
//随机插入结点
void MediumInsert(int Position, int BufferData)
{
//申请内存并初始化
PNode v1 = new Node;
//获得链表的长度
int ListLength = GetLength();
v1->BufferData = BufferData;
//如果插入位置不正确,返回错误,并释放该结点
if (Position<=0 || Position>ListLength + 1)
{
delete v1;
printf("The Insert Position Is illegal!\r\n");
}
else
{
//如果插在第一个位置,调用头插法
if (Position == 1)
{
FrontInsert(BufferData);
}
//调用尾插法
else if (Position == ListLength + 1)
{
TailInsert(BufferData);
}
//在随机两个结点中间插入
else
{
//首先获得待插入位置的前一结点,方便插入操作
PNode v2 = GetPreNode(Position);
v1->Next = v2->Next;
v1->Next->Previous = v2;
v2->Next = v1;
v1->Previous = v2;
}
}
}
//遍历链表
void TravelList()
{
PNode v1 = __ListHead;
while (v1!= NULL)
{
printf("%d ", v1->BufferData);
v1 = v1->Next;
}
printf("\r\n");
}
//查找结点
int FindData(int BufferData)
{
PNode v1 = __ListHead;
int Position = 1;
while (v1 != NULL)
{
//遍历该链表,查找该值是否存在
if (v1->BufferData == BufferData)
{
//查找成功,返回位置
printf("Find %d Success!The Position is %d\r\n", BufferData, Position);
return Position;
}
else
{
//后移,同时位置递增
v1 = v1->Next;
Position++;
}
}
if (v1 == NULL)
{
//表示遍历完该链表,并未查找到此值,返回失败
printf("Find %d Fail!\r\n", BufferData);
return 0;
}
}
//删除结点
void DeleteNode(int BufferData)
{
//获得待删结点的位置
int Position = FindData(BufferData);
if (Position == 0)
{
printf("Delete %d Fail!", BufferData);
}
//通过位置获得待删结点的前一结点
else
{
PNode v1 = GetPreNode(Position);
PNode v2 = v1->Next;
//删除结点
v1->Next = v2->Next;
v2->Next->Previous = v1;
delete v2;
printf("Delete %d Success!\r\n", BufferData);
}
}
void _tmain()
{
//测试代码
FrontInsert(10);
FrontInsert(66);
FrontInsert(105);
TailInsert(111);
TailInsert(4396);
TailInsert(2200);
TravelList();
MediumInsert(0, 222);
MediumInsert(1, 333);
MediumInsert(8, 999);
MediumInsert(3, 222);
MediumInsert(5, 777);
MediumInsert(4, 555);
TravelList();
FindData(100);
FindData(999);
FindData(555);
DeleteNode(4396);
DeleteNode(2200);
TravelList();
}
测试结果: