【数据结构】双向链表的API及C语言实现
数据类型及API声明
双向链表是指链表的每个结点有两个指针域,一个next指针指向后继结点,一个previous指针指向前驱结点。另外,双向链表也包含一个游标slider。
//链表句柄
typedef void DoubleLinkedList;
//链表结点
typedef struct DoubleLinkedListNode DoubleLinkedListNode;
//链表头
typedef struct DoubleLinkedListHead DoubleLinkedListHead;
DoubleLinkedList* DoubleLinkedList_Create();
void DoubleLinkedList_Destroy(DoubleLinkedList* list);
void DoubleLinkedList_Clear(DoubleLinkedList* list);
int DoubleLinkedList_Length(DoubleLinkedList* list);
int DoubleLinkedList_Insert(DoubleLinkedList* list, DoubleLinkedListNode* node, int pos);
DoubleLinkedListNode* DoubleLinkedList_Get(DoubleLinkedList* list, int pos);
DoubleLinkedListNode* DoubleLinkedList_Delete(DoubleLinkedList* list, int pos);
//按元素值删除指定某个结点
DoubleLinkedListNode* DoubleLinkedList_DeleteNode(DoubleLinkedList* list, DoubleLinkedListNode* node);
//重置游标
DoubleLinkedListNode* DoubleLinkedList_Reset(DoubleLinkedList* list);
//获取当前游标
DoubleLinkedListNode* DoubleLinkedList_Current(DoubleLinkedList* list);
//游标下移
DoubleLinkedListNode* DoubleLinkedList_Next(DoubleLinkedList* list);
//游标上移
DoubleLinkedListNode* DoubleLinkedList_Pre(DoubleLinkedList* list);
双向链表的API实现
双向链表结点结构体
链表结点包含了指向后继结点的指针和指向前驱结点的指针。
struct DoubleLinkedListNode
{
DoubleLinkedListNode* previous;
DoubleLinkedListNode* next;
};
表头结构体
表头包含一个链表结点结构体head,用于指向链表的0号结点,包含一个游标slider,用于指示当前结点,包含一个长度length,用于指示链表元素个数。
struct DoubleLinkedListHead
{
DoubleLinkedListNode head;
DoubleLinkedListNode* slider;
int length;
};
创建双向链表并返回双向链表的句柄
- 函数功能:创建一个双向链表并返回双向链表句柄,其实就是创建一个表头
- 函数参数:无
- 函数返回:表头
DoubleLinkedList* DoubleLinkedList_Create()
{
DoubleLinkedListHead* pHead = NULL;
pHead = (DoubleLinkedListHead*)malloc(sizeof(DoubleLinkedListHead));
if (pHead == NULL)
{
printf("pHead = (DoubleLinkedListHead*)malloc(sizeof(DoubleLinkedListHead)) err\n");
return NULL;
}
memset(pHead, 0, sizeof(DoubleLinkedListHead));
return pHead;
}
销毁双向链表
- 函数功能:销毁双向链表就是释放表头资源,销毁双向链表前需要逐个删除链表结点
- 函数参数:双向链表的句柄
- 函数返回:无
void DoubleLinkedList_Destroy(DoubleLinkedList* list)
{
DoubleLinkedListHead* pHead = NULL;
if (list == NULL)
{
printf("err: list == NULL\n");
return;
}
pHead = (DoubleLinkedListHead*)list;
free(pHead);
}
清空双向链表
- 函数功能:返回到双向链表刚创建的状态,即表头指针域指向NULL,长度置为0
- 函数参数:双向链表的句柄
- 函数返回:无
void DoubleLinkedList_Clear(DoubleLinkedList* list)
{
DoubleLinkedListHead* pHead = NULL;
if (list == NULL)
{
printf("err: list == NULL\n");
return;
}
pHead = (DoubleLinkedListHead*)list;
pHead->head.next = NULL;
pHead->head.previous = NULL;
pHead->slider = NULL;
pHead->length = 0;
}
返回双向链表元素个数
- 函数功能:获取双向链表长度,即结点个数
- 函数参数:双向链表句柄
- 函数返回:结点个数
int DoubleLinkedList_Length(DoubleLinkedList* list)
{
DoubleLinkedListHead* pHead = NULL;
if (list == NULL)
{
printf("err: list == NULL\n");
return -1;
}
pHead = (DoubleLinkedListHead*)list;
return pHead->length;
}
插入元素
- 函数功能:在双向链表中插入一个元素,被插入的元素已经在主调函数分配好内存,只需把链表结点DoubleLinkedListNode串接起来即可
- 函数参数:list 双向链表句柄 node 待插入元素 pos 插入位置
- 函数返回:成功返回0
int DoubleLinkedList_Insert(DoubleLinkedList* list, DoubleLinkedListNode* node, int pos)
{
int i = 0;
DoubleLinkedListHead* pHead = NULL;
DoubleLinkedListNode* pCurrent = NULL;
DoubleLinkedListNode* pNext = NULL;
if ((list == NULL) || (node == NULL) || (pos < 0))
{
printf("err: (list == NULL) || (node == NULL) || (pos < 0)\n");
return -1;
}
pHead = (DoubleLinkedListHead*)list;
pCurrent = &(pHead->head);
for (i = 0; i < pos; i++)
{
pCurrent = pCurrent->next;
}
pNext = pCurrent->next;
node->next = pNext;
pCurrent->next = node;
node->previous = pCurrent;
if (pNext != NULL) //见分析示意图:在尾部插入
{
//在尾部插入的时候,pNext = NULL
pNext->previous = node;
}
if (pCurrent == (DoubleLinkedListNode*)list) //见分析示意图,在0号位置插入
{
//在0号位置插入,要修正node->previous = pCurrent;
node->previous = NULL;
}
if (pHead->length == 0)
{
pHead->slider = node;
}
pHead->length++;
return 0;
}
插入元素时的异常处理
(1)普通位置插入
因为双向链表既有后继结点的指针,又有前驱结点的指针,所以需要两个辅助指针pCurrent和pNext。pCurrent指针为当前指针,初始位置指向head头结点,然后根据传入的参数pos往后移动pos步,要插入的元素就是当前pCurrent后面的位置,在插入前应保存原来的pos位处的结点,也就是pCurrent->next,因为被插入的node结点应放在pCurrent->next结点的前驱指针域中,所以就有了pNext = pCurrent->next的操作。
(2)尾部插入
尾部插入时会有一个异常情况,那就是pNext指向NULL,而NULL没有指针域,所以在尾部插入元素时,应该去掉pNext->previous = node这一步。
(3)头部位置插入
在头部插入,应该对node的previous域做修正,使它指向NULL,即node->previous = NULL。
(4)首次插入
首次插入的异常情况刚好是在头部插入和在尾部插入两种情况的综合,即(2)(3)中的分析。
返回指定位置元素
- 函数功能:获取一个指定位置的元素
- 函数参数:list 双向链表句柄 pos 要获取的元素标号
- 函数返回:获取的结点元素
DoubleLinkedListNode* DoubleLinkedList_Get(DoubleLinkedList* list, int pos)
{
int i = 0;
DoubleLinkedListHead* pHead = NULL;
DoubleLinkedListNode* pCurrent = NULL;
if ((list == NULL) || (pos < 0))
{
printf("err: (list == NULL) || (pos < 0)\n");
return NULL;
}
pHead = (DoubleLinkedListHead*)list;
pCurrent = &(pHead->head);
for (i = 0; i < pos; i++)
{
pCurrent = pCurrent->next;
}
return pCurrent->next;
}
删除结点并返回被删除结点
- 函数功能:按位置删除一个结点并返回该结点
- 函数参数:list 双向链表句柄 pos 要删除的链表结点位置
- 函数返回:被删除的结点
DoubleLinkedListNode* DoubleLinkedList_Delete(DoubleLinkedList* list, int pos)
{
int i = 0;
DoubleLinkedListHead* pHead = NULL;
DoubleLinkedListNode* pCurrent = NULL;
DoubleLinkedListNode* pNext = NULL;
DoubleLinkedListNode* pTemp = NULL;
if ((list == NULL) || (pos < 0))
{
printf("err: (list == NULL) || (pos < 0)\n");
return NULL;
}
pHead = (DoubleLinkedListHead*)list;
pCurrent = &(pHead->head);
for (i = 0; i < pos; i++)
{
pCurrent = pCurrent->next;
}
pTemp = pCurrent->next;
pNext = pTemp->next;
pCurrent->next = pNext;
if (pNext != NULL)
{
//如果pNext是NULL,不进行pNext->previous的操作
pNext->previous = pCurrent;
if (pCurrent == (DoubleLinkedListNode*)list) //见分析示意图,删除0号结点
{
//在0号位置删除结点,要修正pNext->previous = pCurrent;
pNext->previous = NULL;
}
}
if (pTemp == pHead->slider)
{
pHead->slider = pNext;
}
pHead->length--;
return pTemp;
}
删除元素异常分析示意图
(1)普通位置删除
因为需要返回被删除结点,所以,在插入结点的基础上又多了一个辅助指针pTemp用于缓存被删除的结点。
(2)尾部删除
和在尾部插入元素一样,在删除尾部结点的时候,应去掉pNext->previous = pCurrent操作。
(3)头部删除
在头部删除时,和在头部插入一样,应加一步修正,pNext->previous = NULL。
(4)删除唯一结点
删除唯一结点的异常情况已被(2)(3)情况覆盖。
按元素删除结点
- 函数功能:按元素删除一个结点并返回该结点
- 函数参数:list 双向链表句柄 node要删除的链表结点元素
- 函数返回:被删除的结点
DoubleLinkedListNode* DoubleLinkedList_DeleteNode(DoubleLinkedList* list, DoubleLinkedListNode* node)
{
int i = 0;
DoubleLinkedListHead* pHead = NULL;
DoubleLinkedListNode* pTemp = NULL;
DoubleLinkedListNode* pCurrent = NULL;
if ((list == NULL) || (node == NULL))
{
printf("err: (list == NULL) || (node == NULL)");
return NULL;
}
pHead = (DoubleLinkedListHead*)list;
pCurrent = &(pHead->head);
for (i = 0; i < pHead->length; i++)
{
if (pCurrent == node)
{
pTemp = pCurrent;
break;
}
pCurrent = pCurrent->next;
}
if (pTemp != NULL)
{
if (NULL == DoubleLinkedList_Delete(list, i - 1))
{
printf("DoubleLinkedList_DeleteNode err\n");
return NULL;
}
}
return pTemp;
}
重置游标
- 函数功能:将游标指向第一个元素
- 函数参数:list 双向链表句柄
- 函数返回:游标位置
DoubleLinkedListNode* DoubleLinkedList_Reset(DoubleLinkedList* list)
{
DoubleLinkedListHead* pHead = NULL;
if (list == NULL)
{
printf("err: list == NULL\n");
return NULL;
}
pHead = (DoubleLinkedListHead*)list;
pHead->slider = pHead->head.next;
return pHead->slider;
}
返回当前游标
- 函数功能:获取游标当前指向的结点
- 函数参数:list 双向链表句柄
- 函数返回:游标位置
DoubleLinkedListNode* DoubleLinkedList_Current(DoubleLinkedList* list)
{
DoubleLinkedListHead* pHead = NULL;
if (list == NULL)
{
printf("err: list == NULL\n");
return NULL;
}
pHead = (DoubleLinkedListHead*)list;
return pHead->slider;
}
游标下移
- 函数功能:获取游标当前指向的结点,并将游标下移
- 函数参数:list 双向链表句柄
- 函数返回:下移前的游标位置
DoubleLinkedListNode* DoubleLinkedList_Next(DoubleLinkedList* list)
{
DoubleLinkedListHead* pHead = NULL;
DoubleLinkedListNode* pTemp = NULL;
if (list == NULL)
{
printf("err: list == NULL\n");
return NULL;
}
pHead = (DoubleLinkedListHead*)list;
pTemp = pHead->slider;
pHead->slider = pHead->slider->next;
return pTemp;
}
游标上移
- 函数功能:获取游标当前指向的结点,并将游标上移
- 函数参数:list 双向链表句柄
- 函数返回:上移前的游标位置
DoubleLinkedListNode* DoubleLinkedList_Pre(DoubleLinkedList* list)
{
DoubleLinkedListHead* pHead = NULL;
DoubleLinkedListNode* pTemp = NULL;
if (list == NULL)
{
printf("err: list == NULL\n");
return NULL;
}
pHead = (DoubleLinkedListHead*)list;
pTemp = pHead->slider;
pHead->slider = pHead->slider->previous;
return pTemp;
}
测试函数
#include "DoubleLinkedList.h"
typedef struct MyData
{
DoubleLinkedListNode node;
int value;
}MyData;
int main()
{
int i = 0;
MyData mVector[5];
MyData* m = NULL;
DoubleLinkedList* mList = NULL;
mList = DoubleLinkedList_Create();
if (mList == NULL)
{
printf("DoubleLinkedList_Create() err\n");
return -1;
}
for (i = 0; i < 5; i++)
{
mVector[i].value = 3 + i;
DoubleLinkedList_Insert(mList, (DoubleLinkedListNode*)(&mVector[i]), DoubleLinkedList_Length(mList));
}
m = (MyData*)DoubleLinkedList_Current(mList);
printf("游标结点:%d\n", m->value);
for (i = 0; i < DoubleLinkedList_Length(mList); i++)
{
if (i == 0)
{
printf("链表内容:");
}
printf("%d ", ((MyData*)DoubleLinkedList_Get(mList, i))->value);
}
printf("\n");
DoubleLinkedList_DeleteNode(mList, (DoubleLinkedListNode*)(&mVector[2]));
for (i = 0; i < DoubleLinkedList_Length(mList); i++)
{
if (i == 0)
{
printf("链表内容:");
}
printf("%d ", ((MyData*)DoubleLinkedList_Get(mList, i))->value);
}
printf("\n");
for (i = 0; i < DoubleLinkedList_Length(mList); i++)
{
DoubleLinkedList_Delete(mList, i);
}
DoubleLinkedList_Destroy(mList);
system("pause");
return 0;
}
代码资源已上传,链接如下: