一.双链表的结构和优势
typedef int LTDataType;
typedef struct ListNode
{
LTDataType data;
struct ListNode* next;
struct ListNode* prev;
}ListNode;
与单链表不同的是,双向循环链表有两个指针,一个指向上一个节点,一个指向下一个节点.双向链表有一个头节点,这个节点不存放数据,而是作为哨兵位,指向链表尾和头。与单链表相比,双链表具有更多的灵活性,因为可以从任意一个节点开始向前或向后遍历整个链表。
相比于单链表
优点:
- 双向遍历: 可以从头到尾或者从尾到头遍历链表。
- 插入和删除更灵活: 相对于单链表,插入和删除节点时不需要像单链表那样追溯到前一个节点,可以更方便地进行操作。
缺点:
- 占用更多空间: 每个节点需要额外存储两个指针,因此相对于单链表来说,双链表占用更多的内存空间。
- 实现复杂性: 对于某些简单的应用场景,双链表可能会显得过于复杂。
二.双链表操作
1.初始化
ListNode* ListCreate()
{
ListNode* head = (ListNode*)malloc(sizeof(ListNode));
if (head == NULL)
{
perror("malloc;");
return NULL;
}
head->data = 0;
head->next = head;
head->prev = head;
return head;
}
创建一个头节点.使它的头和尾都指向自己,返回头节点
2.销毁
void ListDestory(ListNode* pHead)
{
assert(pHead);
ListNode* pHeadnext = pHead->next;
while (pHeadnext != pHead)
{
ListNode* p = pHeadnext;
pHeadnext = pHeadnext->next;
free(p);
}
free(pHead);
}
遍历整个链表,释放各个节点,最后将头节点释放,
3.尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* newNode = NodeCreate(x);
newNode->prev = pHead->prev;
pHead->prev->next = newNode;
newNode->next = pHead;
pHead->prev = newNode;
}
相比与单链表,双链表对于尾插更方便,head的prev就是尾节点,然后改变指针指向即可
4.头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* newNode = NodeCreate(x);
ListNode* pHeadnext = pHead->next;
pHead->next = newNode;
newNode->prev = pHead;
newNode->next = pHeadnext;
pHeadnext->prev = newNode;
}
找到头的下一个节点,改变前后节点的prev和next即可
5.尾删
void ListPopBack(ListNode* pHead)
{
assert(pHead);
ListNode* pHeadprev = pHead->prev;
if (pHeadprev == pHead)
return;
pHead->prev = pHeadprev->prev;
pHeadprev->prev->next = pHead;
free(pHeadprev);
pHeadprev = NULL;
}
6.头删
void ListPopFront(ListNode* pHead)
{
assert(pHead);
ListNode* pHeadnext = pHead->next;
if (pHeadnext == pHead)
return;
pHead->next = pHeadnext->next;
pHeadnext->next->prev = pHead;
free(pHeadnext);
pHeadnext = NULL;
}
7.查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* pHeadnext = pHead->next;
while (pHeadnext != pHead)
{
if (pHeadnext->data == x)
return pHeadnext;
pHeadnext = pHeadnext->next;
}
return NULL;
}
遍历链表,如果找到就返回这个节点的地址,找不到返回NULL
8.插入
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* posprev = pos->prev;
ListNode* newNode = NodeCreate(x);
posprev->next = newNode;
newNode->prev = posprev;
pos->prev = newNode;
newNode->next = pos;
}
在pos节点前面插入,与尾插类似
9. 删除指定节点
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* posprev = pos->prev;
posprev->next = pos->next;
pos->next->prev = posprev;
free(pos);
pos = NULL;
}
更改这个节点的前后两个节点的next和prev指向即可
三