学习内容
已记录:链表、文件
未记录:有限状态自动机
目录
链表
基础必备函数:创建空表头,节点插入(尾插),销毁链表
//创建空链表头
void createListHead(node ** headPtrPtr)
{
*headPtrPtr = malloc(sizeof(node));
if(*headPtrPtr!=NULL)
{
(*headPtrPtr)->nextPtr = NULL;
}
}
//参数是指向链表头指针的指针
//1.给头节点分配空间
//2.如果分配成功,让其指向的节点的nextPtr指向NULL
//节点尾插
void insertEnd(node * headPtr, int value)
{
node * newPtr, * currentPtr,*prePtr;
newPtr = malloc(sizeof(node));
if(newPtr!=NULL)
{
newPtr->value = value;
newPtr->nextPtr=NULL;
prePtr=headPtr; //需要两个复制指针,prePtr和currentPtr
currentPtr=headPtr->nextPtr;
while(currentPtr!=NULL)
{
prePtr=currentPtr;
currentPtr=currentPtr->nextPtr;
}
prePtr->nextPtr=newPtr;
}
else
{
printf("No memory avaliable.\n");
}
}
//参数是头节点的指针,值
//创建新节点,分配空间,如果分配成功
//对于新节点:记录值,指向空
//找到链表的尾节点,让其nextPtr指向新节点
//销毁链表
void destoryList(node * headPtr)
{
node *tempPtr;
while(headPtr!=NULL)
{
tempPtr=headPtr;
headPtr = headPtr->nextPtr;
free(tempPtr);
}
}
//需要一个临时变量tempPtr
//操作:当head不为空时:temp记录头节点,头后移,释放temp
个性化操作:
通常包括遍历操作和顺序的调换
遍历模板
需要一个currentPtr和prePtr
node *currentPtr, *ptrPtr;
PrePtr=headPtr;
currentPtr=headPtr->nextPtr;
while(currentPtr!=NULL)
{
//内部操作
value=currentPtr->value;
//后移操作
ptrPtr=currentPtr;
currentPtr=currentPtr->nextPtr;
}
顺序调换
对于序列[a,b]和序列[c,d]交换位置
提前列好交换关系,其实就是对nextPtr指针的操作
一:节点a的前一个节点的nextPtr改指向为c
二:节点b的nextPtr改为指向d的下一个节点,即B_nextPtr=C_nextPtr
三:节点c的前一个节点的nextPtr改指向为a
四:节点d的nextPtr改为指向b的下一个节点,即C_nextPtr=B_nextPtr
注意四和二是交换,需要中间变量架桥
提供一种思路:先遍历找到操作涉及到的六个节点的指针,再进行交换操作
文件
文件分类:二进制文件和文本文件
文件操作流程:打开文件 --> 读或写文件 --> 关闭文件
打开和关闭
FILE * fPtr; //定义文件类型变量
if((fPtr = fopen("文件路径", "操作类型"))==NULL)
{
//如果成功打开,进行操作
}
else
{
//输出错误信息:文件没能打开
}
fclose(fPtr);
路径:相对:”filename.txt“ 绝对:”C:\\temp\\filename.txt“
操作类型:r--读 w--写 a--追加 二进制操作在后面加b,如"rb"
位置指针与文件操作
文件位置指针:指向下一次文件操作所在字节的整数值
以r,w方式打开文件时指向文件头,a方式打开时指向文件尾
三个函数
void rewind(FILE * stream);
//文件位置指针重新定位到文件起始位置处
void fseek(FILE * stream, long offset, int whence);
//移动文件位置指针到任意位置处
//offest:以参照点为起点移动的字节数(可负)
//whence: 参照点:0--头 1--当前 2--尾
long ftell(FILE * stream);
//返回文件位置指针的当前位置(相对文件头的位移量)
//返回-1表示调用错误
文件读写操作
单字符
int fputc(int c, FILE * stream);
//将c指定的字符写入stream指向的输出流中
//fputc(c, stdout)等价于putchar(c)
int fgetc(FILE * stream);
//指定流中读取字符
//ch = fgetc(stdin)等价于ch = getchar()
int feof(FILE * stream);
//判断是否到达文件尾
//文件尾EOF stdio.h将其定义为-1
//实例:文件复制(成功打开后)
//文件读写常用模板(读取顺序与固定操作流程)
ch = fgetc(source_Ptr); //先从源文件读取 ***顺序很重要,先读一个再进入循环
while(!feof(source_Ptr)){ //当前文件位置指针不是尾时
fputc(ch,dest_Ptr); //复制当前字符到目标文件
ch=fgetc(source_Ptr); //读取下一个字符
}
fclose(source_Ptr);
fclose(dest_Ptr);
读写字符串
int fputs(const char * s, FILE * stream);
//输出s字符串到流
//fputs(s, stdout)等价于puts(s)
char * fgets(char * s, int n, FILE * stream);
//从流中读取最多n-1个字符到s指向的字符串中
//n常写成sizeof(s)
//遇到"\n" EOF 或读满n-1个时停止读入
注意文件位置指针的存在,每次读取操作后,文件位置指针指向读取部分后的位置
键盘输入的EOF 用ctrl+z表示
格式化读写
int fscanf(FILE * stream,const char * format,输入变量首地址表);
//比scanf只多了一个FILE * 参数
int fprintf(FILE * stream,const char * format ,输出参量表);
//比printf只多了一个FILE * 参数
读写数据块(二进制)
因为二进制文件存储记录时每一个记录是等长的,所以可以用这两个函数读写
int fread(void *buffer,int size,int count,FILE * stream);
//将流文件中count个size大小的数据块写到buffer指向的位置
int fwrite(void *buffer,int size,int count,FILE * stream);
//将buffer指向的数组内容写到流中,count个size的大小的数据
注意点:
1.文件打开后一定要关闭,否则可能有一些数据留在缓冲区而没有写到预期的地方
2.理解文件位置指针,每一次读写操作都会改变它的位置,也会影响到下一次读写操作的结果
例如,想读取第n个记录时,可以打开后先读取n-1次,再读取一次即可得到第n条记录
有限状态自动机
什么是有限状态自动机?
是一种具有离散输入/输出系统的数学模型,简称 有限自动机。这一系统具有任意有限数量的内部“状态”。
状态:一个标识,能区分自动机在不同时刻的状况。有限状态系统具有任意有限数目的内部“状态” 自动机接受一定的输入,执行一定的动作,产生一定的结果。
自动机的本质:根据状态、输入和规则决定下一个状态 状态 + 输入(激励)+ 规则 ―> 状态迁移 可能的状态、运行的规则都是事先确定的。
一旦开始运行,就按照事先确定的规则工作,因此叫“自动机”。使用状态迁移描述整个工作过程。
应用有限自动机解题步骤
1、确定输入集
2、绘制状态迁移图(确定状态,在每一个状态下对输入进行分类,针对每一类输入,确定下一个状态)
3、确定状态转移函数(在某状态下,接收到某一字符后,自动机要执行的操作,以及迁移到的下一状态)
格式
定义状态,两层嵌套的switch进行状态的转化。
#include<stdio.h>
#define START 0
#define GET1 1
#define GET0 2
#define END 3
main()
{
char signal;
int vehicles,seconds,interval,longest;
int state;
state=START;
vehicles=0; seconds=0;longest=0;
printf("input signals,'#'to end:\n");
while(state!=END){
signal=getchar();
switch(state){
case START:/*若当前状态是初始状态,则只是简单的对时钟数或者车辆数加1*/
switch(signal){
case '1':
vehicles++;
state=GET1; /*状态迁移到1*/
break;
case '0':
seconds++;
state=GET0;
break;
case '#':
state=END;
break;
}
break;
case GET1:/*若当前状态是q1,即上一个信号是'1'*/
switch(signal){
case '1': /*当前读入信号是'1',则只是简单将车辆加1*/
vehicles++;
break;
case '0': /*前信号是'0',则要开始计数两个1之间的时钟间隔interval*/
seconds++;
interval=1;/*开始计数interval*/
state=GET0;
break;
case '#':
state=END;
break;
}
break;
case GET0: /*若当前状态是q2,即上一个信号是'0'*/
switch(signal){
case '0': /*当前读入信号是'0',则需要判断是否要将interval加1*/
seconds++;
if(interval>0)/*interval>0,表示状态是从状态1转移到状态2的,所以interval需要继续加1*/
interval++;
break;
case '1': /*当前读入信号是'1',需要判断是否要处理最长时间间隔*/
vehicles++;
if(interval>0)/*interval>0,表示状态是从状态q1转移到状态q2的,所以在状态迁移回q1之前需要处理最大时间间隔*/
if(interval>longest)
longest=interval;
interval=0;
state=GET1;
break;
case '#':
state=END;
break;
}
break;
}/*switch*/
}/*while*/
printf("%d vehicles passed in %d seconds\n",vehicles,seconds);
printf("the longest gap was %d seconds\n",longest);
system("PAUSE");
return 0;
}
再论函数和变量
变量的基本属性:变量名、变量类型、变量值
变量的其他属性:
- 作用域(scope,程序中可引用该变量的区域)
- 存储类别(storage class,变量存储在哪里)
- 存储期( storage duration,变量存活期)
- 连接性(linkage,可否被其它文件引用)
作用域:C语言中变量可以在三种位置定义:
- 函数内部的定义部分-->作用域对应函数作用域
- 函数内部的某一个复合语句内部-->块作用域
- 所有函数之外-->文件作用域