目录
一.链表结构
带有哨兵位头节点phead,可以通过phead找到链表第一个节点和最后一个节点,每一个节点除了能找到它下一个节点,还能找到上一个节点
二.定义结构体
typedef int LTDataType;
typedef struct ListNode
{
LTDataType val;
struct ListNode* next;
struct ListNode* prev;
}LTNode;
三.动态开辟新节点并初始化
LTNode* BuyLTNode(LTDataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
newnode->val = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
四.初始化哨兵位的头节点
这里有两种方法:
1:采用二级指针的方法,初始化节点phead
void ListInit(LTNode** pphead)
{
assert(pphead);
*phead = BuyLTNode(0);
(*phead)->next = *phead;
(*phead)->prev = *phead;
}
2:用返回指针的方法,将链表的头节点定义完成后返回其指针
LTNode* ListInit()
{
LTNode* phead = BuyLTNode(0);
phead->next = phead;
phead->prev = phead;
return phead;
}
五.双向链表打印
与单链表打印实现一样,但是要注意循环结束的条件,这里的带头双向循环链表,基于它自身的循环特性,所以当遍历链表用的指针cur走到phead的时候就相当于将整个链表都遍历了一遍,就结束循环
void PrintList(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->val);
cur = cur->next;
}
printf("\n");
}
六.双向链表尾插
有两种实现方法:
方法一,利用哨兵位的头节点phead->prev找到链表尾节点进行尾插
方法二,使用下面实现的“双向链表在pos的前面进行插入”函数,所以尾插就相当于在phead的前面插入
void ListPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
//LTNode* newnode = BuyLTNode(x);
//LTNode* tail = phead->prev;
//tail->next = newnode;
//newnode->prev = tail;
//newnode->next = phead;
//phead->prev = newnode;
ListInsert(phead, x);
}
七.双向链表尾删
有两种实现方法:
方法一,利用哨兵位的头节点phead->prev找到链表尾节点进行尾删
方法二,利用下面实现的“双向链表删除pos位置的节点”函数,所以就是删除phead->prev位置处节点
void ListPopBack(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);//只有一个头的时候就删不了了
//LTNode* tail = phead->prev;
//LTNode* tailPrev = tail->prev;
//free(tail);
//tail = NULL;
//tailPrev->next = phead;
//phead->prev = tailPrev;
ListErase(phead->prev);
}
八.双向链表头插
void ListPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
assert(phead->next != phead);
//LTNode* newnode = BuyLTNode(x);
//LTNode* pheadNext = phead->next;
//phead->next = newnode;
//newnode->prev = phead;
//newnode->next = pheadNext;
//pheadNext->prev = newnode;
ListInsert(phead->next, x);
}
九.双向链表头删
void ListPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
//LTNode* head = phead->next;
//phead->next = head->next;
//head->next->prev = phead;
//free(head);
//head = NULL;
ListErase(phead->next);
}
十.双向链表查找
这里的查找没有特别的方法,就是遍历链表,找到所要查找的数据就返回对应的节点,没找到就返回NULL,同样在遍历的时候要注意循环结束的条件
LTNode* ListFind(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->val == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
十一.双向链表在pos的前面进行插入
void ListInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode = BuyLTNode(x);
LTNode* posPrev = pos->prev;
newnode->next = pos;
pos->prev = newnode;
newnode->prev = posPrev;
posPrev->next = newnode;
}
十二.双向链表删除pos位置的节点
void ListErase(LTNode* pos)
{
assert(pos);
pos->prev->next = pos->next;
pos->next->prev = pos->prev;
free(pos);
pos = NULL;
}
十三.双向链表销毁
这里可以从哨兵位头节点的下一个节点开始释放空间,最后再释放哨兵位头节点空间;也可以直接从哨兵位头节点开始释放空间,但是在释放每一个节点空间前需要记录下一个节点的地址
void ListDestory(LTNode* phead)
{
assert(phead);
LTNode* cur = phead;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
}