戳一戳!和我一起走进信息学的世界

导读

信息学能够有助于孩子未来工作发展,提升孩子的综合能力。


这一期课,我们承上启下,简单复习信息学所有C++的知识体系,讲解一部分初赛和复赛的知识;然后就从顺序表和链表,开始一步一步走进数据结构的世界!


这一节课,我们来分析顺序表的缺点,讲解一个新的数据结构——链表,并做一些链表的实训,分析一道信息学初赛的真题。让我们一起走进今天的文章吧!

往期回顾

【NOIP竞赛/CSP认证】

▶  赛前必看!信息学名师带你复习NOIP竞赛初赛及CSP认证初赛


【信息学精华帖】

▶  收藏!交流会内容全公开,让你陪孩子更好地学习信息学

▶  信息学的万般好处!附C++必备基础知识总结

▶  信息学提高班知识体系详解与家长常见问题解答!让孩子赢在提高班学习的起跑线!

▶  再回首,最全提高班知识总结,做更优秀的自己!


【数据结构前导课】

▶  1 温故知新——一篇文章领略信息学C++知识结构

▶  2 披荆斩棘——只学C++,可以做哪些竞赛题

▶  3 运筹帷幄——一篇文章,让指针学起来也很简单!

▶  4 初试锋芒——顺序表写起来也很简单

▶  5 小试牛刀——STL库之vector数组


【C++提高班教程】

▶  C++强化 | 01 新学期再出发!温故知新!

▶  C++强化 | 02 继续前行,三大结构终极介绍

▶  C++强化 | 03 一维数组入门

▶  C++强化 | 04 数组越界

▶  C++强化 | 05 一维数组经典应用

▶  C++强化 | 06 一篇文章带你掌握字符数组

▶  C++强化 | 07 二维数组

▶  C++强化 | 08 二维数组经典案例

▶  C++强化 | 09 一篇文章带你探索函数的奥秘

▶  C++强化 | 10 函数进阶必备

▶  C++强化 | 11 这样学递归,才不会觉得难

▶  C++强化 | 12 格式化输入输出与文件操作

▶  ​​C++强化 | 13 结构体入门

  ​​C++强化 | 14 结构体进阶


【C++基础班教程】

▶  C++总结 | 01 程序的世界

▶  C++总结 | 02 输出、换行与注释

▶  ​C++总结 | 03 变量定义、赋值与运算​

▶  ​C++总结 | 04 算术运算符与赋值运算符​

▶  ​C++总结 | 05 cin语句​

▶  ​C++总结 | 06 程序中的数据类型​

▶  ​C++总结 | 07 数据类型补充​

▶  ​C++总结 | 08 顺序结构​

▶  ​C++总结 | 09 if 和 if-else​

▶  C++总结 | 10 if嵌套与逻辑运算符

▶  C++总结 | 11 开关语句switch-case

▶  C++总结 | 12 for循环及其应用

▶  C++总结 | 13 数据范围与数据类型

▶  C++总结 | 14 break与continue

▶  C++总结 | 15 while与do-while

▶  C++总结 | 16 循环嵌套及其应用 



1 顺序表分析

前面两节课,我们学习了顺序表!我们一起来简单复习一下顺序表,再分析一下顺序表的缺点。

1 顺序表复习

顺序表是用一组地址连续的存储单元依次存储线性表中的数据元素,从而使逻辑上相邻的两个元素在物理位置上也相邻


经过上面两次课,我们也知道,顺序表中的元素,可以通过索引去访问。

2 顺序表的缺点

顺序表使用起来比较简单,那它是百分百完美的吗?


当然不是,我们在写顺序表的函数的时候,应该会注意到一个问题,如果我们想在中间插入或者删除一个数据,这个位置的后面的数据都要后移或者前移。


也就是说顺序表插入或者删除数据,可能需要移动大量元素

2 玩个游戏

我们一起来看一个小游戏。

1 母鸡与小鸡

母鸡带着小鸡出去买东西,为了防止黄鼠狼带走小鸡,母鸡就让小鸡一个接一个地走在自己的后面,每个小鸡,要记住自己身后的小鸡,母鸡要记住身后第一只小鸡。


一旦黄鼠狼抓走了某一只小鸡,母鸡就知道谁被抓走了,这只小鸡就要重新记住一只新的小鸡。

2 鸡妈妈与小鸡分析

假如,母鸡为A,小鸡有名字为b,c,d,e,f,g;


现在有一只新的小鸡h加进来,站到c和d中间,c小鸡就要重新记住身后的小鸡是h,h要记住身后的小鸡是d。


这个时候,母鸡想要找到小鸡f,就可以按照如下方式:


母鸡能够找到小鸡b;
通过小鸡b找到小鸡c;
通过小鸡c找到小鸡h;
通过小鸡h找到小鸡d;
通过小鸡d找到小鸡e;
通过小鸡e找到小鸡f;
这样母鸡就可以找到小鸡f。


现在我们用箭头表示小鸡b能够找到小鸡c:


b->c;


那么我们就可以得到如下的序列:


A->b->c->h->d->e->f


3 链表

上面的这个流程,就是链表的流程,我们一起来学习一下链表。

1 链式存储结构

我们先来讲一下链式存储结构。


我们可以用一条链将所有的数据连在一起,可以通过链让某一个数据找到它的下一个数据,通过不断找下一个数据的这种方式,我们就可以找到链上的任意一个数据。


数据结构前导课 | 6触类旁通——链表基本理论与信息学竞赛必考点_数据


上面的就是链式结构的示意图。我们发现:每一个数据块通过一个箭头相互连接,所有的数据不用像顺序表一样必须排列在一起


链式存储结构即用一组任意的存储单元存储线性表的数据元素(这组存储单元可以连续,也可以不连续)。我们还需要使用箭头指向它的下一个数据。在C++中,我们使用指针的方式,来指向其下一个数据的地址。这个时候,我们存放数据,还需要存一个指针。


2 什么是链表

多个节点,使用指针一一连接成一个串(链),这个链就是链表。


一个简单的链表,至少需要两部分变量,一个是存放其数据的变量,另一个是存放其下一个数据的指针变量。


也就是说。一个链表的结构如下:


数据结构前导课 | 6触类旁通——链表基本理论与信息学竞赛必考点_c++_02


其中ABCD表示的是数据,箭头表示的是指针,^ 表示后继没有数据,所以为空。

3 定义一个单链表

根据上面的内容,我们就可以定义一个链表:


struct LinkList{
int data; //链表中存放的数据,以int类型为例
LinkList *next; //next指针指向的是下一个数据块,一个数据块就是一个LinkList结构,也就说,指针也得是LinkList结构
};


4 定义一个双向链表

双向也是一种链表,双向链表有两个指针,一个指针指向它的左边的数据,一个指针指向它的右边的数据。


数据结构前导课 | 6触类旁通——链表基本理论与信息学竞赛必考点_c++_03


上面就是一个双向链表的示意图,l(left)表示指向其左边数据的指针,r(right)表示指向其右边数据的指针。


所以我们可以定义双向链表结构如下:


struct TwoWayLinkList{
int data; //链表中存放的数据,以int类型为例
TwoWayLinkList *lLink; //指向其左边的指针
TwoWayLinkList *rLink; //指向其右边的指针
};


3 链表必备操作

前面我们定义了单链表和双向链表,接下来我们一起来看一下信息学中会考到的几个必备操作。


这里大家重点理解逻辑,链表到目前为止,只在信息学提高组初赛中以选择题的形式出现过。所以我们这里也只讲和竞赛相关的。


如果大家有兴趣想更加深入了解顺序表和链表,可以看下面这两篇文章:


对于链表来说最重要的是要理解链表的插入和删除操作,这是因为,链表的插入元素和删除元素只需要给修改对应指针即可,不需要移动元素位置

1 单链表插入元素

以下面的为例:


在b和c之间插入一个数据e:
插入前:a->b->c->d
插入后:a->b->e->c->d


也就是下图:


数据结构前导课 | 6触类旁通——链表基本理论与信息学竞赛必考点_链表_04


我们需要让b的next指针指向e,让e的next 指针指向c,让b指向c的指针断开。


所以我们需要下面流程:


第一步,让b的next指针指向e,这个时候,b指向c的next指针就断开了;
第二步,让e的next指针指向c,这个时候,就把这个链表连起来了。


代码如下:


int insert(LinkList *&b, LinkList *&c, LinkList *&e) {
b->next = e;
e->next = c;
return 0;
}


上面是最简单的情况,如果我们想实现,在b的后面插入一个数据,但是我们不知道原表中b的后面是c,这个时候,如果我们还是先写下面的语句:


b->next = e;


b原本后面的就找不到了,所以,我们要先让插入的结点指向b的next,然后再让b指向新插入的结点。


代码如下:


int insert(LinkList *&b, LinkList *&e) {
e->next = b->next;
b->next = e;
return 0;
}


2 单链表删除元素

最简单的是删除结点b的后一个结点。


图示如下:


数据结构前导课 | 6触类旁通——链表基本理论与信息学竞赛必考点_c++_05


代码如下:


int delNext(LinkList *&b) {
b->next = b->next->next;
return 0;
}


接下来是:已知结点b的前一个结点是a,删除结点b。


数据结构前导课 | 6触类旁通——链表基本理论与信息学竞赛必考点_c++_06


代码如下:


int delList(LinkList *&b, LinkList *&a) {
a->next = b->next;
return 0;
}


3 双向链表插入元素

对于双向链表,我们来看一道信息学竞赛题目:


数据结构前导课 | 6触类旁通——链表基本理论与信息学竞赛必考点_数据_07


对于选项A,太过着急让pq相连,就找不到p本来的左边。如下图,我们假设P的左边是m,我们先连接pq那么q和m就无法连接,所以A不对。


数据结构前导课 | 6触类旁通——链表基本理论与信息学竞赛必考点_链表_08


对于B,第三条语句让q的右边为p,p的左边应该为q,而不是q的右。如果按照它的做法,p的左边就指向自身。图示如下:


数据结构前导课 | 6触类旁通——链表基本理论与信息学竞赛必考点_链表_09


对于C,让p的右为q,就破坏了原本的链表,q是插在p的左边,而不是右边。



数据结构前导课 | 6触类旁通——链表基本理论与信息学竞赛必考点_c++_10


通过以上分析我们已经能知道D就是正确选项了,我们来一起通过图示看一下D选项:


数据结构前导课 | 6触类旁通——链表基本理论与信息学竞赛必考点_c++_11


第一步,让p的左的右指向q;
第二步,让q的右指向p;
第三步:让q的左指向p的左;
第四步:让p的左指向q。


这个解法不唯一,还可以有很多其他做法,例如:


第一步,先让q的左,指向p的左,保证m不会找不到;
第二步,我们可以让m的右指向q,m既可以是p的左,也可以是q的左;
后两步,就可以让pq相互指。


对应代码为:


q->llink = p->llink;
p->llink->rlink = q; // q->llink->rlink = q;
p->llink = q;
q->rlink = p;


图示如下:


数据结构前导课 | 6触类旁通——链表基本理论与信息学竞赛必考点_c++_12


5 作业

本节课的作业,就是复习上面的所有知识,并完成下面的题目!

1 NOIP2014年提高组

单项选择题,选择错误的一项,并说明理由:


数据结构前导课 | 6触类旁通——链表基本理论与信息学竞赛必考点_链表_13




AI与区块链技术

数据结构前导课 | 6触类旁通——链表基本理论与信息学竞赛必考点_c++_14

长按二维码关注