数据库

This commit is contained in:
yinkanglong_lab
2021-04-08 00:13:52 +08:00
parent 0d6737d404
commit 9e0739a93c
6 changed files with 72 additions and 25 deletions

View File

@@ -0,0 +1,3 @@
> https://blog.csdn.net/zh15732621679/article/details/80614091
### Redis使用场景
![](image/2021-04-07-23-46-25.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

View File

@@ -6,9 +6,20 @@
如果 {A1A2... An} 是关系的一个或多个属性的集合,该集合函数决定了关系的其它所有属性并且是最小的,那么该集合就称为**键码**。
对于 A-\>B如果能找到 A 的真子集 A',使得 A'-\> B那么 A-\>B 就是**部分函数依赖**,否则就是**完全函数依赖**。
### 部分函数依赖
* 设X,Y是关系R的两个属性集合存在X→Y若X是X的真子集存在X→Y则称Y部分函数依赖于X。
对于 A-\>BB-\>C则 A-\>C 是一个**传递函数依赖**。
* 举个例子学生基本信息表R中学号身份证号姓名当然学号属性取值是唯一的在R关系中学号身份证号->(姓名),(学号)->(姓名),(身份证号)->(姓名);所以姓名部分函数依赖与(学号,身份证号);
### 完全函数依赖
* 设X,Y是关系R的两个属性集合X是X的真子集存在X→Y但对每一个X都有X!→Y则称Y完全函数依赖于X。
* 例子学生基本信息表R学号班级姓名假设不同的班级学号有相同的班级内学号不能相同在R关系中学号班级->(姓名),但是(学号)->(姓名)不成立,(班级)->(姓名)不成立,所以姓名完全函数依赖与(学号,班级);
### 传递函数依赖
* 设X,Y,Z是关系R中互不相同的属性集合存在X→Y(Y !→X),Y→Z则称Z传递函数依赖于X。
* 例子在关系R(学号 ,宿舍, 费用)中,(学号)->(宿舍),宿舍!=学号,(宿舍)->(费用),费用!=宿舍,所以符合传递函数的要求;
## 2 异常
@@ -49,9 +60,7 @@
每个非主属性完全函数依赖于键码。
可以通过分解来满足。
<font size=4> **分解前** </font><br>
> **分解前**
| Sno | Sname | Sdept | Mname | Cname | Grade |
| :---: | :---: | :---: | :---: | :---: |:---:|
@@ -61,16 +70,15 @@
| 3 | 学生-3 | 学院-2 | 院长-2 | 课程-2 | 95 |
以上学生课程关系中,{Sno, Cname} 为键码,有如下函数依赖:
```
Sno -> Sname, Sdept
Sdept -> Mname
Sno, Cname-> Grade
```
* Grade 完全函数依赖于键码{Sno,Cname},它没有任何冗余数据,每个学生的每门课都有特定的成绩。
* Sname, Sdept 和 Mname 都部分依赖于键码{Sno,Cname},当一个学生选修了多门课时,这些数据就会出现多次,造成大量冗余数据。
- Sno -\> Sname, Sdept
- Sdept -\> Mname
- Sno, Cname-\> Grade
Grade 完全函数依赖于键码,它没有任何冗余数据,每个学生的每门课都有特定的成绩。
Sname, Sdept 和 Mname 都部分依赖于键码,当一个学生选修了多门课时,这些数据就会出现多次,造成大量冗余数据。
<font size=4> **分解后** </font><br>
> **分解后**
关系-1
@@ -103,10 +111,11 @@ Sname, Sdept 和 Mname 都部分依赖于键码,当一个学生选修了多门
非主属性不传递函数依赖于键码。
上面的 关系-1 中存在以下传递函数依赖:
```
Sno -> Sdept -> Mname
```
- Sno -\> Sdept -\> Mname
可以进行以下分解:
> 可以进行以下分解:
关系-11

View File

@@ -2,6 +2,9 @@
> 包含集合和映射。即unordered_set和unordreed_map两种数据结构的实现方式。
> 参考文献
> * [https://www.cnblogs.com/gongcheng-/p/10894205.html#_label1_0](https://www.cnblogs.com/gongcheng-/p/10894205.html#_label1_0)
## 1 哈希表简介
### 哈希表的概念
@@ -22,7 +25,7 @@
1. 当我们插入一个新的键时,哈希函数将决定该键应该分配到哪个桶中,并将该键存储在相应的桶中;
2. 当我们想要搜索一个键时,哈希表将使用相同的哈希函数来查找对应的桶,并只在特定的桶中进行搜索。
### 哈希函数示例
### 哈希函数
![](image/2021-03-13-12-51-08.png)
@@ -33,7 +36,10 @@
- 如果我们搜索 1987我们将使用相同的哈希函数将 1987 映射到 2。因此我们在桶 2 中搜索,我们在那个桶中成功找到了 1987。
- 例如,如果我们搜索 23将映射 23 到 3并在桶 3 中搜索。我们发现 23 不在桶 3 中,这意味着 23 不在哈希表中。
### 哈希表的关键——哈希函数
### 常用hash函数
*
## 2.1 哈希表的关键——哈希函数
* 哈希函数是哈希表中最重要的组件,该哈希表用于将键映射到特定的桶。在上一节的示例中,我们使用 `y = x % 5` 作为散列函数,其中 `x` 是键值,`y` 是分配的桶的索引。
@@ -44,21 +50,50 @@
* 哈希函数的设计是一个开放的问题。其思想是尽可能将键分配到桶中,理想情况下,完美的哈希函数将是键和桶之间的一对一映射。然而,在大多数情况下,哈希函数并不完美,它需要在桶的数量和桶的容量之间进行权衡。
### 哈希表的关键——冲突解决
## 2.2 哈希表的关键——冲突解决
### 问题分析
* 理想情况下如果我们的哈希函数是完美的一对一映射我们将不需要处理冲突。不幸的是在大多数情况下冲突几乎是不可避免的。例如在我们之前的哈希函数_y = x 5_1987 和 2 都分配给了桶 2这是一个`冲突`
* 冲突解决算法应该解决以下几个问题:
1. 如何组织在同一个桶中的值?
2. 如果为同一个桶分配了太多的值,该怎么办?
3. 如何在特定的桶中搜索目标值?
* 根据我们的哈希函数,这些问题与`桶的容量`和可能映射到`同一个桶``键的数目`有关。
* 让我们假设存储最大键数的桶有 `N` 个键。如果 N是常数且很小我们可以简单地使用一个数组将键存储在同一个桶中。如果 N 是可变的或很大,我们可能需要使用`高度平衡的二叉树`来代替。
### 开放寻址法open addressing
## 哈希表的实现
* 这种方法也称再散列法其基本思想是当关键字key的哈希地址p=Hkey出现冲突时以p为基础产生另一个哈希地址p1如果p1仍然冲突再以p为基础产生另一个哈希地址p2直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。这种方法有一个通用的再散列函数形式:
```
Hi=Hkey+di% m i=12n
```
* 其中Hkey为哈希函数m 为表长di称为增量序列。增量序列的取值方式不同相应的再散列方式也不同。
### 链接法
![](image/2021-04-07-23-38-56.png)
* 把散列到同一槽中的所有元素都存放在一个链表中。每个槽中有一个指针指向所有散列到该槽的元素构成的链表的头。如果不存在这样的元素则指针为空。如果链接法使用的是双向链表那么删除操作的最坏情况运行时间与插入操作相同都为O(1),而平均情况下一次成功的查找需要Θ(1+α)时间。α是装填因子。
* 将散列表定义为一个由m个头指针组成的指针数 组T[0..m-1]。凡是散列地址为i的结点均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。在拉链法中装填因子α可以大于 1但一般均取α≤1。
* 优点处理冲突简单且无堆积现象即非同义词决不会发生冲突因此平均查找长度较短由于拉链法中各链表上的结点空间是动态申请的故它更适合于造表前无法确定表长的情况开放定址法为减少冲突要求装填因子α较小故当结点规模较大时会浪费很多空间。而拉链法中可取α≥1且结点较大时拉链法中增加的指针域可忽略不计因此节省空间删除结点的操作易于实现。只要简单地删去链表上相应的结点即可
* 缺点:指针需要额外的空间,故当结点规模较小时,开放定址法较为节省空间,而若将节省的指
### 优缺点
* 开放散列表open hashing/拉链法(针对桶链结构)
1. 优点:①对于记录总数频繁可变的情况,处理的比较好(也就是避免了动态调整的开销) ②由于记录存储在结点中而结点是动态分配不会造成内存的浪费所以尤其适合那种记录本身尺寸size很大的情况因为此时指针的开销可以忽略不计了 ③删除记录时,比较方便,直接通过指针操作即可
2. 缺点: ①存储的记录是随机分布在内存中的,这样在查询记录时,相比结构紧凑的数据类型(比如数组),哈希表的跳转访问会带来额外的时间开销 ②如果所有的 key-value 对是可以提前预知并之后不会发生变化时即不允许插入和删除可以人为创建一个不会产生冲突的完美哈希函数perfect hash function此时封闭散列的性能将远高于开放散列 ③由于使用指针记录不容易进行序列化serialize操作
* 封闭散列closed hashing/ 开放定址法
1. 优点: ①记录更容易进行序列化serialize操作 ②如果记录总数可以预知,可以创建完美哈希函数,此时处理数据的效率是非常高的
2. 缺点:
1. 存储记录的数目不能超过桶数组的长度,如果超过就需要扩容,而扩容会导致某次操作的时间成本飙升,这在实时或者交互式应用中可能会是一个严重的缺陷
2. 使用探测序列,有可能其计算的时间成本过高,导致哈希表的处理性能降低 ③由于记录是存放在桶数组中的而桶数组必然存在空槽所以当记录本身尺寸size很大并且记录总数规模很大时空槽占用的空间会导致明显的内存浪费
3. 删除记录时比较麻烦。比如需要删除记录a记录b是在a之后插入桶数组的但是和记录a有冲突是通过探测序列再次跳转找到的地址所以如果直接删除aa的位置变为空槽而空槽是查询记录失败的终止条件这样会导致记录b在a的位置重新插入数据前不可见所以不能直接删除a而是设置删除标记。这就需要额外的空间和操作。
## 3 哈希表的实现
### 自己设计哈希函数

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB