212
社区成员
这一篇博客算是基础C语言的完结篇啦!!!
这学期的所有内容都包括在这啦,文件操作没讲到就直接看mooc的讲解吧,对你们这学期的结课设计可能关系比较大和pta关系不大
数组大家应该都比较熟悉,我就不过多介绍了,不清楚的同学可以去看看我之前写的博客
链表:顾名思义,是把数据套在一起的一个表,可以理解成数据是一个个锁链中的一截,下面在讲细致的
在开始之前,我们打个比方先:
数组:300格中的一行,从 1 写到 20 每个数据占一个格子,总共20个,并且在这一行的上面一行写上下标
链表:我们在这里比作钥匙扣上的那个环有20个,每个颜色不一样,这样一个接一个的扣在一起,分别代表1-20
数组:如果我们要把300格第5个格子位置删除一个数据,那我们改起来是不是要把第5格往后的数据全部涂掉,全部往前挪一格
链表:我们想把第 5 个钥匙环拿下来,只需要数到第 5 个扣子把他拿下来,然后把第 4 个扣子和第 6 个扣子扣起来就可以了
总结:那么有生活常识(没有的去把这些材料买了玩一下!)的各位应该可以很明显的感觉到操作链表的时候是更方便的
上面的是数组,
下面的钥匙扣是链表,简单理解成
小人 是 1
中间的环(可以买20个颜色不一样的环来玩) 是 2
右边的带子 是 3
插入元素的操作是一样的,我就不多说了,大伙想一想就ok了
一样是查询第 5 个 元素
数组:因为我们在这行的上方写了下标,所以我们只需要看下标就可以快速的发现,第五个元素是下标为 4 的 5
链表:因为我们没有下标,所以要一个个的往下面数,数到第五个环,才能知道他是什么颜色的
不要杠:第五个环可以轻松的看出来,但是如果是第 114514 个环呢,虽然数组也不能是很找,但是你看到 110000 在哪里就能很快速的找到
总结:找数据的话看起来是数组更方便
修改数据是一样的
数组的查询和修改相较于链表是有明显优势的
链表的插入和删除相较于数组是有明显优势的
那么我们应该用链表还是数组呢?
当然是具体场景具体分析啦,删除和插入多的话用链表,查询和修改多的话用数组
但是大部分情况下数组还是有很明显的优势(大部分?全部!)
平常为什么数组用的多大家应该也清楚吧(链表代码难写呀!)
那么我们为什么要学链表呢?(为了应付考试!为了完成作业!)
有四个基础的用法,头插法,尾插法,带头的头插法,带头的尾插法
四个写法在应对不同的题目时都是有一定作用的,而且学会一个,其他几个只要看过代码就能基本掌握啦!!!
下面的举例偏向于面向对象编程,去理解编程,是今后很经常遇到的概念,你们下学期学Java就知道啦
下面的举例中!!!!!!!!!!最重要的!!!!!所有的系扣子的时间忽略不计!!!!!!!!!
比如:系两个扣子的时间 = 系一个扣子的时间 = 0
有钥匙扣或者有锁链的同学可以拿出来玩玩,我们这边依旧是先理解数据是怎么插进去的
代码:
//我们继续用给钥匙扣涂颜色的例子
List HeadCreate(void)
{
ring x; // 数据类型,C语言没有这个类型哦,我随便写的!
List p;
List head; //定义一个头节点,也是最后返回的节点
head = NULL;
scanf("%d", &x); //获取想要涂的颜色
while (x != -1) { //我们暂且把 -1 当成没有颜色
//malloc函数,这个我在指针那篇博客讲过,不知道去看一下指针的内容,很重要!!!
p = (List)malloc(sizeof(struct Node));
p->data = x; //给钥匙扣上色
//这个if语句理解为我们只有一个钥匙扣的时候,我们要记住那个扣子是什么样的
if (head == NULL) { // 若第一次创建节点,则将该点设置为头节点
head = p;
p->Next = NULL; //因为我们现在只有一个扣子,所以他上面没有扣别的东西
} else { // 若不是第一次创建节点,则直接将新节点接到链表头
//-------钥匙扣原理---------
//我们已经有一个或多个环了,那么我们新的环要和旧的环打结起来
//我们怎么获得旧的环,我们刚刚不是已经标记了吗?在 if 那句!
//-------钥匙扣举例---------(第二个环)
//我们拿第二个环举个例子,我们手上是第二个环,桌子上是第一个环
//那么我们把手上的环和桌上的扣起来,并且把我们手上的环记住,并且放在桌子上
//-------钥匙扣举例---------(第三个环)
//我们手上是第三个环,桌子上是第二个环和第一个环
//那么我们把第三的环和桌上的第二个环扣起来,并且把我们第三个环记住,并且放在桌子上
//-------代码实现---------
p->Next = head;
head = p;
}
scanf("%d", &x); //拿下一个环和颜色
}
//返回我们最后记住的那个环,让他作为第一个环
return head;
}
数据: red blue yellew green purple
链表:purple green yellew blue red
看这个结果应该就能知道头插法是可以让数据完成倒序的排序过程
原理和上面是一样的,但是这次我们更换一下道具,换成 5 个环加 1 个带子
数据: red blue yellew green purple
链表:band purple green yellew blue red
代码:
//这边再假设一下,这一步可以有,也可以没有
//如果我们data是int类型的话我们可以把头节点拿来记录插入数据量
//就比如我们在带子上扣一个环,在带子上用 “正” 字计数法计数
List HeadCreate(void)
{
ring x;
List p;
List head; //声明带子
head = (List)malloc(sizeof(struct Node)); //带子是真实存在的,所以要创建一下
head->Next = NULL; //现在只有带子
head->data = 0; //正字现在0比
scanf("%d", &x);
while (x != -1) {
p = (List)malloc(sizeof(struct Node)); //生成一个环
p->data = x; //给他上色
//当第一个环时,这个p->next = null
//但是第二个环的时候,这个p->next = 第一个环
//但是第三个环的时候,这个p->next = 第二个环,第二个环->next = 第一个环
//以此类推
//这个步骤是第一个系扣子的动作,目的是把原本的环卸下来,装到新环上
p->Next = head->Next;
//这个步骤是第二个系扣子的动作,目的是把新环和带子扣在一起
head->Next = p; //带子上系一个环
head->data++; //写正
scanf("%d", &x);
}
return head;
}
其实就是正序的读入和输出
数据: red blue yellew green purple
链表:red blue yellew green purple
代码:
List TailCreate(void)
{
ring x;
List p;
List head; //头节点
List rear; //尾节点
head = NULL; //只用于返回结果,且就是第一个环
rear = NULL;
scanf(%d, &x);
while (x != -1) {
p = (List)malloc(sizeof(struct Node)); //创建这个环
p->data = x; //上色
if (head == NULL) { // 创建链表的第一个节点
head = p; //自己记住第一个环!
rear = p; //再找个人记住这个环!
p->Next = NULL; //这个环没有任何连接
} else {
rear->Next = p; //读入第二个环时,让第一个环和他扣起来
rear = p; //并且让第二个环成为最后的那个环
}
scanf("%d", &x);
}
rear->Next = NULL; //因为我们刚刚是没有初始化的,所以给上,写在循环里执行次数太多了,浪费
return head;
}
原理和上面是一样的,但是这次我们更换一下道具,换成 5 个环加 1 个带子
数据: red blue yellew green purple
链表:band red blue yellew green purple
代码:
List TailCreate(void)
{
ring x;
List p;
List head; // 头节点
List rear; // 尾节点
head = (List)malloc(sizeof(struct Node)); //创建带子
head->data = 0;
head->Next = NULL; //带子下面没有环
rear = head; // 链表为空,头和尾指向同一个位置
scanf("%d", &x);
while (x != -1) {
p = (List)malloc(sizeof(struct Node)); //创建环
p->data = x;
rear->Next = p; //第一次进来,就是头节点,第二次进来就是第一个环
head->data++; //记录数量
rear = p; //变成刚获得的环
}
rear->Next = NULL; // 链表建立结束后将最后一个节点指向 NULL
return head;
}
不难看出,带头节点的尾插法代码是最简单的,且也是最经常用到的,当然不是因为他的代码简单,而是因为他的泛用性比较强
其实我们这大学四年离不开这么四个字:增 删 改 查 ,你们大二上学期的数据库也是张庆老师教的,好好学哦!非常重要!
那么我们直接讲这四个方法吧,当然还是用钥匙扣的例子,且所有的例子都是有带子的(含头节点尾插法生成的链表)
例子:插入第三个环
旧链表:band red blue brown yellew green purple
新链表:band red blue green purple
代码:
//NULL代表没找到
//x从1开始
List insert(List head, int x,ring y)
{
x--; //我们找的是插入节点的前一个节点
List p = head;
if(head->data + < x) return NULL; //插入太远了
while(x--){
p = p->next;
}
List q = (List)malloc(sizeof(List));
q->data = ring;
q->next = p->next;
p->next = q;
//其实不返回也行,因为不影响,正常时返回bool类型
return head;
}
例子:取下第三个环
旧链表:band red blue yellew green purple
新链表:band red blue green purple
代码:
//NULL代表没找到
//x从1开始
List delete(List head, int x)
{
x--; //我们找的是删除节点的前一个节点
List p = head;
int cnt = 0;
if(head->data < x) return NULL;
while(x--){
p = p->next;
}
p->next = p->next->next;
//其实不返回也行,因为不影响,正常时返回bool类型
return head;
}
例子:取下第三个环,换上棕色的环
旧链表:band red blue yellew green purple
新链表:band red blue brown green purple
代码:
//NULL代表没找到
//x从1开始
//其实更新有很多种写法
//我这里的写法算是把黄色的环直接用颜料涂成棕色
//还有一种写法是把黄色的环卸下来,然后再把棕色的环放上去(类似先删除,再插入)
List modify(List head, int x, ring y)
{
List p = head;
int cnt = 0;
if(head->data < x) return NULL;
while(x--){
p = p->next;
}
p->data = y;
//其实不返回也行,因为不影响,正常时返回bool类型
return head;
}
例子:取下第三个环,换上棕色的环
旧链表:band red blue yellew green purple
新链表:band red blue yellow green purple
代码:
//NULL代表没找到
//x从1开始
//查第几个,也可以用这个方法遍历链表
ring search(List head, int x)
{
List p = head;
int cnt = 0;
if(head->data < x) return NULL;
while(x--){
p = p->next;
}
return p->data;
}
//查环ring
List search(List head, ring x)
{
List p = head;
while(p!=NULL){
p = p->next;
if(p->data == x) return p;
}
return NULL;
}
增删改查的操作其实和构建是差不多的,本栏目是为了让大家理解什么是链表,就像之前那篇什么是递归一样,只需要我教的这些,你的链表就已经掌握的非常娴熟了,其他新的业务大家就可以自己应付啦
如果你递归,指针,链表都已经理解的非常透彻了(熟读我这三篇博客)那么你在这个学期的C语言课程就正式毕业啦!!!!!!!!
本栏目应该还会持续的更新到这个学期结束,之后的课程就是为学有余力的同学准备的啦,也是你们下个学期数据结构和下下个学期算法课程的启蒙篇章,大家之后想学也可以打开看看!!!