数据结构之线性表——链表 C语言版

一、链表简介

链表,顾名思义,是一种将一个个携带着数据的结点链接起来的数据结构,这些结点在物理存储上可以是非连续的,结点的基本结构由数据域指针域组成,各个结点的链接正是通过对指针域的操作实现的。我们通常用结构体定义结点。
在这里插入图片描述

1、单向链表:每个节点只有一个指针指向下一个节点,上图即为单链表
2、双向链表:每个节点有两个指针,分别指向下一个节点和上一个节点
3、单向循环链表:单向链表的基础上,让最后一个节点指向第一个节点
4、双向循环链表:双向链表的基础上,连接最后一个结点和第一个结点

本文要介绍的是如上图所示的有头有尾的单向链表

二、链表的封装

(一)创建一个空的单向链表
typedef struct node_t//把结构体类型冲重定义为LinkNode_t
{
	int data;
	struct node_t *next;
}LinkNode_t;

/*
功能:创建一个链表的头节点(堆空间)
返回值:头结点的地址
*/
LinkNode_t *createLinkList()	//为简化程序,本程序没有做申请空间失败的检查
{
	LinkNode_t* node = (LinkNode_t*)malloc(sizeof(LinkNode_t));
	node->next = NULL;//最后一个结点的next一定指向NULL
	return node;//返回指向头结点的指针
}

注意:下面我们调用这个函数时:

LinkNode_t* head = createLinkList();

此时便创建了一个新的单向链表,函数返回的结点指针是这个链表的头结点,这个结点我们不存储数据,此时链表中只有一个结点即 head,但这个链表是一个空链表。
在这里插入图片描述

(二)判断链表是否为空
/* 
功能:判断链表是否为空
参数:链表头结点指针
返回值:空返回1 非空返回0
*/
int emptyLinkList(LinkNode_t* p)
{
	return p->next == NULL;
}

从上图可以看到,传入头结点指针head,head->next=NULL ,此时链表为空

(二)计算链表中除空头结点外的结点个数
/*
 功能:计算链表中除空头结点之外的结点个数
 参数:链表头结点指针
 返回值:结点个数
 */
int lengthLinkList(LinkNode_t* p)
{
	int count = 0;
	LinkNode_t* t = p->next; //不能直接用head作为判断条件
	while(t!=NULL)			 //不要轻易动头结点head
	{
		count++;
		t = t->next;
	}
	return count;
}

在这里插入图片描述

(三)插入新结点
/*
功能:在链表中插入新结点并存入数据
参数1:链表头结点指针
参数2:插入的位置(第一个有数据的结点位置为0)
参数3:新结点的值
返回值:成功返回0 失败返回-1
*/
int insertLinkList(LinkNode_t* p,int pos,int x)
{
	if(pos<0||pos>lengthLinkList(p))//对pos做合法性检查
	{	
		return -1;
	}
	LinkNode_t* newNode = (LinkNode_t*)malloc(sizeof(LinkNode_t));
	newNode->data = x;//赋值
	newNode->next = NULL;  //暂时指向空

在这里插入图片描述
注意:我们要找到待插入位置的前一个结点的地址,插入结点时,先使待插入结点的指针域指向后一个结点,再使前一个结点的指针域指向待插入的结点。

//连接上一段代码
	LinkNode_t* t=p;//指向空头结点,从空头结点开始遍历
	int i;
	for(i = 0;i < pos;i++)
	{
		t = t->next;
	}
	//将结点插入链表
	newNode->next = t->next; //下图中第一步
	t->next = newNode; 		 //下图中第二步
	return 0;
}

在这里插入图片描述

(四)获得某位置结点的值
/*
功能:获得链表中某结点的值
参数1:链表头结点指针
参数2:位置(第一个有数据的结点位置为0)
返回值:失败返回-1 成功返回参数2位置结点的值
*/
int getLinkList(LinkNode_t* p,int pos)
{
	if(pos<0||pos>lengthLinkList(p))
	{
		return -1;
	}
	int i;
	LinkNode_t* t = p->next;
	for(i=0;i<pos;i++)//遍历到pos位置
	{
		t = t->next;
	}
	return t->data;
}
(五)遍历输出链表中所有结点的值
/*
功能:遍历输出链表中结点的值
参数:链表头结点指针
返回值:无
*/
void printLinkList(LinkNode_t* p)
{
	printf("\n有效数据结点数:%d\n",lengthLinkList(p));
	int count = 0;
	LinkNode_t* t = p->next;
	while(t!=NULL)
	{
		printf("NODE[%d]:%d\n",count,t->data);
		t = t->next;
		count++;
	}
}
(六)删除链表中的结点
/*
功能:删除参数位置结点
参数1:链表头结点指针
参数2:删除位置
返回值:失败返回-1 成功返回0
*/
int deleteLinkList(LinkNode_t* p,int pos)
{
	if(pos<0||pos>=lengthLinkList(p))
	{
		return -1;
	}
	int i;
	LinkNode_t* t = p;//指向空头
	for(i=0;i<pos;i++)//循环结束得到待删除结点的前一个结点的位置
	{
		t = t->next;
	}

要删除这个结点,只需要它前一个结点的指针域指向它的下一个结点,然后释放这段内存

在这里插入图片描述

注意:在改变 t 的指针域之前,我们要先保存这个待删除结点的地址,否则会丢失这个地址

	LinkNode_t* s = t->next;//保存要删除结点的地址
	t->next = t->next->next;//连接待删除结点的前后两个结点
	free(s);
	return 0;
}
(七)清空链表
/*
功能:清空链表
参数:链表头结点指针
*/
void clearLinkList(LinkNode_t* p)
{
	while(p!=NULL)
	{
		LinkNode_t* t = p;
		p = p->next;
		free(t);
	}
}
(八)主函数及功能测试
int main()	
{	
	LinkNode_t* head = createLinkList();
	insertLinkList(head,0,10);//10
	insertLinkList(head,0,20);//20 10
	insertLinkList(head,1,30);//20 30 10
	insertLinkList(head,3,40);//20 30 10 40
	printLinkList(head);//20 30 10 40
	insertLinkList(head,4,50);//20 30 10 40 50
	printf("\n%d\n",getLinkList(head,0));//20
	deleteLinkList(head,0);//30 10 40 50
	printLinkList(head);
	return 0;
}

在这里插入图片描述

三、完整源代码

写在结尾:

  • 有错误的地方欢迎大家批评指正
  • 创作不易,如果对你有帮助的话给个赞和关注鼓励一下吧!
#include<stdio.h>
#include<stdlib.h>

typedef struct node_t
{
	int data;
	struct node_t* next;
}LinkNode_t;

LinkNode_t* createLinkList();
int emptyLinkList(LinkNode_t* p);
int lengthLinkList(LinkNode_t* p);
int insertLinkList(LinkNode_t* p,int pos,int x);
void printLinkList(LinkNode_t* p);
int getLinkList(LinkNode_t* p,int pos);
int deleteLinkList(LinkNode_t* p,int pos);
void clearLinkList(LinkNode_t* p);
void ReversePrintList(LinkNode_t* p);

int main()	
{	
	LinkNode_t* head = createLinkList();//指向头的指针不能轻易改变
	insertLinkList(head,0,10);//10
	insertLinkList(head,0,20);//20 10
	insertLinkList(head,1,30);//20 30 10
	insertLinkList(head,3,40);//20 30 10 40
	printLinkList(head);//20 30 10 40
	insertLinkList(head,4,50);//20 30 10 40 50
	printf("\n%d\n",getLinkList(head,0));//20
	deleteLinkList(head,0);//30 10 40 50
	printLinkList(head);
	return 0;
}

/*
 功能:创建一个链表的空头结点(堆空间)返回头结点的地址
 参数:新结点的值
 返回值:空链表的头结点指针
 */
LinkNode_t* createLinkList()
{
	LinkNode_t* node =(LinkNode_t*)malloc(sizeof(LinkNode_t));
	node->next = NULL;
	return node;
}

/* 
功能:判断链表是否为空
参数:链表头结点指针
返回值:空返回1 非空返回0
*/
int emptyLinkList(LinkNode_t* p)
{
	return p->next == NULL;
}

/*
 功能:计算链表中除空头结点之外的结点个数
 参数:链表头结点指针
 返回值:结点个数
 */
int lengthLinkList(LinkNode_t* p)
{
	int count = 0;
	LinkNode_t* t = p->next;
	while(t!=NULL)
	{
		count++;
		t = t->next;
	}
	return count;
}

/*
功能:在链表中插入新结点并存入数据
参数1:链表头结点指针
参数2:插入的位置(第一个有数据的结点位置为0)
参数3:新结点的值
返回值:成功返回0 失败返回-1
*/
int insertLinkList(LinkNode_t* p,int pos,int x)
{
	if(pos<0||pos>lengthLinkList(p))
	{	
		return -1;
	}
	LinkNode_t* newNode = (LinkNode_t*)malloc(sizeof(LinkNode_t));
	newNode->data = x;//赋值
	newNode->next = NULL;

	LinkNode_t* t=p;//指向空头,从空头开始遍历
	int i;
	for(i = 0;i < pos;i++)
	{
		t = t->next;
	}
	//将结点插入链表
	newNode->next = t->next;
	t->next = newNode;
	return 0;
}
/*
功能:获得链表中某结点的值
参数1:链表头结点指针
参数2:位置(第一个有数据的结点位置为0)
返回值:失败返回-1 成功返回参数2位置结点的值
*/
int getLinkList(LinkNode_t* p,int pos)
{
	if(pos<0||pos>lengthLinkList(p))
	{
		return -1;
	}
	int i;
	LinkNode_t* t = p->next;
	for(i=0;i<pos;i++)
	{
		t = t->next;
	}
	return t->data;
}

/*
功能:遍历输出链表中结点的值
参数:链表头结点指针
返回值:无
*/
void printLinkList(LinkNode_t* p)
{
	printf("\n有效数据结点数:%d\n",lengthLinkList(p));
	int count = 0;
	LinkNode_t* t = p->next;
	while(t!=NULL)
	{
		printf("NODE[%d]:%d\n",count,t->data);
		t = t->next;
		count++;
	}
}
/*
功能:删除参数位置结点
参数1:链表头结点指针
参数2:删除位置
返回值:失败返回-1 成功返回0
*/
int deleteLinkList(LinkNode_t* p,int pos)
{
	if(pos<0||pos>=lengthLinkList(p))
	{
		return -1;
	}
	int i;
	LinkNode_t* t = p;//指向空头
	for(i=0;i<pos;i++)
	{
		t = t->next;
	}
	LinkNode_t* s = t->next;//保存要删除结点的地址
	t->next = t->next->next;//连接待删除结点的前后两个结点
	free(s);
	return 0;
}

/*
功能:清空链表
参数:链表头结点指针
*/
void clearLinkList(LinkNode_t* p)
{
	while(p!=NULL)
	{
		LinkNode_t* t = p;
		p = p->next;
		free(t);
	}
}
/*
功能:反向打印链表
参数:链表头结点指针
*/ 
void ReversePrintList(LinkNode_t* p)
{
	static int count=0;
	if(p->next!=NULL)
	{
		count++;
		ReversePrintList(p->next);
		count--;
		printf("NODE[%d]:%d\n",count,p->next->data);
		return;
	}
	return;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值