带有和不带有前导节点的单链接列表的问题

电脑杂谈  发布时间:2020-04-25 07:23:32  来源:网络整理

时间结点与节点_c# 自己写的链表类 如何释放其中的结点空间_不带头结点的单链表

#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
typedef struct Node
{
	int data;
	struct Node *next;
}NODE,*PNODE;
PNODE Create2()
{
	int i,n;
	PNODE pHead,pTail,pNew;
	pHead=(PNODE)malloc(sizeof(NODE));
	if(NULL==pHead)
		exit(-1);
	pHead->next=NULL;	//生成头结点
	pTail=pHead;
	printf("请输入数据元素的个数:");
	scanf("%d",&n);
	for(i=0;i<n;i++)
	{
		pNew=(PNODE)malloc(sizeof(NODE));
		if(NULL==pNew)
			exit(-1);
		printf("请输入第%d个元素的值:",i+1);
		scanf("%d",&pNew->data);
		pTail->next=pNew;
		pTail=pTail->next;
		pNew->next=NULL;
	}
	return pHead;
}
void Destroy(PNODE pHead)
{	//销毁pHead指向的单链表:头结点也不存在只剩一个头指针,且头指针为NULL
	PNODE p;
	while(pHead!=NULL)
	{
		p=pHead->next;
		free(pHead);
		pHead=p;
	}
}
void Traverse(PNODE pHead)
{
	PNODE p=pHead->next;
	while(p!=NULL)
	{
		printf("%d	",p->data);
		p=p->next;
	}
	printf("\n");
}
int main()
{
	PNODE pHead=Create2();
	printf("初始化后的元素:");
	Traverse(pHead);
	Destroy(pHead);
	printf("销毁单链表后:");
	Traverse(pHead);
	return 0;
}

代码运行后:

在这里插入图片描述

通过观察和比较,表面问题出现在导线()的函数中,

两个导线的区别是: 首先在引导节点的单链列表中执行

p=pHead->next;

不带头结点的单链表_时间结点与节点_c# 自己写的链表类 如何释放其中的结点空间

然后判断p是否为空并输出p->数据;

然后在没有头节点的单链接列表中首先执行它

p=pHead;

然后判断p是否为空并输出p->数据;

两者之间的区别在于头节点的输出是pHead-> data;头节点的输出是pHead-> next-> data;

让我们分析此问题的原因:

时间结点与节点_c# 自己写的链表类 如何释放其中的结点空间_不带头结点的单链表

在这里插入图片描述

注意: 上图显示了在没有头节点的情况下执行清除功能或销毁功能的情况. 具有头节点的Destroy函数的执行图只是在第一个节点之前添加头节点,但是引起的问题是相同的.

首先,在Clear函数中,传递的实际参数为pHead,形式参数也为pHead,这等效于值传递,因此实际参数和形式参数指向相同的元素,但是当链表被销毁,形式参数是pHead正在执行. 当释放第一个节点时,形式参数pHead指向下一个元素,但是实际参数指向第一个节点并已被释放,因此此时实际参数pHead的内容是乱码,不再是0x01,而是遍历时,没有头的节点将首先确定pHead是否为空,因为它是乱码,所以不为空,因此它将输出pHead->数据为乱码,pHead指向其下一个节点,检测到下一个节点是空,并跳出while循环,因此执行结果将看到乱码,并且pHead指向前导节点的循环之前的下一个节点,也就是说,其下一个节点为空,因此它是空的将不会执行while循环并直接结束,并且您不会看到乱码.

如何解决此类问题?

以下提供了两种解决方案(以不带头节点的单链表为例)

第一个: 使用C语言方法

时间结点与节点_不带头结点的单链表_c# 自己写的链表类 如何释放其中的结点空间

具体代码如下:

void Clear(PNODE *pHead)
{
	PNODE p=*pHead;
	while(p!=NULL)
	{
		p=(*pHead)->next;
		free(*pHead);
		*pHead=p;
	}
}
int main()
{
	PNODE pHead=Create2();
	printf("初始化后元素:");
	Traverse(pHead);
	printf("销毁链表后:\n");
	Clear(&pHead);
	Traverse(pHead);
	return 0;
}

只需修改以上两个功能

修改后的函数执行图如下:

在这里插入图片描述

第一个节点释放时

不带头结点的单链表_时间结点与节点_c# 自己写的链表类 如何释放其中的结点空间

在这里插入图片描述

注意: 形式参数pHead存储实际参数pHead变量的地址,通过在函数中使用* pHead,表示形式参数pHead的内容,即实际参数pHead,可以在前面看到与修改之后的不同之处在于,修改之前,功能的破坏是通过形式参数进行的不带头结点的单链表,而实际参数不会移动. 修改后,实际参数由形式参数控制,实际参数破坏了功能. 这显示了两者之间的本质区别

第二: C ++引用实现

void Clear(PNODE &pHead)
{
	PNODE p=pHead;
	while(p!=NULL)
	{
		p=pHead->next;
		free(pHead);
		pHead=p;
	}
}
int main()
{
	PNODE pHead=Create2();
	printf("初始化后元素:");
	Traverse(pHead);
	printf("销毁链表后:\n");
	Clear(pHead);
	Traverse(pHead);
	return 0;
}

根据原始错误不带头结点的单链表,您只需要将Clear函数的形式参数更改为(PNODE&pHead).

另一个值是,在没有前导节点的单链接列表上进行操作时,必须以上述两种方式传递参数,这与插入和删除操作中的前导节点单链接列表有重要区别. <


本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-188091-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      热点图片
      拼命载入中...