1、关于尾插法的pTail=pNew的疑惑点
今天学习到了尾插法,但是遇到了一个让我绞尽脑汁都没想明白的点,后面跟室友讨论了一下,才感觉到思想逐渐清晰。先看下面一段代码:
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
typedef struct Node
{
int data; //数据域
struct Node *next; //指针域
} NODE,*PNODE;
PNODE create_list(void);
void traverse_list(PNODE pHead);
int main(void)
{
PNODE pHead=NULL; //
pHead=create_list(); //创建一个肺循环单链表,并将改链表的头结点的地址赋值给pHead
traverse_list(pHead);
return 0;
}
PNODE create_list(void)
{
int len; //用来存放有效结点的个数
int i;
int val; //用来临时存放用户输入的结点的值
PNODE pHead=(PNODE)malloc(sizeof(NODE)); //此处地方的PHead与下面的PHead无关系,这里的是一个局部变量
if(pHead==NULL)
{
printf("分配失败,程序终止!\n");
exit(-1);
}
PNODE pTail=pHead; pTail->next=NULL;
printf("请输入你需要生成链表结点的个数:len=");
scanf("%d",&len);
for(i=0;i<len;i++)
{
printf("请输入%d个结点的值:",i+1);
scanf("%d",&val);
//需要一个接受专门接受数据的新节点
PNODE pNew=(PNODE)malloc(sizeof(NODE));
if(NULL==pNew)
{
printf("分配失败,程序终止!\n");
exit(-1);
}
pNew->data =val;
pTail->next=pNew;
pNew->next=NULL;
pTail=pNew; //这里如何理解?
}
return pHead;
}
void traverse_list(PNODE pHead)
{
.....
}
如代码注释所示,在pTail=pNew这里,我产生了一个疑惑点,为什么会这里将pNew赋值给pTail后,pTail又成为了此链表的尾结点了呢?其实一开始我就没有弄清楚pTail的定义是怎样的,一开始定义了一个指针,这个指针永远指向最后一个结点,这个指针就是pTail。为什么一开始我会疑惑呢,回到上面标蓝的字体上,会发现我的逻辑与思维的错误,因为一开始我就把pTail看成了是一个尾结点,以至于前面学习的指针与现在的尾插法发生了严重的冲突,这个地方整整纠结了我一下午,到了第二天才恍然大悟,pTail并不是尾结点,它只是一个指向尾结点的指针变量而已,这就解释了为什么一开始pTail=pHead; pTail->next=NULL;链表一开始为空,pHead->next也指向NULL,此时的头结点我们可以把它看成是一个尾结点,由于我们定义了pTail指向尾结点,于是此处pTail指向了pHead。接着解释为什么对pTail=pNew产生了疑惑,一开始我认为pNew是一个指针,将pNew的地址赋值给pTail后,pTail就指向pNew,但是为什么pTail就又变成了尾结点了呢?其实这里是指,pTail指向的结点变成了尾结点,也就是新插入的那个结点,变成了尾结点,然后pTail再去指向它。pTail还是pTail,它不是结点,它只是一个指针,将pNew的地址赋值给pTail后,pTail就指向了pNew。总结一句话就是,保证每新插一个结点,这个结点就变成尾结点。书上抠了一幅图来了:
自学最大的困难莫过于此,遇到了思维的分叉点,一旦走入了错误的一方,纠正过来需要花费巨大的精力与时间。这个小小的错误,我从开始理解错误到结束理解正确,中间的我甚至还用以前的知识去证明我错误的点是正确的,所以脑子里一直会有一种不和谐感,再次学习的时候还是会纠结会去想,最后乱成一锅粥。但是豁然开朗的感觉又是如此的舒畅,这或许就是学习的乐趣吧!