名正在想的博客

实现你的第一个链表

By 名正在想


写在前面

我搭了这个博客有一段时间了,但是由于懒等原因,没写过什么东西……今天看到很多人不太理解上课所讲的链表,心血来潮,尽可能详细地讲解了链表的创建操作,以帮助大家巩固学习内容。以后在我的博客上可能会陆续更新更多内容,以期与大家共同学习,欢迎大家关注我的博客并批评指正。

为什么要使用链表?

这是我们首先要考虑的一个问题,我们之前已经学过了数组的使用,可以方便快速地存储大量数据,为何还要引入“链表”的概念呢?

比如我们让50个小朋友排排坐,那事情是很好办的:只需要准备50个椅子,让小朋友们依次坐好即可,这就是数组的使用。但是,如果我们预先不知道有多少小朋友,那事情就不好办了,我们可能需要准备大量的椅子,但这就会浪费很多空间;此外,如果一个新来的小朋友必须进入特定两人之间,椅子是静态的,只能让后面的人一个一个依次往后挪,他才能坐到这个位置,这无疑十分浪费时间。

因此,在这种情况下,我们不再让小朋友排排坐,而是让每人拉着另一个人的衣襟,这样不需要确定椅子数,如果有人需要插入,也只需改变一两个人拉谁的衣襟即可。这也就是我们所说的链表。(要注意,这里是“拉衣襟”而不是“手拉手”,因此严格地说是一种单向链表)

C语言实现

实现方式的选择

我们现在已经知道,要实现链表,我们需要有数据(小朋友),又要把数据连起来(拉衣襟),在编程中,我们特别需要考虑对“拉衣襟”的实现,这时,C语言中的指针就为我们提供了绝佳的工具,因为它可以指向其他的元素。为了将数据和指针组合起来,我们需要一个结构体,在链表中,我们将这种数据域指针域的组合称为结点(node)。

在这里的例子中,为了简单,我们在每个结点只存储一个整型变量:

typedef struct linked_list {
    int num;
    struct linked_list* next;
} l;

在这个结点中,数据域为整型变量num,指针域为指针next,用来指向下一个结点,结点之间依次相连,从而实现“拉衣襟”的操作。

创建结点

创建头结点

很多时候,我们都需要先创建一个不存储数据的头结点,这样即使遇到空链表或对第一个元素的操作等情况也不会出现问题。(注:在课本示例中,并没有使用头结点,但我认为最好使用)

特别注意下面几个名词的区别!

  • 头结点:一个空结点,位于单向链表最前面,它的指针域指向第一个有数据的结点
  • 尾结点:最后一个结点,它的指针域最终指向NULL
  • 头指针:指向头结点的指针,在访问单向链表时要从这个指针所指的结点开始
  • 尾指针:指向尾结点的指针,用于创建链表过程中的操作
l * head, * new_node, * tail; //声明头指针、创建新结点所用指针、尾指针
head = (l*)malloc(sizeof(l)); //为头结点分配内存
tail = head; //让尾指针指向头指针

创建数据结点

接下来我们就可以创建有数据的结点内容了!下面先以其中一个结点为例:

new_node = (l*)malloc(sizeof(l)); //为结点分配内存
scanf("%d", &new_node->num); //输入数据
tail->next = new_node; //让尾结点的指针域指向新创建的结点
tail = new_node; //让尾指针指向新的结点

当然,在实际中,肯定不会只创建一个数据结点,而是用循环语句创建完所需的所有结点。

结束链表创建

现在,我们假设所有结点创建完毕,需要进行最后一步——将尾结点的指针域指向NULL,这样就可以标记链表的结束。

tail->next = NULL;

创建链表的整体操作

#include<cstdio>
#include<cstdlib>

//使用结构体构建结点
typedef struct linked_list {
    int num;
    struct linked_list* next;
} l;

l* create(int n) {
    l * head, * new_node, * tail;
    int i = 0;
    //创建头结点
    head = (l*)malloc(sizeof(l));
    tail = head;
    //使用循环,创建n个数据结点
    for(i=0; i<n; ++i) {
        new_node = (l*)malloc(sizeof(l));
        scanf("%d", &new_node->num);
        tail->next = new_node;
        tail = new_node;
    }
    //将尾结点的指针域指向NULL,标志链表结束
    tail->next = NULL;
    //返回头指针
    return head;
}

int main() {
    l* head_pointer;
    int n;
    printf("请输入数据个数:");
    scanf("%d", &n);
    printf("请依次输入数据:");
    head_pointer = create(n);
    /*
    下面可以进行链表操作,本文暂不演示
    */
    return 0;
}

要注意,我们需要在最后让函数返回头指针,以便在主函数中通过头指针访问我们所创建的链表。

总结

本文一步步介绍了创建一个链表的方法,由于我写了半天,现在有点犯懒了,欲知链表的访问、修改等操作,且听下回分解……