C语言范例学习03-上
第三章 数据结构
章首:不好意思,这两天要帮家里做一些活儿。而且内容量与操作量也确实大幅提升了。所以写得很慢。
不过,从今天开始。我写的东西,许多都是之前没怎么学的了。所以速度会慢下来,同时写得也会详细许多。
第三章是数据结构,数据结构可以说是理论学习的重点。同时许多学校(包括我所就读的大学)都开设了数据结构课程。但是讲的东西大多太过理论性,主要讲解概念与思想。
另外,数据结构可是计算机考研中专业课的重点科目哦。
这章数据结构包括结构体、链表、栈与队列、串与广义表、二叉树、图与图的应用。
每一个都是重点,所以我会细细地过一遍。
3.1结构体
ps:也许你的C语言老师不会讲解链表、二叉树等,但他一定会讲解结构体的使用,并且让你们作出代码实现。
结构体是什么?其实结构体就是一个制式容器。里面包括了规定的各个变量。
比如说,我设定了
struct student
{
char name;
int age;
float score;
6 };
那么在这个结构体所在的程序里,student就是一个包含三个不同数据类型的变量。当然,你也可以认为student是一个新的数据类型,一个自己设立的数据类型。不过这个数据类型是一个特定的数组,因为其中有着许多不同的数据类型(数组只能是相同数据类型)。
创建结构体是要用到struct。其他用法将在接下来的例子中提到。
实例076
问题:从键盘中输入姓名和电话号码,以#结束,编程实现输入姓名,查询电话号码的功能。
逻辑:首先一个主函数作为程序入口,实现程序运行框架。然后一个存储函数readin实现数据的存储,再一个查询函数seach实现数据的查找。
主函数无非定义,初始化变量。调用存储函数来存储我们输入的函数。再调用查询函数来输出所要的结果。
存储函数定义,初始化变量。建立循环,判读输入是否为#,同时将数据存储。
查询函数定义,初始化变量。建立循环,判断所要查找的数据与当前数据是否符合,判断数据是否已经全部遍历。
代码:
#include<stdio.h>
#include<string.h>
#define MAX 101
struct aa
{
char name[];
char tel[];
};
int readin(struct aa *a) //注意这里结构体aa的变量名为*a,是一个指针,因为这个结构体在三个函数中均被调用。所以只有使用指针才较为便利地可以实现函数间大量数据的调用。 //其实,在使用指针为变量时,就已经建立了一个以a为名的aa型结构体数组了。 {
int i=,n=;
while() //本人最为喜欢的循环体设置方法,包括for(;1;)的设立,循环体中再建立相关判断条件。
{
scanf("%s",a[i].name);
if(!strcmp(a[i].name,"#")) //利用这样的判断语句判断输入是否为#,(其实可以在判断语句中实现赋值与判断的共同实现,虽然好看,但是确保自己逻辑正确。) //strcmp()比较两个字符串是否相等,相等返还0,相等返还0,相等返还0.
break;
scanf("%s",a[i].tel);
i++;
n++;
}
return n; //返回输入结构体的数量。(一组数据就是一个结构体)
}
void search(struct aa *b,char *x,int n) //x是一个指针,主函数中name是数组名,即一个指针。
{
int i;
i=;
while()
{
if(!strcmp(b[i].name,x))
{
printf("name:%s tel:%s\n",b[i].name,b[i].tel);
break;
}
else
i++;
n--; //其实这个n是用来计算是否所有结构体遍历。但是我认为用i就行了。虽然i是在判断语句里面,但这个语句在没找到目标是是一直执行的,如同在外面和n相同待遇 //当判断语句找到目标时,那就更不需要他了。因为程序结束了。 if(n==)
{
printf("No found!");
break;
}
}
}
main()
{
struct aa s[MAX];
int num;
char name[];
num=readin(s); //这个语句有两个用处,1.将readin函数返回给num;2.readin函数将数据存储于数组s。
printf("input the name:");
scanf("%s",name);
search(s,name,num);
}
反思:结构体在日后的使用中,就好像int、char这些数据类型一般,很常用。同时,这个程序中,我们学到程序中函数的简单架构。另外,我们完全可以将这个函数扩展化。比如小到平时存储东西,查找东西。大到各个程序中的存储,查找功能等。
3.2链表
ps:一般来说,数据结构这门课讲解的第一个数据结构一般就是链表。很多教材第一页就是链表。
曾经有公司高层谈论编程时,说:”学生大多缺乏实际编程经验,我在招聘时经常让应聘者做一个简单的链表。结果很多人都不会。“
链表可以说是数据结构应用的入门,如果连这个都不会,最好别去考虑与编程有关的职业。
曾经看过一本书,它的开头讲述了一个故事。说有一个刚入行的程序员第一次处理网站数据,做了一个1500长度的数组和循环用来存储临时数据。然后不久那个网站就挂了。后来作者去就将他做得数组改成了一个1500的循环链表就完事儿了。
链表是动态分配存储空间来存储数据,避免了空间的浪费。(当年用数组存储数据有多少人和我一样纠结空间大小的举个手)同时,链表的存储空间可以不连续、不连续、不连续。这样产生了许多便利,并且有效利用了存储空间。
我会讲解每一个例子。(我才不会说,之前看的时候,我就记得单向链表了。。。)
实例078 创建单向链表
问题:创建一个单向链表,实现数据的输入、输出。
逻辑:链表是由一个个节点组成的。每一个节点分为两部分:数据域与指针域。数据域用来存储数据,指针域用来存储下一个节点位置(双向链表就还有一个指向前一个节点的链表)。一般第0个节点是整个链表的头结点,称为”头指针“,一般不存放数据,只存放指向第1个节点的指针。链表的最后一个节点的指针设为空(NULL),作为链表的结束标志。
代码:
#include<stdio.h>
#include<malloc.h>
//malloc是用来划分存储空间的函数。
struct LNode
{
int data;
struct LNode *next;
//指向下一个的结构体。从而形成链表。
};
struct LNode *create(int n)
{
int i;
struct LNode *head,*p1,*p2;
//head表示头节点。p1,实现存储空间的获取,数据域赋值等。p2,作为与p1的中间指针,构成链表的替换连接。
int a;
head=NULL;
//头节点为空。
printf("Input the integers:\n");
for(i=n;i>;--i)
{
p1=(struct LNode*)malloc(sizeof(struct LNode));
//malloc(sizeof(struct LNode)表示划分LNode大小的存储空间。
//(struct LNode*)表示数据类型的转换,将划分来的存储空间首地址转化为LNode的变量名(指针类型)。
scanf("%d",&a);
p1->data=a;
//向p1的结构体中存储数据。
if(head==NULL)
{
head=p1;
p2=p1;
//对头节点的处理。
}
else
{
p2->next=p1;
//表明p2的下一个节点是p1。其中p2是上个循环中的p1。也就是说,p2只是一个中间变量temp。
p2=p1;
//印证了上个注释中,p2的由来。
//具体的过程在变量名的说明中就已经体现了。
}
}
p2->next=NULL;
//最后一个节点的指针域为空,作为结束的标志。
return head;
//返回代表头结点的结构体的变量名(指针)。
}
void main()
{
int n;
struct LNode *q;
printf("Input the count of the nodes you want to creat:");
scanf("%d",&n);
q=create(n);
//输入,返回。
printf("The result is:\n");
while(q)
//q只有到了最后一个节点的next时,为NULL,才会跳出循环。一个很有用的小技巧。
{
printf("%d ",q->data);
q=q->next;
//实现q的转换,从而输出所有数据。
}
getch();
}
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 创建双向链表
问题:创建一个双向链表 ,并将这个链表中数据输出到窗体上,输入要查找的学生姓名,将查找的姓名从链表中删除,并现实删除后的链表。
逻辑:其实就逻辑而言是很简单的。与之前的单向链表相同,不过比后继结点的设置多了一个前驱节点的设置。另外查找,与一般的结构体数组查找,并没有什么区别。至于删除嘛,就需要改动删除节点的前驱节点和后继结点的设置。具体操作,可以看代码。
代码:
#include<stdio.h>
typedef struct node
{
char name[];
struct *prior,*next;
//设立节点的前驱节点和后继结点。
}stud;
stud *creat(int n)
{
stud *p,*h,*s;
int i;
h=(stud*)malloc(sizeof(stud));
//申请存储空间
h->name[]='\0';
//设置name为空
h->prior=NULL;
h->next=NULL;
p=h;
for(i=;i<n;i++)
{
s=(stud*)malloc(sizeof(stud));
p->next=s;
printf("Input the %d student'sname:",i+);
scanf("%s",s->name);
s->prior=p;
s->next=NULL;
p=s;
//方法和单向链表没什么区别,区别只在于多了一个前驱节点的设置。
//从一些方面来说,多了前驱节点更容易理解了。
}
p->next=NULL;
//设置最后节点的后继结点为空,作为结束标志。
return(h);
}
stud *search(stud *h,char *x)
{
stud *p;
char *y;
p=h->next;
while(p)
{
y=p->name;
if(strcmp(y,x)==)
//判断是否为寻找的目标。
return(p);
//返回目标地址。
else
p=p->next;
//继续下一个节点的检测。
}
printf("cannot find data!\n");
//没有任何符合条件的返回。
}
void del(stud *p)
{
p->next->prior=p->prior;
//令p的下一个节点的前驱节点与现在p的前驱节点一致(这样在前驱节点中p就不存在了。并且链表没有断开。)
p->prior->next=p->next;
//令p的前一个节点的后继结点与现在p的后继结点一致(这样在后街节点中p就不存在了。并且链表没有断开。)
//切记:p->next是指p的后一个节点。p->prior同理。
//无法理解的话,请在草稿纸上画出链表图,就清晰无比了。
//这里两句代码在有些运行环境中会报错,可以自行更改语句或环境。
free(p);
//释放p的存储空间(链表上p已经不存在了。当然不能让它占着空间了)
}
main()
{
int number;
char sname[];
stud *head,*sp;
puts("Please input the size of the list:");
scanf("%d",&number);
head=creat(number);
sp=head->next;
printf("\nNow the double list is:\n");
while(sp)
{
printf("%s",&*(sp->name));
sp=sp->next;
}
//之前的都与单向链表相同。
printf("\nPlease input the name which you want to find:\n");
scanf("%s",sname);
sp=search(head,sname);
//通过search函数,寻找到所要寻找的sname的地址sp。
printf("the name you want to find is:\n",*&sp->name);
del(sp);
//将sp带入到del()函数中,删除。
sp=head->next;
//这句话只是为了下面的while循环做二次利用的。完全可以删除这句话,为下面的循环单独设置一个变量。
printf("\nNow the double list is:\n");
while(sp)
{
printf("%s",&*(sp->name));
sp=sp->next;
}
printf("\n");
puts("\nPress any key to quit...");
getch();
//与单向链表相同的输出原理。
}
反思:双向链表与单向链表并没有什么不同。要记得的是,双向链表可是比单向链表多了一个prior的指针,无论是添加,删除,移动,修改,都要记住。
实例080 循环链表
还记得开头,我说的那个网站关于链表的故事吗。这下说的就是循环链表。
其实到了这个时候也就没有什么说的了。循环链表就是将最后节点的后继结点设置为头结点。
循环链表与普通链表的操作基本一致,只是在算法中循环遍历链表节点时判断条件不再是p->next是否为空,而是是否等于链表的头结点
程序代码:(这里就不演示。篇幅不应该留给不需要的东西。)
实例081 双链表逆置
问题:创建一个双向链表,将双向链表的节点逆置,即将尾节点放到第一个节点的位置,倒数第二个节点放到第二个节点的位置,依此类推。
逻辑:双向链表的创建、输入、输出,都和之前一样。为了模块化的操作,就单独创建一个reverse函数,用来执行逆置操作。逆置操作就是指针的变换。
部分代码:
stud *reverse(stud *head)
{
stud *p,*r,*h;
h=head->next; //设置h。
if(h&&h->next)
{
p=h;
r=p->next;
p->next=NULL;
while(r)
{
p=r;
r=r->next;
p->next=h;
h->prior=p;
h=p; //中间变量p,变量r从链表的头部向后遍历,变量h从链表的尾部向前遍历。变量p作为中间变量来转移指针地址,完成链表逆置。
}
head->next=h;
h->prior=head; //事后,完成链表的补全。即链表的开头和结尾。
return head;
}
}
反思:想要完成这些,自己一定不能混淆指针,指针的指针,指针的指向。这三个概念。
后面还有很多相关的知识,比如逆序输出,约瑟夫环,链表的元素插入,节点插入,节点删除,合并链表以及头插入法建立链表等。但是,我认为,如果之前的链表都懂了。那么后面的知识无非就是链表的基础知识版应用。如果有需要,可以找我。
总结:其实链表在学习后,就会发现,之前指针学得好是多么重要啊。尤其是指针的指针这一点。如果,指针学得好,那么链表要学的就是链表的概念,链表的一些专用函数,以及常用的算法。那么之后的一些应用就是水到渠成的事儿。(完全就是之前程序的链表版应用啦。)
由于时间关系,这次就先写这么多了。(其实这次写的比之前多了很多,尤其线下还做了那么多的程序调试。)
谢谢大家的鉴赏和评价。也希望能够结识一些相关兴趣的小伙伴。
另外,也许不久,我还会写一些别的东西。
(刚刚博客园官方通知了我,我才知道有代码插入这个东西。挺赞的。对于我这个有代码行洁癖的,每次copy后敲Tab,也是蛮难过的。谢谢了。)
C语言范例学习03-上的更多相关文章
- C语言范例学习04
第三章 算法 前言:许多人对算法的看法是截然不同的,我之前提到过了.不过,我要说的还是那句话:算法体现编程思想,编程思想指引算法. 同时,有许多人认为简单算法都太简单了,应当去学习一些更为实用的复杂算 ...
- C语言范例学习03-中
栈和队列 这两者都是重要的数据结构,都是线性结构.它们在日后的软件开发中有着重大作用.后面会有实例讲解. 两者区别和联系,其实总结起来就一句.栈,后进先出:队列,先进先出. 可以将栈与队列的存储空间比 ...
- C语言范例学习01
编程语言的能力追求T型. 以前学过C语言,但是只学了理论. 从今天开始,我买了本<C语言程序开发范例宝典>.我要把它通关掉. 这应该可以极大地提升我的编程能力. 第一章 基础知识 这章没太 ...
- C语言范例学习06-上
第六章 文件操作 前言:第五章是C语言在数学上的一些应用,我觉得没有必要,便跳过了.这章正如我标题所写的,是C语言在文件上的操作.学习了这个后,你们可以自行编辑一些所需的快捷程序,来实现一些既定的目的 ...
- 16.go语言基础学习(上)——2019年12月16日
2019年12月13日10:35:20 1.介绍 2019年10月31日15:09:03 2.基本语法 2.1 定义变量 2019年10月31日16:12:34 1.函数外必须使用var定义变量 va ...
- c语言基础学习03
=============================================================================涉及到的知识点有:编码风格.c语言的数据类型. ...
- C语言范例学习03-下
树与图 3.5 二叉树及其应用 PS:二叉树是最经典的树形结构,适合计算机处理,具有存储方便和操作灵活等特点,而且任何树都可以转换成二叉树. 实例101 二叉树的递归创建 实例102 二叉树的遍历 问 ...
- C语言范例学习02
第二章 指针 算是重点吧,这也是C语言的特色啊,直接访问物理存储. 重点: 指针就是一个存放它指向变量地址的变量,好绕口. 区分*在定义是与引用是的作用. 区分*.&的不同. 指针 ...
- | C语言I作业03
| C语言I作业03 标签: 18软件 李煦亮 问题 答案 这个作业属于那个课程 C语言程序设计I 这个作业要求在哪里 https://edu.cnblogs.com 我在这个课程的目标是 学会和掌握 ...
随机推荐
- samba 服务器的搭建
一,安装samba4 不要直接 yum install samba ,默认安装的是samba3版本,但这个版本有问题(open_rpc_pipe_p: copy_serverinfo failed这个 ...
- git删除远程文件夹或文件的方法
由于本地修改了文件夹大全名大小写的原因,同步到git上并不区分大小写,造成了一些文件同步不了,所以要先把git远程库上文件夹删除掉,然后再重新同步 如下,我把src里的全部移除,但是本地文件还保留. ...
- SD卡中FAT32文件格式快速入门(图文详细介绍)【转】
本文转自:http://blog.csdn.net/mjx91282041/article/details/8904705 说明: MBR :Master Boot Record ( 主引导记录) D ...
- cocos2dx 2.x 骨骼动画优化
本文原链接:http://www.cnblogs.com/zouzf/p/4450861.html 公司用的骨骼动画的版本貌似还停留在2.1之前的年代而已没有更新,该因各种历史原因吧,而有个大项目“一 ...
- java之容器
先来一张容器的API框架图,我们在java中所学的所有知识,都是根据下面这张图来学习的.... 容器API: 1.Collection接口------定义了存储一组对象的方法,其子接口Set和List ...
- mysql 索引- 笔记
索引 mysql最常用的索引结构是btree(O(log(n))),但是总有一些情况下我们为了更好的性能希望能使用别的类型的索引.hash就是其中一种选择,例如我们在通过用户名检索用户id的时候,他们 ...
- Ubuntu下PHP的扩展
Ubuntu版本:14.04 1. 下载php-5.5.10.tar.bz2,并解压. 2. 终端进入解压后的目录php-5.5.10,运行configure.(输入命令./configure) 3 ...
- 未能找到类型或命名空间名称“Coco”(是否缺少 using 指令或程序集引用)
未能找到类型或命名空间名称"Coco"(是否缺少 using 指令或程序集引用),如果你确实引用了,那说明你引用的和你的项目环境版本不一样,.NET framework的问题,修改 ...
- hbase安装
HBase的安装 本篇介绍两种HBase的安装方式:本地安装方式和伪分布式安装方式. 安装的前提条件是已经成功安装了hadoop,而且hadoop的版本要和hbase的版本相匹配. 我将要安装的hba ...
- VS2008简体中文正式版序列号-试用到期解决
VS2008简体中文正式版序列号 VS2008简体中文正式版序列号 1.Visual Studio 2008 Professional Edition:XMQ2Y-4T3V6-XJ48Y-D3K2V- ...