单链表

单链表

概念:链表是一个物理存储结构非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。


在这里插入图片描述

链表不单这一种,还分为带头和不带头,单向和双向,循环和非循环,组合下来一共8种。

但常用的就是单链表带头双向循环链表

上图中的例子就是一个单链表

  1. 无头单向非循环链表:结构简单,一般不会用来单独存储数据。实际中更多的是作为其他数据的子结构
  2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表,虽然结构复杂,但是在代码实现中有很多优势,反而变得简单了。


单链表实现

//定义结点
typedef int SLTDataType
typedef struct SListNode
{
    SLTDataType data;
    struct SListNode* next;
}SListNode;

//定义链表的头和尾
typedef struct SList
{
    SListNode* head;
    SListNode* tail;
}SList;

单链表打印

void SListPrint(SList* plist)
{
    SListNode* cur = plist->head;
    while(cur != plist->tail->next)//注意这里cur一定是走到空时才打印完毕
    {
        printf("%d ",cur->data);
        cur=cur->next;
    }
    printf("\n");
}

在这里插入图片描述

单链表初始化

void SListInit(SList* plist)
{
    assert(plist);
    plist->head=plist->tail=NULL;
}

单链表销毁

void SListDestory(SList* plist)
{
	SListNode* cur;
	assert(plist);
	cur = plist->_head;//将cur作为销毁的对象,初始化为头节点
	while (cur != NULL)
	{
		SListNode* next = cur->next;//用next记录cur的下一个,使cur依次向后移动
		free(cur);
		cur = next;//让节点依次向后走,达到逐一释放的目的
	}

	plist->head = plist->tail = NULL;//所有节点释放之后,将头尾结点置为空
}

在这里插入图片描述

创建一个结点

SListNode* BuySListNode(SLTDataType x)
{
	SListNode* node = malloc(sizeof(SListNode));
	assert(node);
	node->data = x;
	node->next = NULL;

	return node;
}

在这里插入图片描述

单链表尾插结点

void SListPushBack(SList* plist, SLTDataType x)
{
	assert(plist);
	if (plist->tail == NULL)//说明链表为空
	{
		plist->head =plist->tail = BuySListNode(x);//即尾插数据即是头又是尾
	}
	else
	{ //说明链表不为空
		SListNode* newnode = BuySListNode(x);//为新节点创建数据部分和指针部分
	    plist->_tail->next=newnode;//链表的尾节点的指针指向新节点
		plist->_tail= newnode;//新节点就成为新的尾节点

	}
}

在这里插入图片描述

单链表尾删结点

void SListPopBack(SList* plist)
{
	SListNode* prev, *cur;//prev的作用是为了记录cur前一个节点,cur作为删除目标
	assert(plist);

	prev = NULL;//prev初始化为NULL
	cur = plist->head;//将cur初始化为头结点

	if (cur->next == NULL)//说明只有一个节点
	{
		free(cur);//直接释放cur
		plist->head = NULL;//将头结点置空
	}
	else
	{
		while (cur->next)//说明节点不止一个,cur,prev依次向后走,直到cur走到链表末尾
		{
			prev = cur;
			cur = cur->next;
		}

		free(cur);//此时cur在末尾位置,将它释放
		prev->next = NULL;//将prev的指针置为NULL
	}
}

在这里插入图片描述

单链表头插结点

void SListPushFront(SList* plist, SLTDataType x)
{
	SListNode* newnode;
	assert(plist);

	newnode = BuySListNode(x);//为新节点赋数值和指针
	newnode->next = plist->head;//将新节点的指针部分指向头结点
	plist->head = newnode;//将新节点做为新的头结点
}

在这里插入图片描述

单链表头删结点

void SListPopFront(SList* plist)
{
	SListNode* cur;
	assert(plist);

	cur = plist->head->next;//用cur保存头结点的下一个结点
	free(plist->head);//将头结点释放
	plist->head = cur;//将cur变成新的头结点 
}

在这里插入图片描述

单链表查找一个结点

SListNode* SListFind(SList* plist, SLTDataType x)
{
	assert(plist);
	SListNode* cur = plist->head;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
    }
	return NULL;
}

在这里插入图片描述

在指定位置的后面插入一个结点

void SListInsertAfter(SListNode* pos, SLTDataType x)
{
	SListNode* cur, *newnode;
	assert(pos);

	cur = pos->next;//将cur记录为pos的下一个结点
	newnode = BuySListNode(x);
	pos->next = newnode;//将新节点赋给pos的下一个
	newnode->next = cur;//将新节点的下一个置为cur
}

在这里插入图片描述

删除指定位置的后面的结点

void SListEraseAfter(SListNode* pos)
{
    SListNode* cur;
	assert(pos);
	if (pos->next == NULL)
	{
		return ;
	}
	cur = pos->next;
	pos->next = cur->next;
	free(cur);
	cur = NULL;
}

在这里插入图片描述

删除链表中指定值第一次出现的结点

void SListRemove(SList* plist, SLTDataType x)
{
	assert(plist);
	if (plist->head->data == x)
	{
		SListPopFront(plist);//如果头结点就是指定值,直接头删
		return ;
	}
	SListNode* prev;
	SListNode* cur = plist->head;
	while (cur)
	{
		if (cur->data == x)
		{
			prev->next = cur->next;//当前prev在cur的前一个节点
			free(cur);
			cur = NULL;
			break;
		}
		else
		{
            
			prev = cur;//prev保存当前cur所在节点
			cur = cur->next;//cur走向下一个节点,此时prev在cur的前一个节点
		}
	}
}

在这里插入图片描述

删除链表中指定值的所有节点

void SListALLRemove(SList* plist, SLTDataType x)
{
	assert(plist);
	if (plist->_head->_data == x)
	{
		SListPopFront(plist);
		return;
	}
	SListNode* prev;
	SListNode* cur = plist->_head;
	while (cur != plist->_tail->_next)
	{
		if (cur->_data == x)
		{
			prev->_next = cur->_next;
			cur = prev->_next;
		}
		else
		{
			prev = cur;
			cur = cur->_next;
		}
	}
}

在这里插入图片描述

测试函数

void TestSList()
{
	SList sl;
	SListInit(&sl);
	SListPushBack(&sl, 1);
	SListPushBack(&sl, 2);
	SListPushBack(&sl, 3);
	SListPushBack(&sl, 2);
	SListPushBack(&sl, 4);
	SListPushBack(&sl, 2);
	SListPushBack(&sl, 5);
	SListPushBack(&sl, 2);
	SListPrint(&sl);
    
	SListPopBack(&sl);
	SListPushFront(&sl, 7);
	SListPopFront(&sl);
    
	SListNode* pos = SListFind(&sl, 2);
    
	SListInsertAfter(pos,100);
	SListEraseAfter(pos);
	SListRemove(&sl, 3);
    SListRemove(&sl,2);
    
	SListPrint(&sl);
    
	SListDestory(&sl);
}

链表增删一定要兼顾前后,处理好前后结点的链接关系,一定要保持链表连通

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值