1
0
mirror of https://github.com/Didnelpsun/CS408.git synced 2026-11-03 22:03:48 +08:00

更新排序

This commit is contained in:
Didnelpsun
2021-09-29 23:34:33 +08:00
parent 03eec49ec2
commit 7b91890693
4 changed files with 137 additions and 18 deletions

View File

@@ -0,0 +1,17 @@
# 排序习题
## 基本概念
**例题** 对任意$7$个关键字进行基于比较的排序,至少要进行()次关键字之间的两两比较。
$A.13$
$B.14$
$C.15$
$D.6$
解:$A$。对于任意序列进行基于比较的排序,求最少的比较次数应考虑最坏情况。对任意$n$个关键字排序的比较次数至少$\lceil\log_2(n!)\rceil$。将$n=7$代入公式,答案为$13$。上述公式证明如下(仅供有兴趣的同学参考):在基于比较的排序方法中,每次比较两个关键字后,仅出现两种可能的转移。假设整个排序过程至少需要做$t$次比较,则显然会有$2^t$种情况。由于$n$个记录共有$n!$种不同的排列,因而必须有$n!$种不同的比较路径,于是有$2^t\geqslant n!$,即$t\geqslant\log_2(n!)$。考虑到$t$为整数,故为$\lceil\log_2(n!)\rceil$。
## 插入排序

View File

@@ -5,7 +5,7 @@
+ 排序:将一个数据元素的任意序列重新排列成一个按关键字有序的序列。
+ 内部排序:待排序的记录存放在计算机的内存中所进行的排序操作称为内部排序。
+ 外部排序:待排序的记录数量很大,以致内存一次不能容纳全部记录,在排序过程中需要访问外存的排序过程称为外部排序。
+ 稳定的排序:比如一个序列是“1,4,3,3*,2”,按从小到大排序后变成“1,2,3,3*,4”,就叫做稳定排序,即3和3*相对顺序不变。如果相同关键字的顺序发生了改变,则是不稳定的排序。稳定性的需要看具体的应用场景。
+ 稳定的排序:比如一个序列是“$1,4,3,3*,2$”,按从小到大排序后变成“$1,2,3,3*,4$”,就叫做稳定排序,即3和3*相对顺序不变。如果相同关键字的顺序发生了改变,则是不稳定的排序。稳定性的需要看具体的应用场景。
+ 内部排序的算法性能取决于算法的时间复杂度和空间复杂度,而时间复杂度一般是由比较和移动次数决定的。外部排序除此之外还要考虑磁盘读写速度和次数。
+ 大部分排序算法都仅适用于顺序存储的线性表。
@@ -314,6 +314,8 @@ $n$个元素二路归并排序,归并一共要$\log_2n$趟,每次归并时
## 内部排序
指在排序期间元素全部存放在内存中的排序。除了分配排序,其他的内部排序往往要经过比较和移动。
算法种类|最好时间复杂度|平均时间复杂度|最好时间复杂度|空间复杂度|是否稳定
:-----:|:------------:|:----------:|:------------:|:-------:|:------:
直接插入排序|$O(n)$|$O(n^2)$|$O(n^2)$|$O(1)$|是
@@ -327,6 +329,8 @@ $n$个元素二路归并排序,归并一共要$\log_2n$趟,每次归并时
## 外部排序
指在排序期间元素无法全部同时存放在内存中,必须在排序的过程中根据要求不断地在内、外存之间移动的排序。
### 外部排序的原理
#### 外部排序的过程
@@ -393,7 +397,7 @@ $k$路平衡归并:最多只能有$k$个段归并为一个;每一趟归并
#### 添加虚段
对于$k$叉归并来说,若初始归并段的数量无法构成严格的$k$叉归并树,则需要补充几个长度为0的虚拟段,再进行$k$叉哈夫曼树的构造。
对于$k$叉归并来说,若初始归并段的数量无法构成严格的$k$叉归并树,则需要补充几个长度为$0$的虚拟段,再进行$k$叉哈夫曼树的构造。
那么添加多少虚段呢?
@@ -401,4 +405,4 @@ $k$叉的最佳归并树一定是一棵严格的$k$叉树,即树种只包含
设度为$k$的结点有$n_k$个,度为0的结点有$n_0$个,归并树的总结点树为$n$,则初始归并段数量+虚段数量=$n_0$。
所以$n=n_0+n_k$,$kn_k=n-1$,所以$n_0=(k-1)n_k+1$,所以$n_k=\dfrac{(n_0-1)}{(k-1)}$一定是可以整除的。如果不整除就要添加虚段。
所以$n=n_0+n_k$,$kn_k=n-1$,所以$n_0=(k-1)n_k+1$,所以$n_k=\dfrac{(n_0-1)}{(k-1)}$一定是可以整除的。如果不整除就要添加虚段。

File diff suppressed because one or more lines are too long

View File

@@ -211,6 +211,8 @@ $B+$树可以顺序查找,在叶子结点的块之间定义指向后面叶子
## 散列表查找
线性表和树表中,数据位置与数据关键字无关,而散列表数据位置与关键字有关。
### 散列表定义
散列表又称哈希表,是一种数据结构,数据元素的关键字与其存储地址直接相关。一个散列结构是一块地址连续的存储空间。
@@ -219,12 +221,12 @@ $B+$树可以顺序查找,在叶子结点的块之间定义指向后面叶子
关键字与地址通过散列函数(哈希函数)来实现映射。即记录位置=散列函数(记录关键字)。
+ 直接定址法:可表示为散列函数(关键字)=a×关键字+b,其中a、b均为常数。这种方法计算特别简单,并且不会发生冲突,但当关键字分布不连续时,会出现很多空闲单元,会将造成大量存贮单元的浪费。
+ 直接定址法:可表示为散列函数(关键字)=$a\times$关键字$+b$,其中$a$、$b$均为常数。这种方法计算特别简单,并且不会发生冲突,但当关键字分布不连续时,会出现很多空闲单元,会将造成大量存贮单元的浪费。
+ 数字分析法:分析关键字的各个位的构成,截取其中若干位作为散列函数值,尽可能使关键字具有大的敏感度,即最能进行区分的关键字位,这些位数都是连续的。
+ 平方取中法:先求关键字的平方值,然后在平方值中取中间几位为散列函数的值。因为一个数平方后的中间几位和原数的每一位都相关,因此,使用随机分布的关键字得到的记录的存储位置也是随机的。适用于关键字的每位取值都不够均匀或均小于散列地址所需的位数。
+ 折叠法:将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为散列函数的值,称为折叠法。例如,假设关键字为某人身份证号码430104681015355,则可以用4位为一组进行叠加,即有5355+8101+1046+430=14932,舍去高位,则有H(430104681015355)=4932。
+ 折叠法:将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为散列函数的值。例如,假设关键字为某人身份证号码$430\,1046\,8101\,5355$,则可以用$4$位为一组进行叠加,即有$5355+8101+1046+430=14932$,舍去高位,则有$H(430104681015355)=4932$。
+ 随机数法:对于存储位置给定随机数安排,查找起来会很麻烦。
+ 除留余数法:散列函数(关键字)=关键字%p,p一般是不大于表长的最大质数。这种方法使用较多,关键是选取较理想的p值,使得每一个关键字通过该函数转换后映射到散列空间上任一地址的概率都相等,从而尽可能减少发生冲突的可能性。一般情形下,取p为一个素数较理想,如果是合数则因为可以被多个数整除从而多个关键字余数相同造成冲突。
+ 除留余数法:散列函数(关键字)=关键字$\%p$,$p$一般是不大于表长的最大质数。这种方法使用较多,关键是选取较理想的$p$值,使得每一个关键字通过该函数转换后映射到散列空间上任一地址的概率都相等,从而尽可能减少发生冲突的可能性。一般情形下,取$p$为一个素数较理想,如果是合数则因为可以被多个数整除从而多个关键字余数相同造成冲突。
### 映射冲突
@@ -232,19 +234,22 @@ $B+$树可以顺序查找,在叶子结点的块之间定义指向后面叶子
#### 开放地址法
可存放新表项的空闲地址既向同义词开放叶向非同义词开放。从发生冲突的那个单元开始,按照一定的次序,从哈希表中找出一个空闲的存储单元,把发生冲突的待插入关键字存储到该单元中,从而解决冲突。既指如果当前冲突,则将元素移动到其他空闲的地方。
可存放新表项的空闲地址既向同义词开放也向非同义词开放。从发生冲突的那个单元开始,按照一定的次序,从哈希表中找出一个空闲的存储单元,把发生冲突的待插入关键字存储到该单元中,从而解决冲突。既指如果当前冲突,则将元素移动到其他空闲的地方。
$H_i=(H(key)+d_i)\mod m$。
+ $i$表示发生第$i$次冲突,$i=1,2,\cdots,m-1)$。
+ $m$为散列表长度,类似于循环队列,超出表长以后就循环到最左边。
+ $d_i$为增量序列,是指发生第i次冲突的时候,H(key)往后偏移了多少位。
+ $d_i$为增量序列,是指发生第$i$次冲突的时候,$H(key)$偏移了多少位。
增量序列选择:
+ 线性探测再散列:$d_i=1,2,3,\cdots,m-1$。线性探测再散列法充分利用了哈希表的空间,但在解决一个冲突时,可能造成新的冲突(聚集:同义和非同义关键字都堆积到一起)。另外,也不能随便对结点进行删除。
+ 二次探测再散列:$di=1,-1,2^2,-2^2\cdots,(\dfrac{m}{2})^2,-(\dfrac{m}{2})^2$。对比线性探测法更不容易产生聚集问题。<span style="color:orange">注意:</span>散列表长度$m$必须是一个可以表示为$4j+3$的素数才能探测到所有位置。
+ 伪随机探测再散列,定义$d_i$是一个伪随机数。
+ 线性探测法:$d_i=1,2,3,\cdots,m-1$。线性探测法充分利用了哈希表的空间,但在解决一个冲突时,可能造成新的冲突(聚集:同义和非同义关键字都堆积到一起)。另外,也不能随便对结点进行删除。
+ 二次探测法:$di=1,-1,2^2,-2^2\cdots,(\dfrac{m}{2})^2,-(\dfrac{m}{2})^2$。对比线性探测法更不容易产生聚集问题。<span style="color:orange">注意:</span>散列表长度$m$必须是一个可以表示为$4j+3$的素数才能探测到所有位置。
+ 伪随机探测法:定义$d_i$是一个伪随机数。
+ 再散列法:$d_i=Hash_2(key)$,又称双散列法。需要使用两个散列函数,当通过第一个散列函数$H(key)$得到的地址发生冲突时,则利用第二个散列函数$Hash_2(key)$计算该关键字的地址增量。它的具体散列函数形式:$H_i=(H(key)+i\times Hash_2(key))\%m$。初始探测位置$H=H(key)\%m$。$i$是冲突的次数,初始为$0$。在再散列法中,最多经过$m-1$次探测就会遍历表中所有位置,回到$H_0$位置。
<span style="color:orange">注意:</span>在开放定址的情形下,不能随便物理删除表中的已有元素,因为若删除元素,则会截断其他具有相同散列地址的元素的查找地址。因此,要删除一个元素时,可给它做一个删除标记,进行逻辑删除。但这样做的副作用是:执行多次删除后,表面上看起来散列表很满,实际上有许多位置未利用,因此需要定期维护散列表,要把删除标记的元素物理删除。
#### 链地址法
@@ -252,12 +257,6 @@ $H_i=(H(key)+d_i)\mod m$。
指针需要额外的空间,故当结点规模较小时,开放定址法较为节省空间,而若将节省的指针空间用来扩大散列表的规模,可使装填因子变小,这又减少了开放定址法中的冲突,从而提高平均查找速度。
#### 再散列法
再散列法或再哈希法其实很简单,就是再使用散列函数去散列一个输入的时候,输出是同一个位置就再次使用另一个散列函数计算,直至不发生冲突:
$H_i=RH_i(Key)\,i=1,2,\cdots,k$。
每次冲突都要重新哈希,计算时间增加。
#### 公共溢出区法
@@ -274,6 +273,8 @@ $H_i=RH_i(Key)\,i=1,2,\cdots,k$。
若散列函数设计得足够好,散列查找时间复杂度可以达到$O(1)$,即不存在冲突。
散列表的查找效率取决于三个因素:散列函数、处理冲突的方法和装填因子。
**装填因子**=表中记录数÷散列表长度。装填因子代表一个散列表中的满余情况,越大则查找效率越低。
若只给出了装填因子$\alpha$,则此时平均查找长度为:$ASL=\dfrac{1}{2}(1+\dfrac{1}{1-\alpha})$。