数据结构
二叉树的定义
数据库
三个范式 link
第一范式(1NF)
1NF是对属性的原子性,要求属性具有原子性,不可再分解;
表:字段1、 字段2(字段2.1、字段2.2)、字段3 ……
如学生(学号,姓名,性别,出生年月日),如果认为最后一列还可以再分成(出生年,出生月,出生日),它就不是一范式了,否则就是.
第二范式(2NF)
2NF是对记录的惟一性,要求记录有惟一标识,即实体的惟一性,即不存在部分依赖;
非主属性完全依赖于主关键字
表:学号、课程号、姓名、学分;
这个表明显说明了两个事务:学生信息, 课程信息;由于非主键字段必须依赖主键,这里学分依赖课程号,姓名依赖与学号,所以不符合二范式。
正确做法:
学生:Student(学号, 姓名);
课程:Course(课程号, 学分);
选课关系:StudentCourse(学号, 课程号, 成绩)。
不满足第二范式可能存在的问题:
数据冗余:,每条记录都含有相同信息;删除异常:删除所有学生成绩,就把课程信息全删除了;插入异常:学生未选课,无法记录进数据库;更新异常:调整课程学分,所有行都调整。
第三范式(3NF)
3NF是对字段的冗余性,要求任何字段不能由其他字段派生出来,它要求字段没有冗余,即不存在传递依赖;
表: 学号, 姓名, 年龄, 学院名称, 学院电话
因为存在依赖传递: (学号) → (学生)→(所在学院) → (学院电话)
可能会存在问题:
数据冗余:有重复值;更新异常:有重复的冗余信息,修改时需要同时修改多条记录,否则会出现数据不一致的情况 .
正确做法:
学生:(学号, 姓名, 年龄, 所在学院)
学院:(学院, 电话)
范式化设计和反范式化设计的优缺点
范式化
优点: 可以尽量减小数据冗余, 更新操作快,表体积更小
缺点: 查询需要对多个表进行关联,导致性能降低; 更难进行索引优化
反范式化
优点: 减少表的关联; 更好的进行索引优化
缺点: 存在数据库冗余及数据维护异常; 对数据的修改需要更多成本
C语言
文件操作
FILE *fopen(const char *filename, const char *mode);
fprintf函数 是否会覆盖所有,取决于fopen时是采用什么方式
1 | fopen(file , "r") ; 读方式打开,写会报错! |
int fclose(FILE *fp)
- 若成功执行了关闭操作,放回值为0; 非0,表示关闭时有错误
int fgetc(FILE *fp);
从分配读一个字符,将位置指针指向下一个字符
若读成功,则返回该字符
若读到文件尾或者读取错误,则返回EOF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main() {
FILE *fp;
int ch;
if ((fp = fopen("demo.txt", "r")) == NULL) {
printf("Failure to open demo.txt! \n");
exit(0);
}
while ((ch = fgetc(fp)) != EOF) {
putchar(ch);
}
fclose(fp);
return 0;
}
int fputc(int c, FILE *fp);
- 向fp输出字符c
- 若写入错误,则返回EOF,否则返回c
feof()
- 函数 feof() 检查是否能达到文件尾,返回非0值,否则返回0值
按块读写数据
size_t fread(void *buffer, size_t size, size_t count, FILE *stream)
从fp所指文件中读取数据块存到 buffer 指向的内存
buffer 是存储数据块的内存首地址,如数组的地址
1
num = fread(a, sizeof char, n, fp); //n个块,每个块占1字节
size_t fwrite(const void *buffer, size_t size, size_t count, FILE *stream)
- 将buffer指向的内存中的数据块写入fp所指的文件
- 返回值:实际写入数据块个数,应等于count,除非出现写入错误
位运算
1、补码
在总结按位运算前,有必要先介绍下补码的知识,我们知道当将一个十进制正整数转换为二进制数的时候,只需要通过除2取余的方法即可,但是怎么将一个十进制的负整数转换为二进制数呢?其实,负数是以补码的形式表示:先按正数转换,然后取反加1。
1 | 要将十进制的-10用二进制表示,先将10用二进制表示: |
2、按位与(&)
参加运算的两个数,换算为二进制(0、1)后,进行与运算。只有当相应位上的数都是1时,该位才取1,否则该位为0。
1 | 将10与-10进行按位与(&)运算: |
3、按位或(|)
参加运算的两个数,换算为二进制(0、1)后,进行或运算。只要相应位上存在1,那么该位就取1,均不为1,即为0。
1 | 将10与-10进行按位或(|)运算: |
4、按位异或(^)
参加运算的两个数,换算为二进制(0、1)后,进行异或运算。只有当相应位上的数字不相同时,该为才取1,若相同,即为0。
1 | 将10与-10进行按位异或(^)运算: |
可以看出,任何数与0异或,结果都是其本身。利用异或还可以实现一个很好的交换算法,用于交换两个数,算法如下:
1 | a = a ^ b; |
5、取反(~)
参加运算的两个数,换算为二进制(0、1)后,进行取反运算。每个位上都取相反值,1变成0,0变成1。
1 | 对10进行取反(~)运算: |
转义字符
1 | 所有的ASCII码都可以用“\”加数字(一般是8进制数字)来表示。而C中定义了一些字母前加"\"来表示常见的那些不能显示的ASCII字符,如\0,\t,\n等,就称为转义字符,因为后面的字符,都不是它本来的ASCII字符意思了。 |
static
注意: 静态局部变量,在函数里定义,就只能在这个函数里使用,同一个文档中的其他函数也是用不了的。
1 |
|