数据结构初阶之单链表(一)

链表:按需申请释放

注意:链式结构在逻辑上是连续的,但是在物理上不一定连续

//signal list table 单链表
//signal link table 单链表
typedef int SLTataType;
typedef struct SListNode  //signal 单的
{
	SLTataType data;  //数据域
	struct SListNode* next;   //指针域
}SLTNode;

先来浅浅写一个打印函数

void PrintSLT(SLTNode * phead)
{
	SLTNode * cur = phead;
	while (cur == NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

初始化链表

    int n;
	printf("请输入链表的长度:");
	scanf("%d", &n);
	printf("\n请依次输入每个结点的值:");

 由于需要定义一个结构体指针来存放链表中第一个数据的地址,因此

SLTNode* plist = NULL;

使用for循环来完成链表数据的输入,但是由于后面的诸多函数(例如增删查改)均需要节点函数,因此我们先来写一个节点函数

SLTNode* BuySListNode(SLTDataType x)
{
	SLTNode* newnode =(SLTNode *) malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

完成节点函数后,利用for循环实现链表的连接:

    for (size_t i = 0; i < n; i++)
	{
		int val;
		scanf("%d", &val);
		SLTNode* newnode = BuySListNode(val);
	}

连接完成以后,我们考虑到,假如 plist==NULL;

        if (plist == NULL)
		{
			plist = newnode;
		}

但是假如plist != NULL,则有头插和尾插两种情况,但由于尾插情况较为复杂,而且我们现在是在初始化链表,因此我们先讨论头插情况。

物理图如下:

 代码实现如下:

        newnode->next = plist;
		plist = newnode;

即代码汇总为

	for (size_t i = 0; i < n; i++)
	{
		int val;
		scanf("%d", &val);
		SLTNode* newnode = BuySListNode(val);

		// 头插
		newnode->next = plist;
		plist = newnode;
	}

那么问题来了,假如链表为空,需要对plist == NULL,进行分情况讨论吗,答案是不需要的,咱们看图说话。

整体代码如下:

void PrintSLT(SLTNode* phead)
{
	SLTNode * cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

SLTNode* BuySListNode(SLTDataType x)
{
	SLTNode* newnode =(SLTNode *) malloc(sizeof(SLTDataType));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}
void Test1()
{
	int n;
	printf("请输入链表的长度:");
	scanf("%d", &n);
	printf("\n请依次输入每个结点的值:");
	SLTNode* plist = NULL;
	for (size_t i = 0; i < n; i++)
	{
		int val;
		scanf("%d",& val);
		SLTNode* newnode = BuySListNode(val);

		//头插
		newnode->next = plist;
		plist = newnode;
	}

	PrintSLT(plist);

}
int main()
{
	Test1();
	return 0;
}

运行如下:

现在来实行尾插 :

代码实现如下:

void SLTPushBack(SLTNode* phead, SLTDataType x)
{
	//phead是形参 plist是实参 
	SLTNode* newnode = BuySListNode(x);
	while (phead->next)
	{
		phead = phead->next;
	} 
	phead->next = newnode;
}

运行如下:

 现在又有一个新的问题来了,假如链表为空,那么要怎么插入呢?

此时就需要改变结构体指针,具体参考如下

void SLTPushBack(SLTNode** phead, SLTDataType x)
{
	//phead是形参 plist是实参 
	SLTNode* newnode = BuySListNode(x);
	if (*phead == NULL)
	{
		*phead = newnode;
	}
	else
	{
		SLTNode* tail = *phead;
		while (tail->next)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

小tips:

1.改变结构体,需要用结构体指针

2.改变结构体指针,  需要用结构体指针的指针

头插尾插都写好了,我们再来写尾删头删

先来看结构图:

代码实现如下: 

void SLTPopBack(SLTNode** phead, SLTDataType x)
{
	//1.空
	assert(*phead);//暴力检查
	//2.一个节点
	//3.一个以上节点
	if ((*phead)->next == NULL)
	{
		free(*phead);
		*phead = NULL;
	}
	else
	{
		//法一:
		SLTNode* tailPrev = NULL;//表示tail前的那个结构体
		SLTNode* tail = *phead;
		while (tail->next)
		{
			tailPrev = tail;
			tail = tail->next;
		}
		free(tail);
		tailPrev->next = NULL;
		//法二:
		SLTNode* tail = *phead;
		while (tail->next->next)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}

运行如下:

头删

代码实现如下:

void SLTPopFront(SLTNode** phead)
{
	//空
	assert(*phead);

	//非空
	SLTNode* newhead = (*phead)->next;
	free(*phead);
	*phead = newhead;
}

运行如下:

接下来我们要进行:

//在pos位置插入x
void SLTInsert(SLTNode** phead, SLTNode* pos, SLTDataType x);

//在pos以后插入x
void SLTInsertAfter(SLTNode* pos, SLTDataType x);

//删除pos位置
void SLTErase(SLTNode** phead, SLTNode* pos, SLTDataType x);

//删除pos的后一个位置
void SLTEraseAfter(SLTNode* pos, SLTDataType x);

欲知后事如何,请看下回分解,今天就先到这里了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

囚徒玩电脑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值