C语言范例学习03-上
第三章 数据结构
章首:不好意思,这两天要帮家里做一些活儿。而且内容量与操作量也确实大幅提升了。所以写得很慢。
不过,从今天开始。我写的东西,许多都是之前没怎么学的了。所以速度会慢下来,同时写得也会详细许多。
第三章是数据结构,数据结构可以说是理论学习的重点。同时许多学校(包括我所就读的大学)都开设了数据结构课程。但是讲的东西大多太过理论性,主要讲解概念与思想。
另外,数据结构可是计算机考研中专业课的重点科目哦。
这章数据结构包括结构体、链表、栈与队列、串与广义表、二叉树、图与图的应用。
每一个都是重点,所以我会细细地过一遍。
3.1结构体
ps:也许你的C语言老师不会讲解链表、二叉树等,但他一定会讲解结构体的使用,并且让你们作出代码实现。
结构体是什么?其实结构体就是一个制式容器。里面包括了规定的各个变量。
比如说,我设定了
1 struct student 2 { 3 char name; 4 int age; 5 float score;6 };
那么在这个结构体所在的程序里,student就是一个包含三个不同数据类型的变量。当然,你也可以认为student是一个新的数据类型,一个自己设立的数据类型。不过这个数据类型是一个特定的数组,因为其中有着许多不同的数据类型(数组只能是相同数据类型)。
创建结构体是要用到struct。其他用法将在接下来的例子中提到。
实例076
问题:从键盘中输入姓名和电话号码,以#结束,编程实现输入姓名,查询电话号码的功能。
逻辑:首先一个主函数作为程序入口,实现程序运行框架。然后一个存储函数readin实现数据的存储,再一个查询函数seach实现数据的查找。
主函数无非定义,初始化变量。调用存储函数来存储我们输入的函数。再调用查询函数来输出所要的结果。
存储函数定义,初始化变量。建立循环,判读输入是否为#,同时将数据存储。
查询函数定义,初始化变量。建立循环,判断所要查找的数据与当前数据是否符合,判断数据是否已经全部遍历。
代码:
1 #include<stdio.h> 2 #include<string.h> 3 #define MAX 101 4 struct aa 5 { 6 char name[15]; 7 char tel[15]; 8 }; 9 int readin(struct aa *a) 10 11 //注意这里结构体aa的变量名为*a,是一个指针,因为这个结构体在三个函数中均被调用。所以只有使用指针才较为便利地可以实现函数间大量数据的调用。 12 13 //其实,在使用指针为变量时,就已经建立了一个以a为名的aa型结构体数组了。 14 15 { 16 int i=0,n=0; 17 while(1) 18 19 //本人最为喜欢的循环体设置方法,包括for(;1;)的设立,循环体中再建立相关判断条件。 20 { 21 scanf("%s",a[i].name); 22 if(!strcmp(a[i].name,"#")) 23 24 //利用这样的判断语句判断输入是否为#,(其实可以在判断语句中实现赋值与判断的共同实现,虽然好看,但是确保自己逻辑正确。) 25 26 //strcmp()比较两个字符串是否相等,相等返还0,相等返还0,相等返还0. 27 break; 28 scanf("%s",a[i].tel); 29 i++; 30 n++; 31 } 32 return n; 33 34 //返回输入结构体的数量。(一组数据就是一个结构体) 35 } 36 void search(struct aa *b,char *x,int n) 37 38 //x是一个指针,主函数中name是数组名,即一个指针。 39 { 40 int i; 41 i=0; 42 while(1) 43 { 44 if(!strcmp(b[i].name,x)) 45 { 46 printf("name:%s tel:%s\n",b[i].name,b[i].tel); 47 break; 48 } 49 else 50 i++; 51 n--; 52 53 //其实这个n是用来计算是否所有结构体遍历。但是我认为用i就行了。虽然i是在判断语句里面,但这个语句在没找到目标是是一直执行的,如同在外面和n相同待遇 54 55 //当判断语句找到目标时,那就更不需要他了。因为程序结束了。 56 57 if(n==0) 58 { 59 printf("No found!"); 60 break; 61 } 62 } 63 } 64 main() 65 { 66 struct aa s[MAX]; 67 int num; 68 char name[15]; 69 num=readin(s); 70 71 //这个语句有两个用处,1.将readin函数返回给num;2.readin函数将数据存储于数组s。 72 printf("input the name:"); 73 scanf("%s",name); 74 search(s,name,num); 75 }
反思:结构体在日后的使用中,就好像int、char这些数据类型一般,很常用。同时,这个程序中,我们学到程序中函数的简单架构。另外,我们完全可以将这个函数扩展化。比如小到平时存储东西,查找东西。大到各个程序中的存储,查找功能等。
3.2链表
ps:一般来说,数据结构这门课讲解的第一个数据结构一般就是链表。很多教材第一页就是链表。
曾经有公司高层谈论编程时,说:”学生大多缺乏实际编程经验,我在招聘时经常让应聘者做一个简单的链表。结果很多人都不会。“
链表可以说是数据结构应用的入门,如果连这个都不会,最好别去考虑与编程有关的职业。
曾经看过一本书,它的开头讲述了一个故事。说有一个刚入行的程序员第一次处理网站数据,做了一个1500长度的数组和循环用来存储临时数据。然后不久那个网站就挂了。后来作者去就将他做得数组改成了一个1500的循环链表就完事儿了。
链表是动态分配存储空间来存储数据,避免了空间的浪费。(当年用数组存储数据有多少人和我一样纠结空间大小的举个手)同时,链表的存储空间可以不连续、不连续、不连续。这样产生了许多便利,并且有效利用了存储空间。
我会讲解每一个例子。(我才不会说,之前看的时候,我就记得单向链表了。。。)
实例078 创建单向链表
问题:创建一个单向链表,实现数据的输入、输出。
逻辑:链表是由一个个节点组成的。每一个节点分为两部分:数据域与指针域。数据域用来存储数据,指针域用来存储下一个节点位置(双向链表就还有一个指向前一个节点的链表)。一般第0个节点是整个链表的头结点,称为”头指针“,一般不存放数据,只存放指向第1个节点的指针。链表的最后一个节点的指针设为空(NULL),作为链表的结束标志。
代码:
1 #include<stdio.h> 2 #include<malloc.h> 3 //malloc是用来划分存储空间的函数。 4 struct LNode 5 { 6 int data; 7 struct LNode *next; 8 //指向下一个的结构体。从而形成链表。 9 }; 10 struct LNode *create(int n) 11 { 12 int i; 13 struct LNode *head,*p1,*p2; 14 //head表示头节点。p1,实现存储空间的获取,数据域赋值等。p2,作为与p1的中间指针,构成链表的替换连接。 15 int a; 16 head=NULL; 17 //头节点为空。 18 printf("Input the integers:\n"); 19 for(i=n;i>0;--i) 20 { 21 p1=(struct LNode*)malloc(sizeof(struct LNode)); 22 //malloc(sizeof(struct LNode)表示划分LNode大小的存储空间。 23 //(struct LNode*)表示数据类型的转换,将划分来的存储空间首地址转化为LNode的变量名(指针类型)。 24 scanf("%d",&a); 25 p1->data=a; 26 //向p1的结构体中存储数据。 27 if(head==NULL) 28 { 29 head=p1; 30 p2=p1; 31 //对头节点的处理。 32 } 33 else 34 { 35 p2->next=p1; 36 //表明p2的下一个节点是p1。其中p2是上个循环中的p1。也就是说,p2只是一个中间变量temp。 37 p2=p1; 38 //印证了上个注释中,p2的由来。 39 //具体的过程在变量名的说明中就已经体现了。 40 } 41 } 42 p2->next=NULL; 43 //最后一个节点的指针域为空,作为结束的标志。 44 return head; 45 //返回代表头结点的结构体的变量名(指针)。 46 } 47 void main() 48 { 49 int n; 50 struct LNode *q; 51 printf("Input the count of the nodes you want to creat:"); 52 scanf("%d",&n); 53 q=create(n); 54 //输入,返回。 55 printf("The result is:\n"); 56 while(q) 57 //q只有到了最后一个节点的next时,为NULL,才会跳出循环。一个很有用的小技巧。 58 { 59 printf("%d ",q->data); 60 q=q->next; 61 //实现q的转换,从而输出所有数据。 62 } 63 getch(); 64 }
ps:如果对其中提到的malloc函数,以及相关的calloc函数,free函数感兴趣的话,可以浏览http://blog.csdn.net/shuaishuai80/article/details/6140979。
反思:学习该例有这么几个要点: 1.指针不要混淆,不要将结构体名的指针和结构体内next的指针混淆,虽然有时这两者表达的是同一个东西;
2.一定要完全理解p1=(struct LNode*)malloc(sizeof(struct LNode));这句代码;(解释在样例中)
3.正确理解p2->next=p1;与p2=p1;这两句代码的实现。(无法理解可带入数据进行两到三次循环,即可理解)。
实例079 创建双向链表
问题:创建一个双向链表 ,并将这个链表中数据输出到窗体上,输入要查找的学生姓名,将查找的姓名从链表中删除,并现实删除后的链表。
逻辑:其实就逻辑而言是很简单的。与之前的单向链表相同,不过比后继结点的设置多了一个前驱节点的设置。另外查找,与一般的结构体数组查找,并没有什么区别。至于删除嘛,就需要改动删除节点的前驱节点和后继结点的设置。具体操作,可以看代码。
代码:
1 #include<stdio.h> 2 typedef struct node 3 { 4 char name[20]; 5 struct *prior,*next; 6 //设立节点的前驱节点和后继结点。 7 }stud; 8 stud *creat(int n) 9 { 10 stud *p,*h,*s; 11 int i; 12 h=(stud*)malloc(sizeof(stud)); 13 //申请存储空间 14 h->name[0]='\0'; 15 //设置name为空 16 h->prior=NULL; 17 h->next=NULL; 18 p=h; 19 for(i=0;i<n;i++) 20 { 21 s=(stud*)malloc(sizeof(stud)); 22 p->next=s; 23 printf("Input the %d student'sname:",i+1); 24 scanf("%s",s->name); 25 s->prior=p; 26 s->next=NULL; 27 p=s; 28 //方法和单向链表没什么区别,区别只在于多了一个前驱节点的设置。 29 //从一些方面来说,多了前驱节点更容易理解了。 30 } 31 p->next=NULL; 32 //设置最后节点的后继结点为空,作为结束标志。 33 return(h); 34 } 35 stud *search(stud *h,char *x) 36 { 37 stud *p; 38 char *y; 39 p=h->next; 40 while(p) 41 { 42 y=p->name; 43 if(strcmp(y,x)==0) 44 //判断是否为寻找的目标。 45 return(p); 46 //返回目标地址。 47 else 48 p=p->next; 49 //继续下一个节点的检测。 50 } 51 printf("cannot find data!\n"); 52 //没有任何符合条件的返回。 53 } 54 void del(stud *p) 55 { 56 p->next->prior=p->prior; 57 //令p的下一个节点的前驱节点与现在p的前驱节点一致(这样在前驱节点中p就不存在了。并且链表没有断开。) 58 p->prior->next=p->next; 59 //令p的前一个节点的后继结点与现在p的后继结点一致(这样在后街节点中p就不存在了。并且链表没有断开。) 60 //切记:p->next是指p的后一个节点。p->prior同理。 61 //无法理解的话,请在草稿纸上画出链表图,就清晰无比了。 62 //这里两句代码在有些运行环境中会报错,可以自行更改语句或环境。 63 free(p); 64 //释放p的存储空间(链表上p已经不存在了。当然不能让它占着空间了) 65 } 66 main() 67 { 68 int number; 69 char sname[20]; 70 stud *head,*sp; 71 puts("Please input the size of the list:"); 72 scanf("%d",&number); 73 head=creat(number); 74 sp=head->next; 75 printf("\nNow the double list is:\n"); 76 while(sp) 77 { 78 printf("%s",&*(sp->name)); 79 sp=sp->next; 80 } 81 //之前的都与单向链表相同。 82 printf("\nPlease input the name which you want to find:\n"); 83 scanf("%s",sname); 84 sp=search(head,sname); 85 //通过search函数,寻找到所要寻找的sname的地址sp。 86 printf("the name you want to find is:\n",*&sp->name); 87 del(sp); 88 //将sp带入到del()函数中,删除。 89 sp=head->next; 90 //这句话只是为了下面的while循环做二次利用的。完全可以删除这句话,为下面的循环单独设置一个变量。 91 printf("\nNow the double list is:\n"); 92 while(sp) 93 { 94 printf("%s",&*(sp->name)); 95 sp=sp->next; 96 } 97 printf("\n"); 98 puts("\nPress any key to quit..."); 99 getch(); 100 //与单向链表相同的输出原理。 101 }
反思:双向链表与单向链表并没有什么不同。要记得的是,双向链表可是比单向链表多了一个prior的指针,无论是添加,删除,移动,修改,都要记住。
实例080 循环链表
还记得开头,我说的那个网站关于链表的故事吗。这下说的就是循环链表。
其实到了这个时候也就没有什么说的了。循环链表就是将最后节点的后继结点设置为头结点。
循环链表与普通链表的操作基本一致,只是在算法中循环遍历链表节点时判断条件不再是p->next是否为空,而是是否等于链表的头结点
程序代码:(这里就不演示。篇幅不应该留给不需要的东西。)
实例081 双链表逆置
问题:创建一个双向链表,将双向链表的节点逆置,即将尾节点放到第一个节点的位置,倒数第二个节点放到第二个节点的位置,依此类推。
逻辑:双向链表的创建、输入、输出,都和之前一样。为了模块化的操作,就单独创建一个reverse函数,用来执行逆置操作。逆置操作就是指针的变换。
部分代码:
1 stud *reverse(stud *head) 2 { 3 stud *p,*r,*h; 4 h=head->next; 5 6 //设置h。 7 if(h&&h->next) 8 { 9 p=h; 10 r=p->next; 11 p->next=NULL; 12 while(r) 13 { 14 p=r; 15 r=r->next; 16 p->next=h; 17 h->prior=p; 18 h=p; 19 20 //中间变量p,变量r从链表的头部向后遍历,变量h从链表的尾部向前遍历。变量p作为中间变量来转移指针地址,完成链表逆置。 21 } 22 head->next=h; 23 h->prior=head; 24 25 //事后,完成链表的补全。即链表的开头和结尾。 26 return head; 27 } 28 }
反思:想要完成这些,自己一定不能混淆指针,指针的指针,指针的指向。这三个概念。
后面还有很多相关的知识,比如逆序输出,约瑟夫环,链表的元素插入,节点插入,节点删除,合并链表以及头插入法建立链表等。但是,我认为,如果之前的链表都懂了。那么后面的知识无非就是链表的基础知识版应用。如果有需要,可以找我。
总结:其实链表在学习后,就会发现,之前指针学得好是多么重要啊。尤其是指针的指针这一点。如果,指针学得好,那么链表要学的就是链表的概念,链表的一些专用函数,以及常用的算法。那么之后的一些应用就是水到渠成的事儿。(完全就是之前程序的链表版应用啦。)
由于时间关系,这次就先写这么多了。(其实这次写的比之前多了很多,尤其线下还做了那么多的程序调试。)
谢谢大家的鉴赏和评价。也希望能够结识一些相关兴趣的小伙伴。
另外,也许不久,我还会写一些别的东西。
(刚刚博客园官方通知了我,我才知道有代码插入这个东西。挺赞的。对于我这个有代码行洁癖的,每次copy后敲Tab,也是蛮难过的。谢谢了。)
转载于:https://www.cnblogs.com/Tiancheng-Duan/p/5702134.html
C语言范例学习03-上相关推荐
- C语言再学习 -- Linux下find命令用法
参看:linux下find(文件查找)命令的用法总结 linux下查找文件的命令有两个:locate 和 find 首先说一下locate: 这个命名是对其生成的数据库进行遍历(生成数据库的命令:uo ...
- EDA实验课课程笔记(三)——TCL脚本语言的学习1
本文参考资料为<Tcl语言教程>,感谢作者的分享,这里仅仅作为简单常用语法的入门,若有需要后期对本文进行添加补充. EDA实验课课程笔记(三)--TCL脚本语言的学习 前言(TCL综述) ...
- c语言编程学多久,丰城c语言编程学习,丰城学c语言编程的学校,丰城学c语言编程一般要多久才能学会...
丰城c语言编程学习,丰城学c语言编程的学校,丰城学c语言编程一般要多久才能学会 首页 > 软件 > 丰城c语言编程学习 作者:镀金池 发布时间:2018-04-09 16:40 在之后 ...
- C语言报名里面培训怎么填,庄河c语言编程学习,庄河学c语言编程培训,庄河学c语言编程报个培训班怎么样...
庄河c语言编程学习,庄河学c语言编程培训,庄河学c语言编程报个培训班怎么样 首页 > 软件 > 庄河c语言编程学习 作者:镀金池 发布时间:2017-11-29 11:13 明天利用时 ...
- 安庆师范大学c语言程序设计,安庆c语言编程学习,安庆学c语言编程培训,安庆学c语言编程一般能拿多少工资...
安庆c语言编程学习,安庆学c语言编程培训,安庆学c语言编程一般能拿多少工资 首页 > C语言 > 安庆c语言编程学习 作者:镀金池 发布时间:2017-10-18 15:20 假定我们 ...
- c语言 字母 八进制表示'/1011',C语言C语言第一课:C语言概述为什么学习C语言怎样学习C语言.DOC...
[摘要]C语言 第一课: C语言概述 为什么学习C语言 怎样学习C语言 参考资料 ----------------------------------------------------------- ...
- 秦州:西瓜书 + 南瓜书 吃瓜系列 13. 降维与度量学习(上)
吃瓜教程--西瓜书+南瓜书 Datawhale南瓜书是经典机器学习教材<机器学习>(西瓜书)的公式推导解析指南,旨在让在学习西瓜书的过程中,再也没有难推的公式,学好机器学习. 内容属性:机 ...
- R语言可视化学习笔记之相关矩阵可视化包ggcorrplot
本文转载自"R语言中文社区",己获授权. 作者简介Introduction taoyan:伪码农,R语言爱好者,爱开源. 个人博客: https://ytlogos.github. ...
- 一文回顾深度学习发展史上最重要经典模型
这篇文章的目的是回顾经过时间考验的,被广泛采用的想法.我将介绍一小部分技术,这些技术涵盖了解现代深度学习研究所必需的许多基本知识.如果你是该领域的新手,那么这是一个很好的起点. 深度学习是一个瞬息万变 ...
- c语言struct_学习了C语言之后还是感觉不会编程,应该怎么办?其实你想错了!...
其实对于程序而言,C语言抑或着C++.Java等其他程序语言只是用于表达你的想法的一个工具.就像让我们以建筑为例,画图是一个基本功,但是画什么才是核心所在.那么,今天我们以一个设计一个RPG(角色扮演 ...
最新文章
- kvm_guest主机克隆
- 计算机组装与维护教案_计算机组装与维护小课堂(1)
- 通过jsl工具将java程序注册为windows服务
- 二 DeepinV20版本安装
- 初学Java Web(3)——第一个Servlet
- ajax 获取服务器返回的XML字符串
- 软件项目管理的内在定律
- threading模块的使用
- NUll is null like
- 【报告分享】零售行业三大平台之对比分析-阿里VS京东VS拼多多:分级、竞争、进化.pdf...
- Windows程序开发——指挥官夏尔对于Windows程序开发框架的选择
- linux php oauth安装,Linux安装phpmyadmin
- 奔图 Pantum M6550 打印机驱动
- 新基建促进智能化基础设施管控平台的搭建
- Mac os 10.5.8, 驱动声卡成功
- beacon帧主要结构
- 高通骁龙600系列处理器
- pytorch【Conv2d参数介绍】
- 目标检测比赛提高mAP的方法
- “一阶数字低通滤波器”原理推导(含仿真和代码实现)
热门文章
- 关于Mysql datetime类型存储范围测试
- ubantu删除文件(夹)
- 字符串匹配-kmp算法
- Angular实现购物车计算
- 关于memcpy和 strcpy的区别 以及memset
- Error:Execution failed for task ':app:clean'. Unable to delete directory: /media/file/workspaces/a
- mabatis传入参数
- SQLserver查询练习
- zencart 1.5.1 英文原版 安装前和安装后目录文件的变换
- 在BizTalk Server 2006 R2 中调用 WCF Services – Part 3