87.【C语言】数据结构之链表的头插和尾插

1.尾插函数

非空链表的代码示例

在SList.h写SLTPushBack函数的声明

下面的代码写入SList.c中

void SLTPushBack(SLTNode* phead, SLTDataType x)
{
	SLTNode*newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return;//错误返回
	}
	//为结点初始化
	newnode->data = x;
	newnode->next = NULL;

	//找尾指针
	SLTNode* tail = phead;//定义尾指针初始化为头指针的值
	while (tail != NULL)
	{
		tail = tail->next;
	}
	tail->next = newnode;//改写原来的尾结点存储的指针为新尾节点的指针
}

注意

1.函数的一开始不需要断言,空链表也能尾插

2.尾插要提供新的结点(newnode)(内含数据和指针),需要开辟内存空间,之后初始化结点,并和前一个结点联系上(称为结点的链接)

3.一个典型的找尾指针的错误代码

	SLTNode* tail = phead;//定义尾指针初始化为头指针的值
	while (tail != NULL)
	{
		tail = tail->next;
	}
	tail = newnode;

只有当tail为NULL时才能退出循环,貌似找到了尾指针,出了函数局部变量会销毁,其实新结点是链接不上的!

将tail = newcode;改为tail->next = newcode;才可以!

非空链表的尾插核心:改写原来的尾结点存储的指针为新尾节点的指针

附:物理结构图

像下面这样,新结点是链接不上的

空链表的尾插

直接让phead指向新结点

void SLTPushBack(SLTNode* phead, SLTDataType x)
{
	SLTNode*newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return;//错误返回
	}
	//为结点初始化
	newnode->data = x;
	newnode->next = NULL;

	if (phead == NULL)
	{
		phead = newnode;
	}
	else
	{
		//找尾指针
		SLTNode* tail = phead;//定义尾指针初始化为头指针的值
		while (tail != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;//改写原来的尾结点存储的指针
	}
}

设计main.c

#include "SList.h"
void TestSList1()
{
	SLTNode* plist = NULL;
	SLTPushBack(plist, 1);
	SLTPushBack(plist, 2);
	SLTPushBack(plist, 3);
	SLTPushBack(plist, 4);
	SLTPushBack(plist, 5);
	SLTPushBack(plist, 6);
	SLTPushBack(plist, 7); 
	SLTPushBack(plist, 8);
	SLTPrint(plist);
}

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

运行后发现什么输出都没有

问题发现

VS2022进入调试模式,打开监视窗口

在第37行卡住了,F11无法继续调试下去,没有反应

问题分析

进入main函数后,调用了TestSList1函数,设plist为NULL,之后调用SLTPushBack函数,malloc开辟空间后,为newnode赋了一个地址,进入if语句,newnode不为NULL,则为newnode结构体中的data和next初始化,进入if语句,phead为NULL,则将newnode赋值给phead,SLTPushBack函数返回后,phead仍然为空指针!也就是说,SLTPushBack函数并没有改变phead的值!

可以想到,这是函数的传调用

解决方法

将传调用改为传调用即可(有关两种调用的概念参见29.【C语言】函数系列中 自定义函数)

修改后的完整的代码

SList.c
#include "SList.h"
void SLTPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL");//读到链表的结尾存储的为空指针
}

//pphead为指向phead的指针(pphead为二级指针)
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return;//错误返回
	}
	//为结点初始化
	newnode->data = x;
	newnode->next = NULL;

	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾指针
		SLTNode* tail = *pphead;//定义尾指针初始化为头指针的值
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;//改写原来的尾结点存储的指针
	}
}

注:想修改plist,必须用plist的地址,即pphead存储的值

 main.c
#include "SList.h"
void TestSList1()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPushBack(&plist, 5);
	SLTPushBack(&plist, 6);
	SLTPushBack(&plist, 7); 
	SLTPushBack(&plist, 8);
	SLTPrint(plist);
}

int main()
{
	TestSList1();
	return 0;
}
SList.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
typedef int SLTDataType;

typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

void SLTPrint(SLTNode* phead);
void SLTPushBack(SLTNode** pphead, SLTDataType x);

注意头文件的函数声明也要改过来

2.头插函数

非空链表的代码示例

在SList.h写SLTPushFront函数的声明

SList.c写入

头插函数也需要开辟空间并初始化结点,因此可以单独将此过程封装为一个函数BuySLTNode

SLTNode* BuySLTNode(SLTDataType x)
{
		SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return NULL;//错误返回
	}
	//为结点初始化
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

3.用VS的监视窗口来查看链表

输入plist

猜你喜欢

转载自blog.csdn.net/2401_85828611/article/details/143096622