c语言双向链表实现,数据结构之双向链表(C语言实现)

数据结构之双向链表(C语言实现)

本次介绍双向链表,双向链表是在单链表的基础上增加一个前驱指针,通过某个节点可以直接找到它的前驱和后继,这个对于删除操作来说更加容易,不需要另外再定义一个指针来指向它的前驱。

和以前一样,首先介绍双向链表的通用操作:

1.链表的初始化

2.申请一个链表节点

3.链表的头插法

4.链表的尾插法

5.获取链表长度

6.删除链表节点

7.查找指定值的节点

8.销毁链表(释放链表所有节点的内存空间)

9.输出单链表(输出单链表所有节点的数据域)

说明:以下双向链表的实现,是数据域以整型为例,而且带有头结点。

一、双向链表

1. 链表的结构

typedef struct _DNode

{

int data;

struct _DNode* pre;//前驱指针

struct _DNode* next; //后继指针

}DNode, *DList;

2.链表的操作

(1).链表的初始化(带头结点)

这里的初始化只要是指初始化头结点的指针域

void InitList(DList plist)//初始化头结点

{

if (NULL == plist)

return;

plist->next = NULL;

plist->pre = NULL;

}

(2).申请一个链表节点

从堆中申请一个节点,注意这里是从堆中申请的内存,只能通过free(p)显式释放内存。即使是局部变量,该内存也不会随着函数调用完成而释放该内存。

DNode* BuyNode(int val)

{

DNode* pTmp = (DNode*)malloc(sizeof(DNode));

pTmp->data = val;

pTmp->next = NULL;

pTmp->pre = NULL;

return pTmp;

}

(3).链表头插法

这里的链表是带有头结点的,所以每次新插入的节点应插入头结点后面。注意看看下面的注释

void InsertHead(DList plist, int val)

{

DNode* pTmp = BuyNode(val);//申请一个新的节点

pTmp->next = plist->next;

pTmp->pre = plist;

if (NULL != pTmp->next)//这里的判断一定不能少,读者可以试想去掉这个会发生什么

{

pTmp->next->pre = pTmp;//后一个节点的前驱指向pTmp

}

plist->next = pTmp;

}

(4).链表尾接法

每次将新插入的节点插入到最后一个节点后面,所以采用尾接法插入节点时首先要找到尾节点

void InsertTail(DList plist, int val)

{

DNode* pTmp = BuyNode(val);

DNode* pCur = plist;

while (NULL != pCur->next)

{

pCur = pCur->next;

}

pTmp->pre = pCur;

pCur->next = pTmp;

}

(5).获取链表长度

对链表进行遍历,每遍历一个节点,计数器加一。

int GetListLen(DList plist)

{

DNode* pTmp = plist->next;

int iCount = 0;

while (NULL != pTmp)

{

++iCount;

pTmp = pTmp->next;

}

return iCount;

}

(6).删除链表节点

删除指定值的链表节点时,需要遍历该链表,找到对应节点后想要删除该节点必须要知道该节点的前驱节点,这样才能正确删除该节点。这里要注意对于最后一个节点的删除要先进行判断,读者可以自己思考一下这样做的原因

bool Delete(DList plist, int key)

{

DNode* pPre = plist;//前驱结点

DNode* pCur = plist->next;//后继节点

while (NULL != pCur)

{

if (pCur->data == key)

{

if (NULL != pCur->next)//这涉及到删除最后一个节点,因为对于最后一个节点来说pCur->next为空,也即pCur->next没有pre

{

pCur->next->pre = pCur->pre;

}

pCur->pre->next = pCur->next;

return true;

}

else

{

pPre = pCur;

pCur = pCur->next;

}

}

return false;

}

(7).查找指定值的节点

查找指定值的节点也需要从头到尾遍历链表,若找到则返回该节点,没找到则返回NULL。

DNode* Search(DList plist, int key)

{

DNode* pCur = plist->next;

while (NULL != pCur)

{

if (pCur->data == key)

{

return pCur;

}

pCur = pCur->next;

}

return NULL;

}

(8).销毁链表

销毁链表就是释放链表中所有节点的内存。

void Destroy(DList plist)

{

DNode* pTmp = plist->next;//这里必须从plist->next开始释放内存,原因有两个

while (NULL != pTmp) //一是头结点不是由malloc开辟的,不能有free释放

{ //二是一个空链表是指只有头结点的链表,销毁操作就是把原链表置为空链表

plist = pTmp->next;

free(pTmp);

pTmp = plist;

}

}

(9).输出单链表

输出单链表的操作也比较简单,从头到尾遍历单链表,每遍历一个节点就输出该节点的指针域

void ShowDList(DList plist)

{

DNode* pCur = plist->next;

while (NULL != pCur)

{

printf("%5d", pCur->data);

pCur = pCur->next;

}

printf("\n");

}

最后附上完整代码和运行结果:

//DLink.h

#include#includetypedef struct _DNode

{

int data;

struct _DNode* pre;//前驱指针

struct _DNode* next; //后继指针

}DNode, *DList;

void InitList(DList plist);

void InsertHead(DList plist, int val);

void InsertTail(DList plist, int val);

bool Delete(DList plist, int key);

DNode* Search(DList plist, int key);

int GetListLen(DList plist);

void Destroy(DList plist);

DNode* BuyNode(int val);

void ShowDList(DList plist);

//DLink.c

#include "test.h"

int main()

{

DNode head;

InitList(&head);

for (int i = 1; i < 5; ++i)

{

InsertHead(&head, i);

}

ShowDList(&head);

printf("DList length is %d \n", GetListLen(&head));

for (int i = 6; i < 10; ++i)

{

InsertTail(&head, i);

}

ShowDList(&head);

printf("DList length is %d \n", GetListLen(&head));

printf("search 第一个节点4:");

DNode* pTmp = Search(&head, 4);

printf("%d\n", pTmp->data);

printf("search 最后一个节点9:");

pTmp = Search(&head, 9);

printf("%d\n", pTmp->data);

printf("删除第一个节点4:\n");

if (Delete(&head, 4))

{

ShowDList(&head);

}

else

{

printf("Not Fount!!!\n");

}

printf("删除最后一个节点9:\n");

if (Delete(&head, 9))

{

ShowDList(&head);

}

else

{

printf("Not Fount!!!\n");

}

Destroy(&head);//销毁链表

return 0;

}

void InitList(DList plist)//初始化头结点

{

if (NULL == plist)

return;

plist->next = NULL;

plist->pre = NULL;

}

DNode* BuyNode(int val)

{

DNode* pTmp = (DNode*)malloc(sizeof(DNode));

pTmp->data = val;

pTmp->next = NULL;

pTmp->pre = NULL;

return pTmp;

}

void ShowDList(DList plist)

{

DNode* pCur = plist->next;

while (NULL != pCur)

{

printf("%5d", pCur->data);

pCur = pCur->next;

}

printf("\n");

}

void InsertHead(DList plist, int val)

{

DNode* pTmp = BuyNode(val);//申请一个新的节点

pTmp->next = plist->next;

pTmp->pre = plist;

if (NULL != pTmp->next)//这里的判断一定不能少,读者可以试想去掉这个会发生什么

{

pTmp->next->pre = pTmp;//后一个节点的前驱指向pTmp

}

plist->next = pTmp;

}

void InsertTail(DList plist, int val)

{

DNode* pTmp = BuyNode(val);

DNode* pCur = plist;

while (NULL != pCur->next)

{

pCur = pCur->next;

}

pTmp->pre = pCur;

pCur->next = pTmp;

}

bool Delete(DList plist, int key)

{

DNode* pPre = plist;//前驱结点

DNode* pCur = plist->next;//后继节点

while (NULL != pCur)

{

if (pCur->data == key)

{

if (NULL != pCur->next)//这涉及到删除最后一个节点,因为对于最后一个节点来说pCur->next为空,也即pCur->next没有pre

{

pCur->next->pre = pCur->pre;

}

pCur->pre->next = pCur->next;

return true;

}

else

{

pPre = pCur;

pCur = pCur->next;

}

}

return false;

}

DNode* Search(DList plist, int key)

{

DNode* pCur = plist->next;

while (NULL != pCur)

{

if (pCur->data == key)

{

return pCur;

}

pCur = pCur->next;

}

return NULL;

}

int GetListLen(DList plist)

{

DNode* pTmp = plist->next;

int iCount = 0;

while (NULL != pTmp)

{

++iCount;

pTmp = pTmp->next;

}

return iCount;

}

void Destroy(DList plist)

{

DNode* pTmp = plist->next;

while (NULL != pTmp)

{

plist = pTmp->next;

free(pTmp);

pTmp = plist;

}

}

运行结果:

77182227ccf0ada34ce3af1dff37afbf.png

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值