链表:带头节点和不带头结点,还有头指针

一、概念理解

        最近在处理数据结构的时候,发现链表的这一部分比较模糊,主要也是头指针和头结点的问题。(感觉很多人也都有这个问题。链表插入和取出什么的操作逻辑一清二楚,创建链表带不带头结点,怎么操作的倒不明白了)。头指针是每个链表必带的。

1、首先,先换个角度说说头指针

        在网上查到的信息里,跟多都说头指针指向了链表的位置。(这样挺让人迷糊的)

概念上说:头指针表示了链表的地址,就像我们创建了一个变量,对变量取地址后,那个地址就是变量的位置 (//接下篇测试用例  链表部分操作案例-CSDN博客)

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 
#include 

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;
}

链表:带头节点和不带头结点,还有头指针_第1张图片

二、带头结点

带头结点:头结点就是上面的(*L)了。L就是头指针,指向   *L。参考(a)

三、不带头结点

看上面要是看懂了,这个不带头结点的,那我们创建一个结点直接赋值就行了,直接在它后面或者前面插入元素就行;返回值的话,直接返回第一个结点的地址就行,也就是头指针,方便我们后续找到和使用链表;

感觉这样理解起来比较简单些。操作上关键在于我们给不给一个结点赋值,不赋值就让它一直在第一个,做头结点。

你可能感兴趣的:(链表,数据结构)