C++链表(一)小白的理解

要想理解链表,一定得理解指针。链表的本质就是指针。

关于指针的理解,小白可以看这篇文章,比较容易理解。https://blog.csdn.net/u014095878/article/details/104559879

要想学习指针,第一步,就是学会接受它,不要觉得它很难。第二步,要立体的去思考,要想着这个指针里面装了什么东西,而不是简单的给这个变量赋值。

创建单链表有四种情况:不带头节点的头插法,不带头节点的尾插法,带头节点的头插法,带头节点的尾插法。

看完下面我的分析。你就可以掌握单向链表的创建了。

先来看一个不带头节点的尾插法的实例:

#include <stdio.h>
#include <malloc.h>
#include<iostream>
using namespace std;

typedef int ElemType;
typedef struct Node_s        //定义链表节点的结构体
{
    ElemType data;           //数据域
    struct Node_s* next;     //指针域

}Node, * LinkList;             //Node相当于struct Node_s    Linklist相当于struct Node_s *

int Output_L(LinkList head)  //输出不带头节点的单链表的元素
{
    if (!head)
        return -1;

    LinkList p;
    p = head;

    while (p != NULL)
    {
        printf("%d ", p->data);
        p = p->next;
    }
    printf("\n");
    return 0;
}

int main()
{
    int  i;
    Node *head = NULL;
    Node *tail = NULL;
    Node *new_node = NULL;

    for(i=0;i<5;i++)
    {
        new_node = (LinkList)malloc(sizeof(Node));
        new_node->data = i+1;
        new_node->next = NULL;     //将即将要插入的节点打包(数据域放什么,指针域放什么)

        if(head == NULL)              //如果为空链表
        {
            head = new_node;        //头指针指向第一个节点,头结点
            tail = new_node;        //尾指针指向第一个节点
        }
        else
        {
            tail->next = new_node;  //尾指针指向的节点(上一个节点)的指针域指向新插入的节点
            tail = new_node;        //这两句不能反
        }
        cout << "i =" << i << ",tail = " << tail << endl;
    }

    Output_L(head);
}

刚接触这个很难去理解这个链表。我们知道链表是由一个结点一个结点串起来的。也知道一个结点有两部分组成,一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。却不知道具体是是怎么实现的,尽管有一堆代码告诉我们实现的方法,我们却难以消化。

刚看到这个代码时,我只理解一句,new_node = (LinkList)malloc(sizeof(Node));这个是申请一块内存。

说说不理解的点。

1.head只赋值了一次,为什么后面打印输出是一个完整的单向链表?

2.为什么tail->next = new_node;赋值之后又赋值tail = new_node; ?

 

 

带着这两个疑问,我们重新梳理一下上面的代码。

Node *head = NULL;
Node *tail = NULL;
Node *new_node = NULL;

有3个变量,一个是head 头指针,一个是tail尾指针,一个是new_node不断的申请新内存的指针。

1.第一次进循环来,i=0;我们已经理解了new_node = (LinkList)malloc(sizeof(Node));每循环一次就申请一个新内存,也就新节点的地址;

 

2.new_node->data = i+1;给数据域赋值。i=0的时候,这个时候我们知道了第一个结点的地址是什么,地址里存放的数据域是什么,但是还不知道下一个结点的地址。

if(head == NULL)              //如果为空链表
{
    head = new_node;        //头指针指向第一个节点,头结点
    tail = new_node;        //尾指针指向第一个节点
}

 

3.知道了第一个结点的地址,也就是头结点,赶紧把这个地址赋值给head(head = new_node;)。

我们疑惑的问题点到了,为什么只赋值一次?

因为,头结点赋值了之后,就不必再变了。后面变化的一直是tail尾指针,因为你不断的往后面插入数据。所以,尾指针是一直变化的。

这里我们也看到了,tail第一次跟head指向的地址一样(tail = new_node;)。

这个时候第一个结点的数据域知道了,但是指针域还不知道,head->next = null,tail->next = null,new_node->next = null,还不知道下一个地址在哪里。

 

4.紧接着,第二次进来,i= 1,new_node又申请了新的内存地址,即第二个结点。

new_node->data = i+1 =2;这个时候tail还是指向第一个地址(头结点),第一个地址存放的数据tail->data=1,tail->next=null.

所以这个时候,给tail->next赋值,tail->next = new_node,

把第一个结点的指针指向第二个结点,这个时候就串起来链表了。但是只有两个结点的链表。

后面是我们的第二个问题点,为什么又赋值tail = new_node; 因为后面还要继续往后面插入数据。

tail已经完成了第一次两个结点的串联,tail这个时候要移到第二个结点来,它要搬家了。

head头结点地址不变,tail从头结点地址移到第二个结点的地址,专业术语是,指向第二个结点的地址。

 else
 {
      tail->next = new_node;  //尾指针指向的节点(上一个节点)的指针域指向新插入的节点
      tail = new_node;        //这两句不能反
 }

 

如果还不理解的,可以看看这段代码。我们知道指针就是存地址的嘛。

    int i = 1,j=2;
    int *p = &i;
    cout<<"p的地址:"<<p<<endl;
    p = &j;
    cout<<"p的地址:"<<p<<endl;

指针p的地址从指向i的地址,到第二次指向j的地址。

tail从第一个结点的地址,指向第二个结点的地址。结点地址里存着数据data和一个地址,这个地址指向下一个结点的地址。

后面的循环,你可以自己在草稿纸里演算,演算成功了,你就掌握了这个链表的创建了。

 

如果你还想了解单链表的其他的不带头节点的头插法,带头节点的头插法,带头节点的尾插法,请参看下面这位博主的这篇文章

https://blog.csdn.net/TAlice/article/details/82112921

感谢这位博主的介绍。感谢C/C++小白基础交流群里@雁的指导帮助。希望你们能完全掌握链表。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值