一、原理
双链表又称双向链表,通常情况下是带头循环结构,在C++STL标准模板库中封装的<list.h>头文件就是带头双向循环链表。
特性:增删灵活且高效,支持随机增删但不支持随机访问
设计思路:
- 链表包含一个头节点head,不存储数据,用于链表的维护,提高数据增删效率
- 每一个链表节点Node都包含一个数据和两个指针(前驱指针prev和后继指针next)
- 前驱指针prev指向前一个节点,后继指针next指向后一个节点
- 当链表为空时,头结点head的prev指针和next指针都指向head自身
- 节点的增删通过前后指针指向的改变即可完成,无需数据移动,效率高
二、DoubleList.h
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int DataType;
typedef struct Node
{
DataType data;
struct Node* next;
struct Node* prev;
}Node;
typedef struct List
{
Node* head;
}List;
void Init(List* plist)
{
plist->head = (Node*)malloc(sizeof(Node));
plist->head->prev = plist->head;
plist->head->next = plist->head;
}
bool Empty(List* plist)
{
return plist->head == plist->head->next;
}
Node* BuyNode(DataType x)
{
Node* node = (Node*)malloc(sizeof(Node));
node->data = x;
node->next = NULL;
node->prev = NULL;
}
void PushFront(List* plist, DataType x)
{
Node* node = BuyNode(x);
if (Empty(plist))
{
node->next = plist->head;
node->prev = plist->head;
plist->head->next = node;
plist->head->prev = node;
}
else
{
Node* next = plist->head->next;
node->prev = plist->head;
node->next = next;
next->prev = node;
plist->head->next = node;
}
}
void PushBack(List* plist, DataType x)
{
Node* node = BuyNode(x);
if (Empty(plist))
{
node->next = plist->head;
node->prev = plist->head;
plist->head->next = node;
plist->head->prev = node;
}
else
{
Node* prev = plist->head->prev;
node->next = plist->head;
node->prev = prev;
prev->next = node;
plist->head->prev = node;
}
}
void PopFront(List* plist)
{
if (Empty(plist))
{
printf("双链表为空,头删失败\n");
return;
}
Node* cur = plist->head->next;
plist->head->next = cur->next;
cur->next->prev = plist->head;
free(cur);
cur = NULL;
}
void PopBack(List* plist)
{
if (Empty(plist))
{
printf("双链表为空,尾删失败\n");
return;
}
Node* cur = plist->head->prev;
plist->head->prev = cur->prev;
cur->prev->next = plist->head;
free(cur);
cur = NULL;
}
Node* Find(List* plist, DataType x)
{
Node* cur = plist->head->next;
while (cur != plist->head)
{
if (cur->data == x)
return cur;
cur = cur->next;
}
return NULL;
}
void InsertFront(Node* pos, DataType x)
{
if (pos == NULL)
{
printf("pos为空,插入失败\n");
return;
}
Node* node = BuyNode(x);
Node* prev = pos->prev;
prev->next = node;
node->prev = prev;
node->next = pos;
pos->prev = node;
}
void Delete(Node* pos)
{
if (pos == NULL)
{
printf("pos为空,Delete失败\n");
return;
}
Node* next = pos->next;
Node* prev = pos->prev;
next->prev = prev;
prev->next = next;
free(pos);
pos = NULL;
}
void Destroy(List* plist)
{
while (!Empty(plist))
{
PopFront(plist);
}
free(plist->head);
plist->head = NULL;
printf("双链表销毁成功\n");
}
void Print(List* plist)
{
if (plist->head == NULL)
{
printf("双链表不存在\n");
return;
}
Node* cur = plist->head->next;
printf("head -> ");
while (cur != plist->head)
{
printf("%2d -> ", cur->data);
cur = cur->next;
}
printf("head\n");
}
三、test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "DoubleList.h"
int main()
{
List list;
Init(&list);
Print(&list); // head -> head
// 尾插数据
PushBack(&list, 1);
PushBack(&list, 3);
PushBack(&list, 5);
PushBack(&list, 7);
Print(&list); // head -> 1 -> 3 -> 5 -> 7 -> head
// 头插数据
PushFront(&list, 2);
PushFront(&list, 4);
PushFront(&list, 6);
PushFront(&list, 8);
Print(&list); // head -> 8 -> 6 -> 4 -> 2 -> 1 -> 3 -> 5 -> 7->head
// 尾删数据
PopBack(&list);
PopBack(&list);
PopBack(&list);
Print(&list); // head -> 8 -> 6 -> 4 -> 2 -> 1 -> head
// 头删数据
PopFront(&list);
PopFront(&list);
PopFront(&list);
Print(&list); // head -> 2 -> 1 -> head
// 在查询的节点前插入数据
InsertFront(Find(&list, 1), 11);
InsertFront(Find(&list, 11), 111);
Print(&list); // head -> 2 -> 111 -> 11 -> 1 -> head
// 删除查询的节点
Delete(Find(&list, 1));
Delete(Find(&list, 11));
Delete(Find(&list, 111));
Print(&list); // head -> 2 -> head
// 销毁链表
Destroy(&list); // 链表销毁成功
Print(&list); // 链表不存在
return 0;
}