数据结构中双向链表(doublist)的实现以及排序


前言


本期和大家主要分享的是关于数据结构中双向链表的实现过程,那么话不多说,来具体看看吧!

一、双向链表结构体定义

来看一下,双向链表中数据结构的设计的优势在与在定义结构体的时候节点类型不仅有指向后一个节点的指针,也有指向前一个节点的指针,那么使得双向链表在一些操作时变得比较简单;

#ifndef __DOULIST_H__
#define __DOULIST_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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语言实现排序的时候比较简单,但是过渡到数据结构会有一点难度,这是没有关系的,主要是多画图多理解多思考;几遍下来比较难的问题就会变得容易许多;

1. 冒泡排序

/* 冒泡排序 */
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;
}

2. 选择排序

/* 选择排序 */
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;
}

总结

链表的优点:双向链表可以克服单链表查找链表中某结点不方便的缺点。既然它有单链表没有的优点,所以它在一些操作的时候也需要同时注意前驱和后继;

本期的分享就到这里结束啦,总结一下,双向链表也是必须掌握的一种数据结构,是闭着眼睛都要能写出来的一种数据结构,所以必须多练习,多思考;
最后,各位小伙伴们如果喜欢我的分享可以点赞收藏哦,你们的认可是我创作的动力,一起加油!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值