目录
双向链表的方法:
双向链表的初始化方法
我们可以对比双向链表和单链表方法在实现上的区别:
双向链表的实现引进头结点的概念:
双向链表的优势:
1、尾插尾删
2、指定位置的插入和删除
双向链表:也叫做有头节点的双向循环链表
typedef int LTDataType;
typedef struct ListNode
{
LTDataType x;
struct ListNode* next;
struct ListNode* prev;
//。。。。
}LTNode;
//void LTInit(LTNode** pphead);
LTNode* LTInit();
void LTDestroy(LTNode* phead);
void LTPrint(LTNode* phead);
bool LTEmpty(LTNode* phead);
void LTPushBack(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);
void LTPushFront(LTNode* phead, LTDataType x);
void LTPopFront(LTNode* phead);
//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x);
void LTErase(LTNode* pos);
LTNode *LTFind(LTNode* phead,LTDataType x);
之所以由原来的:void LTInit(LTNode** pphead);
改成LTNode* LTInit();
是因为使用原来的方法需要传入的参数是二级指针,破坏了接口的统一性,为了使方法的使用成本更低,所以把初始化方法调整成这样的形式。
单链表常常需要传入二级指针,而双向链表只需要一级指针就能完成所有的任务。
数据结构笔记2:链表-CSDN博客
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
//
}SLTNode;
void SLTPrint(SLTNode* phead);
//头部插入删除/尾部插入删除
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);
//销毁链表
void SListDesTroy(SLTNode** pphead);
可以发现,由于单链表的增删都有可能对头结点进行修改,举个例子:
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = SLTBuyNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
return;
}
SLTNode* cur = *pphead;
while (cur->next)
{
cur->next = cur->next->next;
}
cur->next = newnode;
}
像是对单链表的尾插,当链表为空链表(头结点为空时),就需要修改头结点的值,让新的节点成为头结点。
或者是:单链表的尾删
void SLTPopBack(SLTNode** pphead)
{
assert(pphead);
assert(*pphead);
SLTNode* cur = *pphead;
if (cur->next == NULL)
{
free(*pphead);
*pphead = NULL;
return;
}
while (cur->next->next)
{
cur = cur->next;
}
free(cur->next);
cur->next = NULL;
}
当链表只剩下一个元素的时候,就需要将头结点置为空。
LTNode* BuyNode(LTDataType x)
{
LTNode* node = (LTNode*)malloc(sizeof(LTDataType));
node->data = x;
node->next = node;
node->prev = node;
return node;
}
LTNode* LTInit()
{
LTNode* phead = BuyNode(-1);
return phead;
}
这使得当链表为空时,我们进行插入操作不需要修改头结点,只需要将头结点指向的next指针置为新节点。
当链表只剩下一个节点时,我们进行删除操作不需要修改头结点,只需要将头结点指向的next指针和prev指针指向自己。
双向链表的尾插不需要遍历整个链表。只需要修改头结点和头结点前一个节点的指向。
尾删也是同理。
void LTPushBack(LTNode* phead, LTDataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
newnode->next = phead;
newnode->prev = phead->prev;
phead->prev->next = newnode;
phead->prev = newnode;
}
void LTPopBack(LTNode* phead)
{
assert(phead->next != phead);
LTNode* del = phead->prev;
del->prev->next = phead;
phead->prev = del->prev;
free(del);
}
双向链表的指定位置的插入和删除不需要遍历链表找指定位置前的节点。
//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* node = BuyNode(x);
node->next = pos->next;
node->prev = pos;
pos->next->prev = node;
pos->next = node;
}
void LTErase(LTNode* pos)
{
assert(pos);
assert(pos->next != pos);
LTNode* del = pos;
del->prev->next = del->next;
del->next->prev = del->prev;
free(del);
}
总结:双向链表引进了头结点,并且可循环,可双向查询节点。在插入和删除方面对比单链表更具有优势。