C语言基础完结篇:链表(钥匙扣)的构建使用及特点

星落化尘 2023-12-03 22:29:08
加精

这一篇博客算是基础C语言的完结篇啦!!!

这学期的所有内容都包括在这啦,文件操作没讲到就直接看mooc的讲解吧,对你们这学期的结课设计可能关系比较大和pta关系不大

1. 链表和数组的区别和选择

数组大家应该都比较熟悉,我就不过多介绍了,不清楚的同学可以去看看我之前写的博客

链表:顾名思义,是把数据套在一起的一个表,可以理解成数据是一个个锁链中的一截,下面在讲细致的

1.1 区别

在开始之前,我们打个比方先:

数组:300格中的一行,从 1 写到 20 每个数据占一个格子,总共20个,并且在这一行的上面一行写上下标

链表:我们在这里比作钥匙扣上的那个环有20个,每个颜色不一样,这样一个接一个的扣在一起,分别代表1-20

1.1.1 删除元素

数组:如果我们要把300格第5个格子位置删除一个数据,那我们改起来是不是要把第5格往后的数据全部涂掉,全部往前挪一格

链表:我们想把第 5 个钥匙环拿下来,只需要数到第 5 个扣子把他拿下来,然后把第 4 个扣子和第 6 个扣子扣起来就可以了

总结:那么有生活常识(没有的去把这些材料买了玩一下!)的各位应该可以很明显的感觉到操作链表的时候是更方便的

上面的是数组,

下面的钥匙扣是链表,简单理解成

小人 是 1 

中间的环(可以买20个颜色不一样的环来玩) 是 2

右边的带子 是 3

 

插入元素的操作是一样的,我就不多说了,大伙想一想就ok了

1.1.2 查询元素

一样是查询第 5 个 元素

数组:因为我们在这行的上方写了下标,所以我们只需要看下标就可以快速的发现,第五个元素是下标为 4 的 5

链表:因为我们没有下标,所以要一个个的往下面数,数到第五个环,才能知道他是什么颜色的

不要杠:第五个环可以轻松的看出来,但是如果是第 114514 个环呢,虽然数组也不能是很找,但是你看到 110000 在哪里就能很快速的找到

总结:找数据的话看起来是数组更方便

修改数据是一样的

1.1.3 总结

数组的查询和修改相较于链表是有明显优势的

链表的插入和删除相较于数组是有明显优势的

那么我们应该用链表还是数组呢?

当然是具体场景具体分析啦,删除和插入多的话用链表,查询和修改多的话用数组

但是大部分情况下数组还是有很明显的优势(大部分?全部!)

平常为什么数组用的多大家应该也清楚吧(链表代码难写呀!)

那么我们为什么要学链表呢?(为了应付考试!为了完成作业!)

2. 链表的基本用法

2.1 构建链表

有四个基础的用法,头插法,尾插法,带头的头插法,带头的尾插法

四个写法在应对不同的题目时都是有一定作用的,而且学会一个,其他几个只要看过代码就能基本掌握啦!!!

下面的举例偏向于面向对象编程,去理解编程,是今后很经常遇到的概念,你们下学期学Java就知道啦

下面的举例中!!!!!!!!!!最重要的!!!!!所有的系扣子的时间忽略不计!!!!!!!!!

比如:系两个扣子的时间 = 系一个扣子的时间 = 0

2.1.1 头插法

有钥匙扣或者有锁链的同学可以拿出来玩玩,我们这边依旧是先理解数据是怎么插进去的

代码:

//我们继续用给钥匙扣涂颜色的例子

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

看这个结果应该就能知道头插法是可以让数据完成倒序的排序过程

2.1.2 带头头插法

原理和上面是一样的,但是这次我们更换一下道具,换成 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;
}

2.1.3 尾插法

其实就是正序的读入和输出

数据: 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;
}

2.1.4 带头尾插法

原理和上面是一样的,但是这次我们更换一下道具,换成 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;
}

2.1.5 总结

不难看出,带头节点的尾插法代码是最简单的,且也是最经常用到的,当然不是因为他的代码简单,而是因为他的泛用性比较强

2.2基本的使用

其实我们这大学四年离不开这么四个字: 查 ,你们大二上学期的数据库也是张庆老师教的,好好学哦!非常重要!

那么我们直接讲这四个方法吧,当然还是用钥匙扣的例子,且所有的例子都是有带子的(含头节点尾插法生成的链表)

 

2.2.1 插入操作

例子:插入第三个环

旧链表: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;
}

2.2.2 删除操作

例子:取下第三个环

旧链表: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;
}

2.2.3 更新操作

例子:取下第三个环,换上棕色的环

旧链表: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;
}

2.2.4 查询操作

例子:取下第三个环,换上棕色的环

旧链表: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;
}

2.2.5 总结

增删改查的操作其实和构建是差不多的,本栏目是为了让大家理解什么是链表,就像之前那篇什么是递归一样,只需要我教的这些,你的链表就已经掌握的非常娴熟了,其他新的业务大家就可以自己应付啦

如果你递归指针链表都已经理解的非常透彻了(熟读我这三篇博客)那么你在这个学期的C语言课程就正式毕业啦!!!!!!!!

最后的最后祝大家考试都能取得好成绩,当然不只是C语言这门课,也祝学弟学妹们能开启一个愉快的大学生活,向光而生,永不言弃,未来可期!!!!!

本栏目应该还会持续的更新到这个学期结束,之后的课程就是为学有余力的同学准备的啦,也是你们下个学期数据结构和下下个学期算法课程的启蒙篇章,大家之后想学也可以打开看看!!!

...全文
210 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

212

社区成员

发帖
与我相关
我的任务
社区描述
程序设计基础课程教学群
c语言c++ 高校 福建省·厦门市
社区管理员
  • xmzq001
  • 鹿饮涧鸣
  • jiangxiaoju
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

请加入学习社区的软件23级同学修改社区昵称为学号+姓名,以便登记作业提交情况。

试试用AI创作助手写篇文章吧