第一章
NANO
第二章
NANO
第三章
第三章是数据结构的基础,主要介绍常见数据类型:整型、浮点型、字符型、结构体、数组、链表和串等。
例题3.2-3.5
测试代码
测试系统中随机数产生器,针对r=10,100和1000,N=103,104,105和106。利用rand()产生0—r-1的随机数,并计算样本的平均值和标准方差。3.3输出整型,3.4改变r,3.5将随机数改为随机位。
#include<stdlib.h>
#include"stdio.h"
#include<math.h>
#include"randnum.h"
int main (int argc,char *argv[])
{
float miu = 0.0f;
float rou = 0.0f;
unsigned int count = 0u;
unsigned int N = 0u;
unsigned int scale = 10u;
CalRand_Structure temp_Data ;
randnum_Type_Flag temp_Flag;
temp_Flag = TYPE_INT;
RANDNUM(temp_Flag,scale,&temp_Data,10);
return 0;
}
#ifndef _RANDNUM_H
#define _RANDNUM_H
#include <math.h>
#include <stdlib.h>
typedef union
{
int randnum_INT;
float randnum_FLOAT;
}INT2FLOAT;
typedef INT2FLOAT randnumber;
typedef struct
{
randnumber miu;
randnumber rou;
}CalRand_Structure;
typedef enum
{
TYPE_INT = 0x00,
TYPE_FLOAT = 0x01
}randnum_Type_Flag;
void RANDNUM(
randnum_Type_Flag type_Flag,
int Scale,
CalRand_Structure * const Cal_RandBuff,
int Sample
);
void RANDNUM(
randnum_Type_Flag type_Flag,
int Scale,
CalRand_Structure * const Cal_RandBuff,
int Sample
)
{
randnumber temp_RandBuff;
int Sample_Count ;
for(Sample_Count = 0,Cal_RandBuff->miu.randnum_INT = 0,Cal_RandBuff->rou.randnum_INT = 0;Sample_Count < Sample;Sample_Count++)
{
temp_RandBuff.randnum_FLOAT =((float)(rand())/RAND_MAX)*Scale;
Cal_RandBuff->miu.randnum_FLOAT += temp_RandBuff.randnum_FLOAT/Sample;
Cal_RandBuff->rou.randnum_FLOAT += (temp_RandBuff.randnum_FLOAT)*(temp_RandBuff.randnum_FLOAT)/Sample;
}
if(type_Flag == TYPE_INT )
{
printf("Rand Numbers 's average = %d \n ",(int)(Cal_RandBuff->miu.randnum_FLOAT));
printf("Std. debiation = %d \n",(int)(sqrt((Cal_RandBuff->rou.randnum_FLOAT-(Cal_RandBuff->miu.randnum_FLOAT*Cal_RandBuff->miu.randnum_FLOAT)))));
}
else if(type_Flag == TYPE_FLOAT)
{
printf("Rand Numbers 's average = %1f \n ",Cal_RandBuff->miu.randnum_FLOAT);
printf("Std. debiation = %1f \n",sqrt((Cal_RandBuff->rou.randnum_FLOAT-(Cal_RandBuff->miu.randnum_FLOAT*Cal_RandBuff->miu.randnum_FLOAT))));
}
}
#endif
题3.1-3.5都是基于程序3.2进行修改,其中需要注意的是如何将随机数限制到规定的范围,是依靠语句((float)(rand())/RAND_MAX)*Scale
完成。RAND_MAX在头文件"stdlib.h"中定义,定义为2147483647,实际上是int 型的最大值。Scale是自行定义的目标范围。
例题3.7-3.10
测试代码
编写客户程序,可以在输入的一系列坐标点坐标点中找到距离第一个点最近的点;基于此完成三点是否共线的判断;最后完成任意三点之间的三角形面积求解。
#ifndef _DISTANCE_H
#define _DISTANCE_H
#include "math.h"
#include "stdlib.h"
typedef enum
{
Colinear_Flag = 0x00,
Triangularity_Flag = 0x01
}Point_Status;
typedef struct
{
float x;
float y;
}distance_Structure;
typedef distance_Structure Point_Adr;
typedef float Point_Distance;
Point_Distance Distance_Cal(
const Point_Adr * Point_1_Adr_G,
const Point_Adr * Point_2_Adr_G
);
Point_Status Cal_Points_Status(
Point_Adr * const Point_A_Infos,
Point_Adr * const Point_B_Infos,
Point_Adr * const Point_C_Infos
);
/******************Distance_Cal********
*该函数用于计算两点之间的距离
*
*
*
*********************************************/
Point_Distance Distance_Cal(
const Point_Adr * Point_1_Adr_G,
const Point_Adr * Point_2_Adr_G
)
{
float dx,dy;
dx = (float)(Point_2_Adr_G->x-Point_1_Adr_G->x);
dy = (float)(Point_2_Adr_G->y-Point_1_Adr_G->y);
return (Point_Distance)(sqrt(dx*dx+dy*dy));
}
Point_Adr Elect_ShorterDistance(
const Point_Adr * Point_Info,
const unsigned int PointNum
)
{
Point_Adr Origin_Point_L = *Point_Info ;
Point_Distance Point_Distance_Min ;
Point_Distance Point_Distance_Temp;
unsigned int PointNum_Count;
unsigned int PointNum_Buff;
Point_Distance_Min = Distance_Cal(&Origin_Point_L,&Point_Info[1]);
printf("The distances of point 1 between point2 is %1f ! \n ",Point_Distance_Min);
PointNum_Buff = 1 ;
for(PointNum_Count = 2 ;PointNum_Count < PointNum ; PointNum_Count++)
{
Point_Distance_Temp = Distance_Cal(&Origin_Point_L,&Point_Info[PointNum_Count]);
if( Point_Distance_Min > Point_Distance_Temp )
{
Point_Distance_Min = Point_Distance_Temp;
PointNum_Buff = PointNum_Count;
}
else
{
}
}
printf("The Point %d is closest The Origin point !,The distances is %1f ! \n ",PointNum_Buff,Point_Distance_Min);
return Point_Info[PointNum_Buff];
}
/******************Cal_Points_Status********
*该函数用于判断输入三点之间的关系,精度在0.0001f
*其中使用坐标点类型结构体进行传递,但是计算方法有点麻烦,是否可以简化?
*
*
*********************************************/
Point_Status Cal_Points_Status(
Point_Adr * const Point_A_Infos,
Point_Adr * const Point_B_Infos,
Point_Adr * const Point_C_Infos
)
{
float A_B_Distance;
float A_C_Distance;
float B_C_Distance;
A_B_Distance = Distance_Cal(Point_A_Infos,Point_B_Infos);
A_C_Distance = Distance_Cal(Point_A_Infos,Point_C_Infos);
B_C_Distance = Distance_Cal(Point_B_Infos,Point_C_Infos);
//printf("A_B distance = %1f \n",A_B_Distance);
//printf("A_C distance = %1f \n",A_C_Distance);
//printf("B_C distance = %1f \n",B_C_Distance);
if(A_B_Distance > A_C_Distance)
{
if(A_C_Distance >= B_C_Distance )
{
if(A_B_Distance*A_B_Distance - (A_C_Distance + B_C_Distance)*(A_C_Distance + B_C_Distance) < 0.0001f )
{
return Colinear_Flag;
}
else
{
return Triangularity_Flag;
}
}
else
{
if(A_B_Distance > B_C_Distance)
{
if(A_B_Distance*A_B_Distance - (A_C_Distance + B_C_Distance)*(A_C_Distance + B_C_Distance) < 0.0001f )
{
return Colinear_Flag;
}
else
{
return Triangularity_Flag;
}
}
else
{
if(B_C_Distance*B_C_Distance - (A_C_Distance +A_B_Distance)*(A_C_Distance + A_B_Distance) < 0.0001f )
{
return Colinear_Flag;
}
else
{
return Triangularity_Flag;
}
}
}
}
}
/******************Triangler_Area_Cal********
*该函数用于计算3点之间的三角形面积,但是没有添加共线判断,使用时可以先进行判断。
*其中使用坐标点类型结构体进行传递,计算方法使用的是海伦公式
*
*
*********************************************/
Triangle_area Triangler_Area_Cal(
Point_Adr * const Point_A_Infos,
Point_Adr * const Point_B_Infos,
Point_Adr * const Point_C_Infos
)
{
float girth;
float distance_A_B,distance_A_C,distance_B_C;
distance_A_B = Distance_Cal(
Point_A_Infos,
Point_B_Infos
);
distance_A_C = Distance_Cal(
Point_A_Infos,
Point_C_Infos
);
distance_B_C = Distance_Cal(
Point_C_Infos,
Point_B_Infos
);
girth = (float)(distance_A_B + distance_A_C + distance_B_C)/2 ;
//printf("A_B = %1f \n",distance_A_B);
//printf("A_C = %1f \n",distance_A_C);
//printf("B_C = %1f \n",distance_B_C);
//printf("girth = %1f \n",girth);
return (float)(sqrt(girth*(girth-distance_A_B)*(girth - distance_A_C)*(girth -distance_B_C)));
}
例题3.34-3.37
前言
数组与链表有相似之处也有不同的地方,数组与链表都可以以任意类型结构为单元进行整合,但是数组的内存是连续的,加上类型的一致性,只需要通过起始地址以及访问的序号就可以迅速的访问其中的成员,A[n]就等价于*(Adr + (sizeof(Adr[1])*n))
。虽然数组访问速度十分快,但是带来了相应的矛盾,那就是成员的灵活性,数组并不支持成员的删减和插入。为此增加了一个新的结构:链表,链表并不强调成员的稳定性,这是由于他的成员的内存多是通过函数mollac进行动态分配的,所以他可以任意的删减(free)或插入,但是作为条件则是牺牲节点访问的速度。
为此,埃拉托色尼筛选法成为了数组结构的经典例子,而链表则是约瑟夫问题。
测试代码
要求将编写一个函数完成将给定链表的最大数据项移到该表中的最后一个节点;将最小数据项移到该表的第一个节点;实现给定链接t和u指向的节点互换位置。
#include "stdio.h"
#include "XHH_Function.h"
#include "stdlib.h"
#include "time.h"
typedef struct node* link ;
typedef unsigned int LINK_Length;
typedef struct { int Node_Num; }Item;
struct node {link next; Item Content; };
link LINK_FindMax(
link Target_Link
);
link LINK_FindMin(
link Target_Link
);
void Link_Trans_Fun(
link t,
link u
);
/**********************LINK_Initial_FUN********************
*
*describe: this function will be product the list that the last link point to NULL
*由于该函数是传址函数,虽然使用了参数代替传递的参数名,但是本质上对原链表进行了修改,故即使不返回参数都可以通过调用传进来的参数进行访问
*这个问题在后面寻找最小值函数的返回中可以体现,从打印情况来看,最小值被寻找到,但是其他的节点排序和本函数返回的一致!
*
*
*********************************************************/
link LINK_Initial_FUN(
LINK_Length Length,
Operation_Enum Operation
)
{
link Link_Head = malloc(sizeof *Link_Head);
link Link_Operation = Link_Head;
LINK_Length Length_Count;
Link_Operation->Content.Node_Num = rand();
srand(time(NULL));
// link Link_Head = malloc(sizeof * Link_Head) ;
// Link_Head->Content.Node_Num = 1;
for(Length_Count = 0 ; Length_Count < Length ; Length_Count ++ )
{
Link_Operation = (Link_Operation->next = malloc(sizeof *Link_Operation));
// printf("%d \n ",Link_Operation->Content.Node_Num);
if(Operation == Opera_Rand) Link_Operation->Content.Node_Num = rand(); //根据操作参数决定该链表节点的数据项信息是否有序
else Link_Operation->Content.Node_Num = Length_Count;
}
Link_Operation->next =NULL;
return Link_Head;
}
/*********************************************************
*备注:由于该函数是传址函数,虽然使用了参数代替传递的参数名,但是本质上对原链表进行了修改,故即使不返回参数都可以通过调用传进来的参数进行访问
* 这个问题在后面寻找最小值函数的返回中可以体现,从打印情况来看,最小值被寻找到,但是其他的节点排序和本函数返回的一致!
*记录:初始化函数使用的是单向链表,拥有头指针且尾部指向NULL,这样的链表结构注定遍历的时候,需要格外注意,遍历条件如果是判断当前是否为NULL,那么
* 不能在循环中对节点的成员进行操作,否则在最后会出现非法访问错误。如何对所有节点的成员都进行遍历,目前的方法是在循环体内添加判断,若为NULL
* 则不进行操作直接返回。
*
*
*************************************************************/
link LINK_FindMax(
link Target_Link
)
{
link Link_temp = Target_Link , Link_Last , Link_Max = Target_Link,Link_Output ;
int MAX_Data = Link_temp->Content.Node_Num ;
while( (Link_temp != NULL) && (Link_temp->next != NULL) )
{
if(Link_temp->next->Content.Node_Num > MAX_Data)
{
MAX_Data = Link_temp->next->Content.Node_Num ;
// printf("Find the max data = %d \n",MAX_Data);
Link_Last = Link_temp;
Link_Max = Link_temp->next;
// printf(" Max = %d \n",Link_Max->next->Content.Node_Num);
}
Link_temp = Link_temp->next;
}
// printf("Max data = %d \n",Link_Max->Content.Node_Num);
//if(Link_temp->next == NULL)
// printf("temp data = %d \n ",Link_temp->Content.Node_Num);
if(Link_Max == Target_Link )
{
Target_Link = Link_Max->next;
}
if(Link_Max->next != NULL)
{
Link_Last->next = Link_Max->next;
}
Link_temp->next = Link_Max;
Link_Max->next = NULL;
//printf("Last Data = %d ,Max Data = %d \n ", Link_Last->Content.Node_Num,Link_Max->Content.Node_Num);
return Target_Link;
}
link LINK_FindMin(
link Target_Link
)
{
link Link_temp = Target_Link , Link_Last , Link_Min = Target_Link,Link_Output ;
int MIN_Data = Link_temp->Content.Node_Num ;
while( (Link_temp != NULL) && (Link_temp->next != NULL) )
{
if(Link_temp->next->Content.Node_Num < MIN_Data)
{
MIN_Data = Link_temp->next->Content.Node_Num ;
// printf("Find the min data = %d \n",MAX_Data);
Link_Last = Link_temp;
Link_Min = Link_temp->next;
// printf(" Max = %d \n",Link_Max->next->Content.Node_Num);
}
Link_temp = Link_temp->next;
}
// printf("Min data = %d \n",Link_Min->Content.Node_Num);
// printf(" data = %d \n ",Link_Max->next->Content.Node_Num);
if(Link_Min == Target_Link)
{
printf("Min Data = Target_Link \n"); ;//表头就是最小数据项
}
else
{
Link_Last->next = Link_Min->next;
// if(Link_Last->next == NULL)
// printf(" NULL \n ");
Link_Min->next = Target_Link;
}
// printf("Last Data = %d ,Min Data = %d \n ", Link_Last->Content.Node_Num,Link_Min->Content.Node_Num);
return Link_Min;
}
void Link_Trans_Fun(
link t,
link u
)
{
// printf("Link_H Address = %p \n",(t->next));
// printf("Link_temp Address = %p \n",(u->next));
t->next =(link)((long)(t->next) ^(long)(u->next));
u->next =(link)((long)(t->next) ^(long)(u->next));
t->next =(link)((long)(u->next) ^(long)(t->next));
//printf("Link_H Address = %p \n",(t->next));
//printf("Link_temp Address = %p \n",(u->next));
}
值得注意的是,这里提供的链表初始化函数,使用的是头指针且尾节点为空的单向链表结构,这样的结构可以确保遍历时不会发生溢出错误,但是由于不同的操作条件会影响到溢出的判断条件。
而例3.37要求互换节点位置,实际上是对地址进行互换,地址也是数据,可以通过异或操作来节省中间变量的开销,但是异或是无法对相同数据进行互换操作的,虽然不同的链表,地址不会相同,但是这也是潜在的bug,可以通过判断参数是否相等来避免。
第四章
第四章是基于第三章的基础数据结构封装而成的ADT(abstract data type)进行分析。ADT是一个比较重要的工具,在简单的代码中数据结构是透明的,这有助于代码的简化,但是若在复杂的功能中,代码往往是模块化的,调用者就是被调用者的客户,被调用者就是接口。引入客户、接口的概念就是为了将功能模块化,采用不透明的数据结构进行传输,这样一来可以降低模块之间的耦合性,例如当一个结构体的成员需要改变名称时,若采用透明的数据结构传输,那么所有的代码包括客户代码都需要改变,这对于大型功能来说,工作量庞大且没有必要;若使用非透明的数据结构传输,那么只需要修改接口函数,而客户函数不需要修改。
例题4.8
给定两个序列,给出算法用来判断是否可以在序列中添加星号,使得由第一个序列生成第二个序列。栈操作序列的含义由练习4.7来解释。
#ifndef __STACK_T_H
#define __STACK_T_H
#include "stdio.h"
#include "stdlib.h"
typedef int ItemP;
typedef int Item;
#define Char_end '\n'
typedef enum
{
FUNC_OK ,
FUNC_ERROR,
FUNC_BUSY,
FUNC_LOCK
}general_status;
Item* STACKinit(int length);
Item STACKpop(void);
void STACKpush(Item data);
int STACKempty(void);
void STACK_inout(void);
general_status STACK_Test(Item* sequence,Item* input);
void STACKprint(void);
void STACK_cal(char *charater_seq);
#endif
#include "stack_T.h"
Item* STACK_BUFF;//栈指针
ItemP STACK_P=0;//栈深度记录
Item* STACKinit(int length)//动态获取栈
{
return malloc(sizeof(Item)*length);
}
int STACKempty(void)
{
return 0;
}
void STACKpush(Item data)//入栈
{
if(STACK_P<20)
STACK_BUFF[STACK_P++] = data;
}
Item STACKpop(void)
{
Item temp;
if(STACK_P)
{
// printf("STACK_P = %d ",STACK_P);
temp = STACK_BUFF[STACK_P-1];
STACK_BUFF[STACK_P--] = NULL;
return temp;
}
else
return NULL;
}
void STACKprint(void)
{
int point_t = 0;
printf("STACK_BUFF info => |");
while(STACK_BUFF[point_t])
{
printf(" %d |",STACK_BUFF[point_t++]);
}
printf("\n");
}
//比较origin_seq与target_seq之间的差别,是否可以通过出栈入栈的操作实现输出
general_status STACK_Test(Item *origin_seq,Item* target_seq)
{
int point =0;
int point_send,point_s = 0;
int cnt = 0;
int resolution[10];
// printf("size of sequence is %d \n",length);|| ((point--)&&(origin_seq[point]==target_seq))
while( (origin_seq[point] != '-') )
{
// printf("input\n");
STACKpush(origin_seq[point++] );
// STACKprint();
cnt++;
while((STACK_BUFF[STACK_P-1] == *target_seq)&&STACK_P)
{
resolution[point_s++] =cnt ;
// printf("%d", resolution[point_s-1]);
// printf("STACK_BUFF[%d] = %c ,target_seq = %c\n",STACK_P-1,STACK_BUFF[STACK_P-1],*target_seq);
STACKpop();
(target_seq)++;
}
}
while(( *target_seq != '-')&&( *target_seq != NULL) )
{
cnt++;
if(STACK_BUFF[STACK_P-1] == *target_seq)
{
resolution[point_s++] =cnt ;
// printf("%d", resolution[point_s-1]);
// printf("STACK_BUFF[%d] = %c ,target_seq = %c\n",STACK_P-1,STACK_BUFF[STACK_P-1],*target_seq);
STACKpop();
}
(target_seq)++;
}
// printf("origin_seq[point] = %c ,target_seq[point] = %c\n",origin_seq[point],target_seq[point]);
// printf("STACK_P = %d\n",STACK_P);
if(STACK_P == 0)
{
printf("\nOK\n");
point = 0;
point_send = point_s;
point_s = 0;
while(origin_seq[point] != '-')
{
printf("%c",origin_seq[point] ) ;
while(resolution[point_s] ==(point+1) )
{
point_s++;
printf("*");
} point++;
}
while(point_s++<point_send) printf("*");
return FUNC_OK;}
else {printf("\nERROR\n"); return FUNC_ERROR;}
}
//字符流获取,单个字节获取,当回车时认定结束。*为出栈操作,其余的将会入栈。
void STACK_inout(void)
{
Item data_temp;
printf("please input the character!\n");
while((data_temp=getchar())!='\n')
{
if(data_temp=='*')
{
//printf("*");
printf("%c ",STACKpop());
}
else
{
// printf("%c",data_temp);
STACKpush((Item)data_temp);
}
}
printf("\n end \n ");
}
#include "stdio.h"
#include "stdlib.h"
#include "stack_T.h"
extern Item* STACK_BUFF;
Item data_temp;
//char * test;
int main(int argc,char **argv)
{
STACK_BUFF = STACKinit(20);
char input_t[10]={'-'};
printf("please input your test charater!\n");
scanf("%s",input_t);
STACK_Test("EASY-",input_t);
//STACK_Test("EASY-","AYES-");
// printf("input:%s",input_t);
//STACK_inout();
return 0;
}
例题4.8是通过出栈、入栈的操作来判断目标序列是否可以实现。目前并不能理清序列之间的映射关系,只能通过代码进行操作来判断。但该例题并不是主要考察映射关系,而是希望理解ADT的应用,这里实际上还没有完全隔离开,因为客户通过访问指针可以访问到栈内容。而STACK_Test函数的实现方式还可以再优化。
程序4.2-4.3
通过例题4.8的基础,可以实现波兰表示法,即输入序列:598+46**7+来实现5(((9+8)(46))+7)
void STACK_cal(char *charater_seq)
{
while(*charater_seq != NULL)
{
switch((char)(*charater_seq))
{
case '+':
STACKprint();
STACKpush((STACKpop()+STACKpop()));
break;
case '-':
STACKpush((STACKpop()-STACKpop()));
break;
break;
case '*':
STACKprint();
STACKpush((STACKpop()*STACKpop()));
break;
break;
case '/':
STACKpush((STACKpop()/STACKpop()));
break;
break;
default:
STACKpush((int)((*charater_seq)-'0'));
printf("%d\n",(int)((*charater_seq)-'0'));
break;
}
*charater_seq++;
}
printf("result = %d \n",(int)(STACKpop()));
}
缺陷:虽然实现了+、-、*,但是对于结果为小数的情况并不适用,可以通过修改定义typedef float Item;
但需要改动的地方比较大。
//未完