本期和大家主要分享的是关于数据结构中双向链表的实现过程,那么话不多说,来具体看看吧!
来分析一下,这里呢定义了一个int的数据类型,表明整个链表存放的是整形的数据;其次定义了链表节点的数据类型,其中包括了此节点存放的数据以及链接向下一个节点的地址;最主要的呢我们的链表是有头的(且链表类型结构体中存放了当前链表中的元素),并且链表头(链表类型,并不是一个节点)后的第一个节点是空节点,我们的有效节点是从这个空节点后面的这个节点开始存放数据的;
#ifndef __LINKLIST_H__
#define __LINKLIST_H__
#include
#include
#include
/* 存储数据的类型 */
typedef int DataType;
/* 链表节点类型 */
typedef struct node
{
DataType Data; //数据域
struct node *pNext; //指向下一个节点的指针
}LinkNode;
/* 链表表头类型 */
typedef struct list
{
LinkNode *pHead;
int clen;
}LinkList;
extern LinkList *CreateLinkList(void);
extern LinkList *InsertHeadLinkList(LinkList *pTmpList, DataType TmpData);
extern int ShowLinkList(LinkList *pTmpList);
extern LinkNode *SearchLinkList(LinkList *pTmpList, DataType TmpData);
extern int UpdateLinkList(LinkList *pTmpList, DataType OldData, DataType NewData);
extern int InsertTailLinkList(LinkList *pTmpList, DataType TmpData);
extern int DeleteLinkList(LinkList *pTmpList, DataType TmpData);
extern int DestroyLinkList(LinkList **ppTmpList);
extern LinkNode *FindMidLinkNode(LinkList *pTmpList);
extern int ReverseLinkList(LinkList *pTmpList);
extern int BubbleSort(LinkList *pTmpList);
extern int ChoiceSortLinkList(LinkList *pTmpList);
#endif
代码如下(示例):
#include "linklist.h"
/* 创建链表 */
LinkList *CreateLinkList(void)
{
LinkList *pTmpList = NULL
pTmpList = malloc(sizeof(LinkList));
if (NULL == pTmpList)
{
perror("fail to malloc");
return NULL;
}
pTmpList->clen = 0;
pTmpList->pHead = malloc(sizeof(LinkNode));
if (pTmpList->pHead == NULL)
{
perror("fail to malloc");
return NULL;
}
pTmpList->pHead->pNext = NULL;
return pTmpList;
}
/* 头插法插入数据 */
LinkList *InsertHeadLinkList(LinkList *pTmpList, DataType TmpData)
{
LinkNode *pTmpNode = NULL;
pTmpNode = malloc(sizeof(LinkNode));
if (NULL == pTmpNode)
{
perror("fail to malloc");
return NULL;
}
pTmpNode->Data = TmpData;
pTmpNode->pNext = pTmpList->pHead->pNext;
pTmpList->pHead->pNext = pTmpNode;
pTmpList->clen++;
return pTmpList;
}
/* 链表遍历 */
int ShowLinkList(LinkList *pTmpList)
{
LinkNode *pTmpNode = NULL;
pTmpNode = pTmpList->pHead->pNext;
while (pTmpNode != NULL)
{
printf("%d ", pTmpNode->Data);
pTmpNode = pTmpNode->pNext;
}
putchar('\n');
return 0;
}
/* 查找元素 */
LinkNode *SearchLinkList(LinkList *pTmpList, DataType TmpData)
{
LinkNode *pTmpNode = NULL;
pTmpNode = pTmpList->pHead->pNext;
while (pTmpNode != NULL)
{
if (pTmpNode->Data == TmpData)
{
return pTmpNode;
}
pTmpNode = pTmpNode->pNext;
}
return NULL;
}
/* 修改元素 */
int UpdateLinkList(LinkList *pTmpList, DataType OldData, DataType NewData)
{
LinkNode *pTmpNode = NULL;
pTmpNode = pTmpList->pHead->pNext;
while (pTmpNode != NULL)
{
if (pTmpNode->Data == OldData)
{
pTmpNode->Data = NewData;
}
pTmpNode = pTmpNode->pNext;
}
return 0;
}
/* 尾插法插入元素 */
int InsertTailLinkList(LinkList *pTmpList, DataType TmpData)
{
LinkNode *pTmpNode = NULL;
LinkNode *pptmNode = NULL;
pTmpNode = pTmpList->pHead;
pptmNode = malloc(sizeof(LinkNode));
if (pptmNode == NULL)
{
perror("fail to malloc");
return -1;
}
while (pTmpNode->pNext != NULL) //访问NULL的空间会段错误,这个就是第一个节点存在的好处
{
pTmpNode = pTmpNode->pNext; //跳出后为最后一个节点
}
pptmNode->Data = TmpData;
pTmpNode->pNext = pptmNode;
pptmNode->pNext = NULL;
pTmpList->clen++;
return 0;
}
/* 删除一个链表节点(所有相同的元素都删除) */
int DeleteLinkList(LinkList *pTmpList, DataType TmpData)
{
LinkNode *preLinkNode = NULL;
LinkNode *desLinkNode = NULL;
preLinkNode = pTmpList->pHead;
desLinkNode = pTmpList->pHead->pNext;
while (desLinkNode != NULL)
{
if (desLinkNode->Data == TmpData)
{
preLinkNode->pNext = desLinkNode->pNext;
free(desLinkNode);
desLinkNode = preLinkNode->pNext;
pTmpList->clen--;
}
else
{
preLinkNode = preLinkNode->pNext;
desLinkNode = desLinkNode->pNext;
}
}
return 0;
}
/* 销毁链表 */
int DestroyLinkList(LinkList **ppTmpList)
{
LinkNode *pFreeNode = NULL;
LinkNode *pTmpNode = NULL;
pFreeNode = (*ppTmpList)->pHead;
pTmpNode = (*ppTmpList)->pHead;
while (pTmpNode != NULL)
{
pTmpNode = pTmpNode->pNext;
free(pFreeNode);
pFreeNode = pTmpNode;
}
free(*ppTmpList);
*ppTmpList = NULL;
return 0;
}
/* 以下为链表的高级操作 */
/* 查找中间节点 */
LinkNode *FindMidLinkNode(LinkList *pTmpList)
{
LinkNode *pFast = NULL;
LinkNode *pSlow = NULL;
pFast = pTmpList->pHead->pNext;
pSlow = pTmpList->pHead->pNext;
while (pFast != NULL)
{
pFast = pFast->pNext;
if (NULL == pFast)
{
break;
}
pFast = pFast->pNext; //快指针走两步(分两步,防止出现非法访问地址)
if (NULL == pFast)
{
break;
}
pSlow = pSlow->pNext; //慢指针走一步
}
return pSlow;
}
/* 链表倒置(头插法) */
/* 将链表断开,再次用头插法插入即可实现链表倒置 */
int ReverseLinkList(LinkList *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;
pTmpList->pHead->pNext = pInsertNode;
}
return 0;
}
这里给出两种排序方法,可能大家用c语言实现排序的时候比较简单,但是过渡到数据结构会有一点难度,这是没有关系的,主要是多画图多理解多思考;几遍下来比较难的问题就会变得容易许多;
/* 冒泡排序(最后一次不需要进行比较) */
int BubbleSort(LinkList *pTmpList)
{
LinkNode *preTmpNode = NULL;
LinkNode *deTmpNode = NULL;
LinkNode *pEndTmpNode = NULL;
DataType TmpData = 0;
preTmpNode = pTmpList->pHead->pNext; //前一个指向第一个节点
if (preTmpNode == NULL || preTmpNode->pNext == NULL) //第一个节点没存数据或者只有第一个节点存储了数据不用排序
{
return 0;
}
deTmpNode = preTmpNode->pNext; //后一个指针指向第一个指针的下一个节点
while (deTmpNode != pEndTmpNode) //前一个指针和后一个指针不是同一个节点,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;
}
return 0;
}
/* 选择排序 */
int ChoiceSortLinkList(LinkList *pTmpList)
{
LinkNode *pMinNode = NULL; //每轮找出最小的一个,与pSwapNode交换值
LinkNode *pTmpNode = NULL; //每次从 pSwapNode的后一个开始往后寻找最小的数(与pMinNode比较)
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; //每轮开始从 pSwapNode开始
pTmpNode = pSwapNode->pNext; //从后一个往后开始比较寻找比最小的还小的
while (pTmpNode != NULL)
{
if (pTmpNode->Data < pMinNode->Data)
{
pMinNode = pTmpNode; //找到后将最小的地址赋值给 pMinNode
}
pTmpNode = pTmpNode->pNext; //接着往后找
}
if (pMinNode != pSwapNode) //如果最小的不是第一个就交换,若是第一个则不用进行操作
{
TmpData = pMinNode->Data;
pMinNode->Data = pSwapNode->Data;
pSwapNode->Data = TmpData;
}
pSwapNode = pSwapNode->pNext; //此位置已经排列完毕,开始从下一个位置开始
}
return 0;
}
/* 如何判断一个链表是否有环(用快慢指针的方法) */
int IsCircleLinkList(LinkList *pTmpList, int *pn, LinkNode *ppNode) //未理解
{
LinkNode *pFast = NULL;
LinkNode *pSlow = NULL;
LinkNode *pTmpNode = NULL;
int cnt = 1;
pFast = pTmpList->pHead->pNext;
pSlow = pTmpList->pHead->pNext;
while (pFast != NULL)
{
pFast = pFast->pNext;
if (NULL == pFast)
{
return 0;
}
pFast = pFast->pNext;
if (NULL == pFast)
{
return 0;
}
pSlow = pSlow->pNext;
if (pFast == pSlow)
{
pTmpNode = pSlow->pNext;
while (pTmpNode != pSlow)
{
cnt++;
pTmpNode = pTmpNode->pNext;
}
*pn = cnt;
pFast = pTmpList->pHead->pNext;
while (1)
{
pSlow = pSlow->pNext;
pFast = pFast->pNext;
if (pSlow == pFast)
{
*ppNode = pSlow;
break;
}
}
return 1;
}
}
return 0;
}
链表的优点:原则上只要系统的内存足够大,那么链表能够进行无限存储;而且存一个申请一个,不会提前占用空间,它是一种空间上不连续的存储方式;
本期的分享就到这里结束啦,总结一下,链表是必须掌握的一种数据结构,是闭着眼睛都要能写出来的一种数据结构,所以必须多练习,多思考;
最后,各位小伙伴们如果喜欢我的分享可以点赞收藏哦,你们的认可是我创作的动力,一起加油!