数据结构中链表的实现以及排序

这篇具有很好参考价值的文章主要介绍了数据结构中链表的实现以及排序。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言


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

一、链表结构体定义

来分析一下,这里呢定义了一个int的数据类型,表明整个链表存放的是整形的数据;其次定义了链表节点的数据类型,其中包括了此节点存放的数据以及链接向下一个节点的地址;最主要的呢我们的链表是有头的(且链表类型结构体中存放了当前链表中的元素),并且链表头(链表类型,并不是一个节点)后的第一个节点是空节点,我们的有效节点是从这个空节点后面的这个节点开始存放数据的;

#ifndef __LINKLIST_H__
#define __LINKLIST_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* 存储数据的类型 */
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语言实现排序的时候比较简单,但是过渡到数据结构会有一点难度,这是没有关系的,主要是多画图多理解多思考;几遍下来比较难的问题就会变得容易许多;数据结构中前面会学习到顺序表和链式表,后面会紧接着学习顺序栈和链式栈,因此每到一个地方,都可以自己手动的去练习一下,这些都是一些最基本的模块化的程序,在程序设计上可以根据自己的需要在定义数据类型时适当进行增加都是可以的;

1. 冒泡排序

/* 冒泡排序(最后一次不需要进行比较) */
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;
}

2. 选择排序

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

3. 判断一个链表是否有环

/* 如何判断一个链表是否有环(用快慢指针的方法) */
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;
}

总结

链表的优点:原则上只要系统的内存足够大,那么链表能够进行无限存储;而且存一个申请一个,不会提前占用空间,它是一种空间上不连续的存储方式;

本期的分享就到这里结束啦,总结一下,链表是必须掌握的一种数据结构,是闭着眼睛都要能写出来的一种数据结构,所以必须多练习,多思考;
最后,各位小伙伴们如果喜欢我的分享可以点赞收藏哦,你们的认可是我创作的动力,一起加油!文章来源地址https://www.toymoban.com/news/detail-787946.html

到了这里,关于数据结构中链表的实现以及排序的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构——双向链表的实现

    注意: 双向链表又称带头双向循环链表 这⾥的“带头”跟前⾯我们说的“头节点”是两个概念,实际前⾯的在单链表阶段称呼不严 谨,但是为了同学们更好的理解就直接称为单链表的头节点。 带头链表⾥的头节点,实际为“ 哨兵位 ”,哨兵位节点不存储任何有效元素,只

    2024年02月06日
    浏览(48)
  • 【(数据结构)— 双向链表的实现】

    注意: 这里的 “带头” 跟前面我们说的 “头节点” 是两个概念,实际前面的在单链表阶段称呼不严 谨,但是为了同学们更好的理解就直接称为单链表的头节点。 带头链表里的头节点,实际为 “哨兵位” ,哨兵位节点不存储任何有效元素,只是站在这里“放哨 的” “哨

    2024年02月06日
    浏览(46)
  • 【数据结构】单向链表的增删查改以及指定pos位置的插入删除

    目录  单向链表的概念及结构  尾插 头插 尾删 ​编辑  头删  查找  在pos位置前插  在pos位置后插  删除pos位置  删除pos的后一个位置 总结 代码  概念:链表是一种 物理存储结构上非连续 、非顺序的存储结构,数据元素的 逻辑顺序 是通过链表中的 指针链接 次序实现的

    2024年02月05日
    浏览(48)
  • 数据结构----链表介绍、模拟实现链表、链表的使用

    ArrayList底层使用连续的空间,任意位置插入或删除元素时,需要将该位置后序元素整体往前或者往后搬移,故时间复杂度为O(N) 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后

    2024年02月21日
    浏览(51)
  • 数据结构——链表的实现(Java版)

    目录 一、链表 二、代码实现 1.创建结点 2.构造函数 3.链表的相关属性 4.添加虚拟节点 5.判断链表是否为空 6.添加方法 (1)在头部添加 (2)在尾部添加 (3)在索引位置添加 (4)对头插法和尾插法代码进行简化(调用任意位置添加的方法) 7.打印链表(遍历,重写toString方

    2024年01月23日
    浏览(50)
  • 数据结构:双向链表的实现(C实现)

    个人主页 : 个人主页 个人专栏 : 《数据结构》 《C语言》 本篇博客,将要实现的是 带头双向循环链表 ,该结构实现尾插,尾删只需要O(1)的时间复杂度。 其结构如下所示: 既然要实现的链表是双向循环的,那么对于指针域,我们就需要 指向节点上一个的指针 和 指向节点

    2024年02月14日
    浏览(52)
  • Java中链表的实现(超详细)

            在Java中,链表可以通过 创建节点 和 链接节点 来实现。以下是一个简单的 链表实现 示例: 输出:         在这个示例中,我们创建了一个  LinkedList 类 ,它 包含一个 Node 类 和 一些方法 来操作链表。我们可以使用  push() 方法在链表的头部插入节点 ,使

    2024年02月14日
    浏览(42)
  • 【数据结构】 链表简介与单链表的实现

    在【数据结构】 ArrayList简介与实战中我们已经熟悉了ArrayList的使用,并且进行了简单模拟实现。通过源码知道,ArrayList底层使用数组来存储元素 由于其底层是一段连续空间, 当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为

    2024年02月12日
    浏览(202)
  • 【数据结构】—带头双向循环链表的实现(完美链表)

    链表结构一共有八种形式,在前面的文章里已经讲完了不带头单向非循环链表的实现,但是我们发现该链表实现尾插与尾删时比较麻烦,要先从头节点进行遍历,找到尾节点,时间复杂度为O(N),而本次所讲的带头双向循环单链表,则可以直接找到尾节点。 虽然该链表看起来

    2024年01月25日
    浏览(63)
  • c语言数据结构——链表的实现及其基本操作

    顺序表的问题及思考 问题: 中间/头部的插入删除,时间复杂度为O(N) 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到 200,我们再继续插入了5个数据,后面没有数据插

    2023年04月09日
    浏览(85)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包