一、概念理解
最近在处理数据结构的时候,发现链表的这一部分比较模糊,主要也是头指针和头结点的问题。(感觉很多人也都有这个问题。链表插入和取出什么的操作逻辑一清二楚,创建链表带不带头结点,怎么操作的倒不明白了)。头指针是每个链表必带的。
1、首先,先换个角度说说头指针
在网上查到的信息里,跟多都说头指针指向了链表的位置。(这样挺让人迷糊的)
概念上说:头指针表示了链表的地址,就像我们创建了一个变量,对变量取地址后,那个地址就是变量的位置 (//接下篇测试用例 链表部分操作案例-优快云博客)
int a = 1;
int *p = &a;
(a)对a取地址,p 就是我们创建的一个指针,被赋值后指向的就是a的地址空间。
而常用的链表 头指针也是同样的原理(指向头结点,头结点不存储东西),写的时候感觉最关键的就是,我们定义头结点的时候,要让谁当头结点(主动权在我们手里,让它是就是,就不赋值;不让它做头结点,直接赋值,让它做个普通新结点就完事了)
//参考以前数据结构那本书上的代码 typedef struct Node{ struct Node * pnext; int val; }Nodes, *Linklist; //看清楚了哦,这里起了个别名,*Linklist与Node这个数据结构是等价的 //而Linklist就是这个结点的地址(结点本身是一个数据结构,这个存储的就是这样的数据结构的地址,相当于 &Nodes。) Linklist List_HeadInsert(LinkList & L) { Nodes *s; int x; L = (Linklist)malloc(sizeof(Nodes)); //这里代码是创建了一个 Linklist类型的指针L(L就是一个这样数据结构的地址) //指向一个Nodes类型的数据结构空间。然后L代表的结点我们就可以将它当作头结点(也就是跟多文章说这个东西就是头结点的说法) L->next = NULL; //初始为空链表 scanf("%d",&x); While(x!=9999) { s = (Linklist)malloc(sizeof(Nodes)); s->val = x; s->next = L->next; //相当于L整体往前挪了一个位置 L->next = s; //使L指向的下一个元素是最新的结点就行 scanf("%d",&x); } return L; //按照定义的数据结构 * } //有头结点的链表进行头插的时候还是比较简单的,毕竟前面有一个头结点作为参照。头结点不存储元素信息,顶多使用它的值域去存储链表的大小
(b)关于这个头结点的地址和位置,写了个测试程序,可以参考下
#include <stdio.h> #include <stdlib.h> typedef struct Node{ int val; struct Node * _next; }Nodes,*Linklist; int main(){ Linklist L = (Linklist)malloc(sizeof(struct Node)); L->val = 1; L->_next = NULL; //这里对L取地址 printf("&L = %p\n",&L); //L本身只是现在定义的数据结构(*L)的地址,就是一个普通结点, //使用它作为头节点的话,不给它赋值就好了 Nodes newNode; printf("&newNode = %p\n",&newNode); //此时只定义了一个newNode的对象, newNode = *L; printf("newNode-> val = %d\n",newNode.val);// 这个newNode 相当于是Node的一个对象。 printf("&newNode = %p\n",&newNode); printf("当我们打印L的信息时\n"); Linklist *p = &L; //定义一个指针P,这个指针就是头指针了可以看打印的地址信息 printf("p = %p\n",p); printf("&L = %p\n",&L); //如果L结点不存储val值让它做头结点,这里对L取地址后,不就是头指针吗,头指针指向了链表头结点所在的位置 printf("\n"); printf("L = %p\n",L); printf("&*L = %p\n",&(*L)); return 0; }
二、带头结点
带头结点:头结点就是上面的(*L)了。L就是头指针,指向 *L。参考(a)
三、不带头结点
看上面要是看懂了,这个不带头结点的,那我们创建一个结点直接赋值就行了,直接在它后面或者前面插入元素就行;返回值的话,直接返回第一个结点的地址就行,也就是头指针,方便我们后续找到和使用链表;
感觉这样理解起来比较简单些。操作上关键在于我们给不给一个结点赋值,不赋值就让它一直在第一个,做头结点。