本期和大家主要分享的是关于数据结构中双向链表的实现过程,那么话不多说,来具体看看吧!
来看一下,双向链表中数据结构的设计的优势在与在定义结构体的时候节点类型不仅有指向后一个节点的指针,也有指向前一个节点的指针,那么使得双向链表在一些操作时变得比较简单;
#ifndef __DOULIST_H__
#define __DOULIST_H__
#include
#include
#include
typedef int DataType; //链表存储数据类型
typedef struct node //链表节点结构(双向)
{
DataType Data;
struct node *pPre;
struct node *pNext;
}LinkNode;
typedef struct list //链表头节点的类型(用来管理链表节点)
{
LinkNode *pHead;
int clen;
}DouList;
extern DouList *CreateDouList(void);
extern int InsertDouList(DouList *pTmpList, DataType TmpData);
extern int ShowDouList(DouList *pTmpList);
extern LinkNode *SearchDouList(DouList *pTmpList, DataType TmpData);
extern int UpdateDouList(DouList *pTmpList, DataType OldData, DataType NewData);
extern int InsertTailDouList(DouList *pTmpList, DataType TmpData);
extern int DeleteDouList(DouList *pTmpList, DataType TmpData);
extern int DestroyDouList(DouList **pTmpList);
extern LinkNode *FindMidLinkNode(DouList *pTmpList);
extern int ReverseDouList(DouList *pTmpList);
extern int BubbleSort(DouList *pTmpList);
extern int ChoiceSortDouList(DouList *pTmpList);
#endif
代码如下:
#include "doulist.h"
/* 创建一个双向链表 */
DouList *CreateDouList(void)
{
DouList *pTmpList = NULL; //定义一个双向链表类型的空指针
pTmpList = malloc(sizeof(DouList)); //使得空指针指向一个链表类型的空间存储区域
if (NULL == pTmpList)
{
perror("fail to malloc");
return NULL;
}
pTmpList->clen = 0; //创建的是一个空链表,此时无元素
pTmpList->pHead = malloc(sizeof(LinkNode)); //创建第一个空节点,使得链表的头指向空节点
if (NULL == pTmpList->pHead)
{
perror("fail to malloc");
return NULL;
}
pTmpList->pHead->pPre = pTmpList->pHead->pNext = NULL; //此时空节点的头和尾都是空
return pTmpList;
}
/* 头插法 */
int InsertDouList(DouList *pTmpList, DataType TmpData)
{
LinkNode *pTmpNode = NULL;
pTmpNode = malloc(sizeof(LinkNode)); //申请一个节点
if (NULL == pTmpNode)
{
perror("fail to pTmpNode");
return -1;
}
pTmpNode->Data = TmpData; //1.先存储数据
pTmpNode->pNext = pTmpList->pHead->pNext; //2.此节点的后,指向链接向空节点的后一个
pTmpNode->pPre = pTmpList->pHead; //3.此节点的前,指向上一个空节点
pTmpList->pHead->pNext = pTmpNode; //4.空节点指向这个新节点
if (pTmpNode->pNext != NULL) //5.如果后面有节点,则后面的节点头指向此节点,如果没有赋值为空
{
pTmpNode->pNext->pPre = pTmpNode;
}
pTmpList->clen++; //6.增加了一个新节点,元素个数增加
return 0;
}
/* 链表遍历 */
int ShowDouList(DouList *pTmpList)
{
LinkNode *pTmpNode = NULL;
pTmpNode = pTmpList->pHead->pNext;
if (pTmpNode == NULL)
{
return 0;
}
while (pTmpNode != NULL)
{
printf("%d ", pTmpNode->Data);
pTmpNode = pTmpNode->pNext;
}
putchar('\n');
return 0;
}
/* 查找元素 */
LinkNode *SearchDouList(DouList *pTmpList, DataType TmpData)
{
LinkNode *pTmpNode = NULL;
pTmpNode = pTmpList->pHead->pNext;
if (NULL == pTmpNode)
{
return NULL;
}
while (pTmpNode != NULL)
{
if (pTmpNode->Data == TmpData)
{
return pTmpNode;
}
pTmpNode = pTmpNode->pNext;
}
return NULL;
}
/* 修改元素 */
int UpdateDouList(DouList *pTmpList, DataType OldData, DataType NewData)
{
LinkNode *pTmpNode = NULL;
pTmpNode = pTmpList->pHead->pNext;
if (NULL == pTmpNode)
{
return 0;
}
while (pTmpNode != NULL)
{
if (pTmpNode->Data == OldData)
{
pTmpNode->Data = NewData;
}
pTmpNode = pTmpNode->pNext;
}
return 0;
}
/* 尾插法插入元素 */
int InsertTailDouList(DouList *pTmpList, DataType TmpData)
{
LinkNode *pTmpNode = NULL;
LinkNode *pptmpNode = NULL;
pptmpNode = malloc(sizeof(LinkNode));
if (NULL == pptmpNode)
{
perror("fail to malloc");
return -1;
}
pTmpNode = pTmpList->pHead->pNext;
while (pTmpNode->pNext != NULL)
{
pTmpNode = pTmpNode->pNext;
}
pptmpNode->Data = TmpData;
pptmpNode->pNext = NULL;
pptmpNode->pPre = pTmpNode;
pTmpNode->pNext = pptmpNode;
pTmpList->clen++;
return 0;
}
/* 删除一个链表节点 */
int DeleteDouList(DouList *pTmpList, DataType TmpData)
{
LinkNode *pTmpNode = NULL;
LinkNode *preTmpNode = NULL;
preTmpNode = pTmpList->pHead;
pTmpNode = pTmpList->pHead->pNext;
while (pTmpNode->pNext != NULL)
{
if (pTmpNode->Data == TmpData)
{
preTmpNode->pNext = pTmpNode->pNext;
if (pTmpNode->pNext != NULL)
{
pTmpNode->pNext->pPre = preTmpNode;
}
free(pTmpNode);
pTmpList->clen--;
pTmpNode = preTmpNode->pNext;
}
else
{
pTmpNode = pTmpNode->pNext;
preTmpNode = preTmpNode->pNext;
}
}
return 0;
}
/* 销毁链表 */
int DestroyDouList(DouList **pTmpList)
{
LinkNode *pTmpNode = NULL;
LinkNode *pFreeNode = NULL;
pTmpNode = (*pTmpList)->pHead;
pFreeNode = (*pTmpList)->pHead;
while (pTmpNode != NULL)
{
pTmpNode = pTmpNode->pNext;
free(pFreeNode);
pFreeNode = pTmpNode;
}
free(*pTmpList);
*pTmpList = NULL;
return 0;
}
/* 以下为链表的高级操作 */
/* 查找中间节点 */
LinkNode *FindMidLinkNode(DouList *pTmpList)
{
LinkNode *pFast = NULL;
LinkNode *pSlow = NULL;
pFast = pTmpList->pHead->pNext;
pSlow = pTmpList->pHead->pNext;
while (pFast != NULL)
{
pFast = pFast->pNext;
if (pFast == NULL)
{
break;
}
pFast = pFast->pNext;
if (pFast == NULL)
{
break;
}
pSlow = pSlow->pNext;
}
return pSlow;
}
/* 链表倒置(头插法) */
/* 将链表断开,再依次插入即可 */
int ReverseDouList(DouList *pTmpList)
{
LinkNode *pTmpNode = NULL;
LinkNode *pInsertNode = NULL;
pTmpNode = pTmpList->pHead->pNext;
pTmpList->pHead->pNext = NULL;
while (pTmpNode != NULL)
{
pInsertNode = pTmpNode;
pTmpNode = pTmpNode->pNext;
pInsertNode->pNext = pTmpList->pHead->pNext;
pInsertNode->pPre = pTmpList->pHead;
pTmpList->pHead->pNext = pInsertNode;
}
return 0;
}
这里给出两种排序方法,可能大家用c语言实现排序的时候比较简单,但是过渡到数据结构会有一点难度,这是没有关系的,主要是多画图多理解多思考;几遍下来比较难的问题就会变得容易许多;
/* 冒泡排序 */
int BubbleSort(DouList *pTmpList)
{
LinkNode *preTmpNode = NULL;
LinkNode *deTmpNode = NULL;
LinkNode *pEndTmpNode = NULL;
DataType TmpData;
preTmpNode = pTmpList->pHead->pNext;
if (preTmpNode == NULL || preTmpNode->pNext == NULL)
{
return 0;
}
deTmpNode = preTmpNode->pNext;
while (deTmpNode != pEndTmpNode)
{
while (deTmpNode != pEndTmpNode)
{
if (preTmpNode->Data > deTmpNode->Data)
{
TmpData = preTmpNode->Data;
preTmpNode->Data = deTmpNode->Data;
deTmpNode->Data = TmpData;
}
preTmpNode = preTmpNode->pNext;
deTmpNode = deTmpNode->pNext;
}
pEndTmpNode = preTmpNode;
preTmpNode = pTmpList->pHead->pNext;
deTmpNode = preTmpNode->pNext; //判断条件是第一个等不等于最后一个,所以一轮过后 deTmpNode必须归位
//(由于两个是绑定关系,一个中有一个改变的因素,所以必须一起挪下来)
}
return 0;
}
/* 选择排序 */
int ChoiceSortDouList(DouList *pTmpList)
{
LinkNode *pMinNode = NULL;
LinkNode *pTmpNode = NULL;
LinkNode *pSwapNode = NULL;
DataType TmpData;
if (pTmpList->pHead->pNext == NULL || pTmpList->pHead->pNext->pNext == NULL)
{
return 0;
}
pSwapNode = pTmpList->pHead->pNext;
while (pSwapNode->pNext != NULL)
{
pMinNode = pSwapNode;
pTmpNode = pSwapNode->pNext;
while (pTmpNode != NULL)
{
if (pMinNode->Data > pTmpNode->Data)
{
pMinNode = pTmpNode;
}
pTmpNode = pTmpNode->pNext;
}
if (pMinNode != pSwapNode)
{
TmpData = pMinNode->Data;
pMinNode->Data = pSwapNode->Data;
pSwapNode->Data = TmpData;
}
pSwapNode = pSwapNode->pNext;
}
return 0;
}
链表的优点:双向链表可以克服单链表查找链表中某结点不方便的缺点。既然它有单链表没有的优点,所以它在一些操作的时候也需要同时注意前驱和后继;
本期的分享就到这里结束啦,总结一下,双向链表也是必须掌握的一种数据结构,是闭着眼睛都要能写出来的一种数据结构,所以必须多练习,多思考;
最后,各位小伙伴们如果喜欢我的分享可以点赞收藏哦,你们的认可是我创作的动力,一起加油!