散列 (哈希)
实现一种快速索引
call by value 通过值去索引。
java 中有 HashMap散列 和 Hashtable散列表 利用key-value pair存到相应的位置。
python 中有 dictionary字典 利用hashtable
数据结构和算法是硬币的两面。hashtable是一种数据结构,而hash是一种算法。
在字符串匹配的BC表中 bad character 如果是unicode那么BC表存储的大小为65536。存在数据存储的问题???
1、space 空间大小,效率(利用率)。
2、time 需要做一个初始化。for… 或者 memset…
哈希算法:
如果开一个非常大列表,所有的查询操作仅需要O(1)的时间。但是需要的空间非常大、利用率非常低。
基于该朴素的思想,假象开一个这么大的空间,但是之前的一一对应之间加入一个hashtable。
一共分为两步:keys——hashtable——entry 此时散列表可以做得更小。
假设中间的hashtable的值为90001(素数) hash(key) = key%M 但是会存在冲突的情况。key不同但hash值相同。
如果不存在冲突的情况就是完美散列。perfect hashing。 Synonym collision 同义冲突 将出现collision情况的key称为synonym同义词。
hash函数有以下几种:
1、Division-Remainder 取模函数(mod) :存在零点不动点。邻近数依然相邻。
2、Mulitiply-Add-Division(MAD): (a*key+b)%M 改进了上面两点
3、Selecting Digits 选择数位法 奇偶错位法
4、Mid-Square 混沌 平方去中间三位值
多项式polynomial散列码的移位实现:
hashcode()函数将不是整数的数据(如字符串)进行预处理转换为整数。
static size_t hashCode( char s[] ){
unsigned int h = 0;
for( size_t n=strlen(s), i=0; i<n; i++ )
{ h = (h<<5) | (h>>27) ; h += ( int ) s[i] ; } //散列码循环左移五位再累加当前字符。
return (size_t) h;
} //循环移位5个 是根据经验的得到的
利用多项式散列码可以避免类似于tops和stop之类的冲突。
如果真的发生collision冲突,该如何解决呢 。散列表hashtable=bucketarray
bucket array 每个桶都存了一个索引。
在期望意义下,其复杂度为O(1).
open-hashing — close-addressing 利用率由于link的存在,因此也利用率不超过50%。 查询时有一个key-hash()-entry。
open指散列表在某个地方还可以利用list再延长。也就是独立链法
定址close指任何一个地址,要么是空的,要么就是存储它所指向的值。
close-hashing — open-addressing
close是指计算出hash位置后,不会像open一样,生成link,而是另找隔壁或者找差分数组后的位置,步长发生变化。1、3、5、7… 差分的数组。后者也称之为平方试探(quadratic probing)。
open是指地址不是固定。
Bucketsort 桶排序
m个桶,n个数
初始化initialO(m)—分配distrO(n)—collection收集O(m)。 当n远大于m时,该算法比较好。会变成一个线性的排序算法。
stability + deduplicate 算法的稳定性。同一组的同义词。在最终输出的有序序列中,要保持原有的有序性,2a始终在2b&2c之前。是否能够一直保持这种性质。如何做的分配,如何收集起来——以保持算法的稳定性呢。
Max - Gap 最大间隙
求一段序列切分过以后,最长的序列。
Navie 算法(brute force): sorting + scan lo—hi 记录d 最少需要O(nlogn) 并不认为是一个好的解法希望做到O(n).
改进:花线性的时间寻找到lo&hi。等间距寻找,n-1个桶,最后在配上一个桶单独罩住hi。[lo,hi)
仿照bucketsort 对每个切分点做一个分配。有些桶里会有点落进去,删除非空桶。对于每一个非空的桶。只记其中最左边和最右边的点,落于中间的切点可以忽略不计。遍历,前后非空桶之间会有空的gap。寻找出maxgap。
原理:n-1个间隙的maxgap不会低于平均宽度(lo-hi/n-1) 最宽的一定比平均值大。也就是4个O(n)。
巧妙利用了分桶的策略,降低复杂度。
radixsort 基数排序 : lexicographioc order 字典序
radix 基数
逐列(位):桶排序。先对个位数进行桶排列。bucketsort。十进制准备十个桶就够了
correctness & Stability 稳定性,因为先对低位进行过排序,再对高位进行排序。因此,高位相同时,低位的大小影响其大小比较。
从低位开始逐一做一个bucketsort。 也为O(n)
Bitmap:Structure 数据结构
位图 长度。(以char为单位 也就是一个字节八位)
如何存储整数的子集。相当于一个映射表,只要记录1和0就可以了。
class Bitmap{
private:
int N; //位图长度(以sizeof(char)为单位)
char * M; //以char(8比特)为单位的比特位图
public:
Bitmap( int n=8 ) { M = new char[N = (n+7)/8 ]; memset(M, 0, N ); }
~Bitmap() { delete [] M; M = NULL; } //析构
void set( int k ); void clear( int k ); bool test( int k ); //ADT
}
union() clear() test() 对集合中的元素进行操作。变成位图进行实现。
void set( int k )
{ expand( k ); M[ k>>3 ] |= (0x80 >> (k & 0x07) ); }
void clear( int k )
{ expand( k ); M[ k>>3 ] &= ~(0x80 >> (k & 0x07) ); }
void test( int k )
{ expand( k ); return M[k >> 3] & (0x80 >> (k&0x07) ); }
k/8 就是k>>3 k右移三位。右移操作比除法要快。挪到相应的字节位置。
k%8 就是 k & 0x07 按位与。 0x80 — 1000 0000 代表8位 后移几位由k&0x07确定。 0x80 >> (k&0x07) 会得到一个mask,来测试该位是否为1。
基本功是test 由test可以得到 clear 和 set 操作。
bitmap应用 Eratosthenes:Idea 素数的筛法
void Eratosthenes( int n, char * file ){
Bitmap B(n);
B.set(0); B.set(1);
for(int i=2; i<n; i++)
if( !B.test(i) ) //断定i是素数prime
for(int j=2*i; j<n; j+=i)
B.set(j);
B.dump(file);
}
散列表的缺陷。初始化需要O(n)时间
Hopcroft 发明的O(1)时间初始化。
代价是需要除B[]外的两倍空间。 F[]&T[]。
F暗示from ;T暗示to
校验环:凡是出现校验环的位置是有效的位置,没有的话就不是有效位置。F[ T[x] ] = x
还有一个top值,只有在top值的左边才是有效的。top值所指的位置不是有效。
clear:删除元素的操作
末尾易操作。
中间元素的操作:把栈尾元素需要挪到待删除元素 。 top–。需要同时修改F中相应数组的元素。