C语言实现链表常用接口(单向无头非循环链表)

定义节点

链表节点是由数据+指针组成的结构体。

typedef int dataType;

typedef struct ListNode ListNode;

struct ListNode
{
	dataType data;
	ListNode* next;
};

创建节点

开辟一块内存空间存放节点,并将该节点地址作为返回值。

ListNode* createNode(dataType n)
{
	ListNode* node = malloc(sizeof (ListNode));
	node->data = n;
	node->next = NULL;
	return node;
}

尾插

当链表为空时,尾插等同于头插,由于没有哨兵位的头结点,所以需要对实参即存放链表首节点地址的指针变量进行修改,因此在传入参数时,我们选择传入实参的地址,同时形参也需要用二级指针来接收;当链表不为空时,需要遍历找到尾节点,将尾节点的next指向创建的新节点。

void SLTPushBack(ListNode** pplist, dataType n)
{
	ListNode* node = createNode(n);
	if (*pplist == NULL)
	{
		*pplist = node;
	}
	else
	{
		ListNode* cur = *pplist;
		while (cur->next)//遍历找尾节点
			cur = cur->next;
		cur->next = node;//将尾节点链向新创建的节点 新节点作为新的尾节点
	}
}

头插

无论链表是否为空,头插都会修改首节点的地址,将创建的新节点作为新的首节点,需要修改实参即存放链表首节点地址的指针变量,所以需要传入实参的地址,同时形参也用二级指针来接收。

void SLTPushFront(ListNode** pplist, dataType n)
{
	ListNode* node = createNode(n);
	//将创建的新节点链向当前首节点 再将创建的新节点作为新的首节点
	node->next = *pplist;
	*pplist = node;
}

尾删

链表只有一个节点时,free该节点后,链表变为空链表,因此需要将首节点地址置为NULL;链表有多个节点时,需要遍历找到尾节点和尾节点的前一个节点,free尾节点并让尾节点的前一个节点的next指向NULL。

void SLTPopBack(ListNode** pplist)
{
	if (*pplist == NULL)//链表为空时
		return;
	if ((*pplist)->next == NULL)//链表只有一个节点时
	{
		free(*pplist);
		*pplist = NULL;
	}
	else//当链表有两个及以上节点时
	{
		ListNode* cur = *pplist;
		ListNode* prev = NULL;
		while (cur->next)
		{
			prev = cur;
			cur = cur->next;
		}
		free(cur);
		prev->next = NULL;
	}
}

头删

只要链表不为空,只需要free当前首节点,将第二个节点(链表只有一个节点时为NULL)作为新的首节点地址。由于free掉当前首节点后会无法找到第二个节点,所以在free之前需要保存首节点的下一个节点的地址。

void SLTPopFront(ListNode** pplist)
{
	if (*pplist == NULL)
		return;
	struct ListNode* next = (*pplist)->next;
	free(*pplist);
	*pplist = next;
}

后插

在实现后插之前,需要先实现查找节点的功能,这里我们用data值作为查找的依据,找到对应的节点就返回节点的地址,没有找到节点或链表为空就返回NULL。注意:在实际项目中,最好将唯一能够确定一个节点的数据作为查找的依据。

ListNode* SLTFindNode(ListNode* plist, dataType n)
{
	ListNode* cur = plist;
	while (cur)
	{
		if (cur->data == n)
			return cur;
		cur = cur->next;
	}
	return NULL;
}

后插是在查找到一个节点的基础上,将找到的这个节点的地址作为参数传入,把创建的新节点的next指向找到的这个节点的下一个节点(如果找到的节点是尾节点,则下一个节点为NULL),再把找到的这个节点的next指向创建的新节点。

void SLTInsertAfter(ListNode* pos, dataType n)
{
	ListNode* node = createNode(n);
	node->next = pos->next;
	pos->next = node;
}

前插

在不传入首节点地址的情况下实现前插,可以先进行后插,再将插入节点的data值与查找到的这个节点的data值进行交换,这样也相当于“变相”实现了前插。这种实现方式不仅避免了传递不必要的参数,并且当查找到的节点为首节点时,便需要进行头插,使用这种实现方式也不用修改首节点地址。

void SLTInsertBefore(ListNode* pos, dataType n)
{
	SLTInsertAfter(pos, n);
	dataType tmp = pos->data;
	pos->data = pos->next->data;
	pos->next->data = tmp;
}

删除任意节点

要删除的节点是首节点时,直接复用头删接口即可。要删除的节点不是首节点时,需要遍历找到要删除的节点的前一个节点,将前一个节点的next指向要删除的节点的next,再free要删除的节点。

void SLTErase(ListNode** pplist, ListNode* pos)
{
	if (*pplist == pos)//要删除的节点就是首节点
	{
		SLTPopFront(pplist);
		return;
	}
	//要删除的节点不是首节点 遍历找到要删除的节点的前一个节点
	ListNode* cur = *pplist;
	while (cur->next != pos)
	{
		cur = cur->next;
	}
	cur->next = pos->next;
	free(pos);
}

打印所有节点

为了观察链表的逻辑结构,我们需要对链表的节点进行打印。

void SLTPrint(struct ListNode* plist)
{
	struct ListNode* cur = plist;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值