# 数据结构——散列

## 散列的代码实现

interface IHashTable<T>{
// 获取索引值
getIndex(flag:string):number;
// 两种存放方式：放入值，放入一个值和简称值
put(flag:string,ele:T):void;
// 获取值
get(flag:string):T;
// 移除元素
remove(flag:string):T;
// 获取散列中的元素
toString():T[];
}


hashTable.put("MIKE","mike@mike.com")
hashTable.put("JACK","jack@jack.com")


class HashTable<T> implements IHashTable<T>{
// 散列表
private dataStore:T[] = [];
// 散列表的长度
private dataStoreLen:number = 0;
// 盐值
private hashSalt:number = 71;
constructor(dataStoreLen:number,hashSalt?:number){
// 创建散列对象时传入散列表的长度和盐值
this.dataStoreLen = dataStoreLen;
if(hashSalt){
this.hashSalt = hashSalt;
}
}
getIndex(flag:string):number{
let hash:number = -1,pos:number = -1;
if(flag.length){
hash = 0;
for(const c of flag){
hash += c.charCodeAt(0);
}
pos = hash % this.dataStoreLen;
}
// 存放位置为计算出的 hash 值对数组的长度取余
return pos;
}
put(flag:string,ele:T):void{
const pos:number = this.getIndex(flag);
this.dataStore[pos] = ele;
}
get(flag:string):T{
const pos:number = this.getIndex(flag);
return this.dataStore[pos];
}
remove(flag:string):T{
const pos:number = this.getIndex(flag);
const res = this.dataStore[pos];
this.dataStore[pos] = undefined;
return res;
}
toString():T[]{
const tmp:T[] = [];
for(const ele of this.dataStore){
if(ele !== undefined){
tmp.push(ele);
}
}
return tmp;
}
}


const h = new HashTable<string>(301);
h.put("MIKE","mike@mike.com")
h.put("JACK","jack@jack.com")
h.put("PENNY","penny@penny.com")
h.put("JERRY","jerry@jerry.com")
// 获取元素
console.log(h.get("PENNY"))
console.log(h.toString())
// 移除元素
h.remove("JERRY")
console.log(h.toString())


penny@penny.com
[ 'penny@penny.com',
'jerry@jerry.com',
'jack@jack.com',
'mike@mike.com' ]
[ 'penny@penny.com', 'jack@jack.com', 'mike@mike.com' ]


## 碰撞问题

const h = new HashTable<string>(301);
h.put("MIKE","mike@mike.com")
h.put("JACK","jack@jack.com")
h.put("PENNY","penny@penny.com")
h.put("JERRY","jerry@jerry.com")
h.put("ACKJ","ackj@ackj.com")
console.log(h.toString())


[ 'penny@penny.com',
'jerry@jerry.com',
'ackj@ackj.com',
'mike@mike.com' ]


...
getIndex(flag:string):number{
// 减少散列碰撞，加点盐
let
hash:number = -1,
pos:number = -1;

if(flag.length){
hash = 0;
for(const c of flag){
hash += (this.hashSalt * hash + c.charCodeAt(0));
}
pos = hash % this.dataStoreLen;
}
// 存放位置为计算出的 hash 值对数组的长度取余
return pos;
}
...


[ 'penny@penny.com',
'mike@mike.com',
'jerry@jerry.com',
'jack@jack.com',
'ackj@ackj.com' ]


...
getIndex(flag:string):number{
let hash:number = 5381;
for (const c of flag) {
hash = hash * 33 + c.charCodeAt(0);
}
return hash % 1013;
}
...


## 解决碰撞的其他方案

### 分离链接法

[
{key:"JACK",value:"jack@jack.com"},
{key:"ACKJ",value:"ackj@ackj.com"}
]


interface IHashTable<T>{
// 获取索引值
getIndex(flag:string):number;
// 两种存放方式：放入值，放入一个值和简称值
put(flag:string,ele:T):void;
// 获取值
get(flag:string):T;
// 移除元素
remove(flag:string):T;
// 获取散列中的元素
toString():Map<string,T>[];
}

class HashTable<T> implements IHashTable<T>{
// 散列表
private dataStore:Map<string,T>[] = [];
// 散列表的长度
private dataStoreLen:number = 0;
// 盐值
private hashSalt:number = 71;
constructor(dataStoreLen:number,hashSalt?:number){
// 创建散列对象时传入散列表的长度和盐值
this.dataStoreLen = dataStoreLen;
if(hashSalt){
this.hashSalt = hashSalt;
}
}
getIndex(flag:string):number{
// let hash:number = 5381;
// for (const c of flag) {
//     hash = hash * 33 + c.charCodeAt(0);
// }
// return hash % 1013;
let hash:number = -1,pos:number = -1;
if(flag.length){
hash = 0;
for(const c of flag){
hash += c.charCodeAt(0);
}
pos = hash % this.dataStoreLen;
}
// 存放位置为计算出的 hash 值对数组的长度取余
return pos;
}
put(flag:string,ele:T):void{
const pos:number = this.getIndex(flag);
// 初始化操作
if(this.dataStore[pos] === undefined){
// 创建一个 Map 对象
const tmp = new Map<string,T>();
tmp.set(flag,ele);
this.dataStore[pos] = tmp;
}else{
this.dataStore[pos].set(flag,ele);
}
}
get(flag:string):T{
const pos:number = this.getIndex(flag);
return this.dataStore[pos].get(flag);
}
remove(flag:string):T{
const pos:number = this.getIndex(flag);
const res = this.get(flag);
this.dataStore[pos].delete(flag);
return res;
}
toString():Map<string,T>[]{
const tmp:Map<string,T>[] = [];
for(const ele of this.dataStore){
if(ele !== undefined){
tmp.push(ele);
}
}
return tmp;
}
}


const h = new HashTable<string>(301);
h.put("MIKE","mike@mike.com")
h.put("JACK","jack@jack.com")
h.put("PENNY","penny@penny.com")
h.put("JERRY","jerry@jerry.com")
h.put("ACKJ","ackj@ackj.com")
console.log(h.toString())


[ Map { 'PENNY' => 'penny@penny.com' },
Map { 'JERRY' => 'jerry@jerry.com' },
Map { 'JACK' => 'jack@jack.com', 'ACKJ' => 'ackj@ackj.com' },
Map { 'MIKE' => 'mike@mike.com' } ]


## 线性探测法

interface IHashTable<T>{
// 获取索引值
getIndex(flag:string):number;
// 两种存放方式：放入值，放入一个值和简称值
put(flag:string,ele:T):void;
// 获取值
get(flag:string):T;
// 移除元素
remove(flag:string):T;
// 查找元素
find(flag:string,startPos:number):number;
// 获取散列中的元素
toString():{key:string,value:T}[];
}

class HashTable<T> implements IHashTable<T>{
// 散列表
private dataStore:{key:string,value:T}[] = [];
// 散列表的长度
private dataStoreLen:number = 0;
// 盐值
private hashSalt:number = 71;
constructor(dataStoreLen:number,hashSalt?:number){
// 创建散列对象时传入散列表的长度和盐值
this.dataStoreLen = dataStoreLen;
if(hashSalt){
this.hashSalt = hashSalt;
}
}
getIndex(flag:string):number{
// let hash:number = 5381;
// for (const c of flag) {
//     hash = hash * 33 + c.charCodeAt(0);
// }
// return hash % 1013;
let hash:number = -1,pos:number = -1;
if(flag.length){
hash = 0;
for(const c of flag){
hash += c.charCodeAt(0);
}
pos = hash % this.dataStoreLen;
}
// 存放位置为计算出的 hash 值对数组的长度取余
return pos;
}
find(flag:string,startPos:number):number{
let len:number = this.dataStoreLen;
while(len--){
const tmp = this.dataStore[startPos];
if(tmp.key === flag){
return startPos;
}
startPos++;
}
}
put(flag:string,ele:T):void{
let pos:number = this.getIndex(flag);
// 初始化操作
if(this.dataStore[pos] === undefined){
const tmp = {key:flag,value:ele};
this.dataStore[pos] = tmp;
}else{
const tmp = {key:flag,value:ele};
while(this.dataStore[pos] !== undefined){
pos++;
}
this.dataStore[pos] = tmp;
}
}
get(flag:string):T{
const pos:number = this.getIndex(flag);
const realPos:number = this.find(flag,pos);
return this.dataStore[realPos].value;
}
remove(flag:string):T{
const pos:number = this.getIndex(flag);
const realPos:number = this.find(flag,pos);
const res = this.get(flag);
this.dataStore[realPos] = undefined;
return res;
}
toString():{key:string,value:T}[]{
const tmp:{key:string,value:T}[] = [];
for(const ele of this.dataStore){
if(ele !== undefined){
tmp.push(ele);
}
}
return tmp;
}
}


const h = new HashTable<string>(301);
h.put("MIKE","mike@mike.com")
h.put("JACK","jack@jack.com")
h.put("ACKJ","ackj@ackj.com")
h.put("PENNY","penny@penny.com")
h.put("CKAJ","ckaj@ckaj.com")
h.put("AKCJ","ackj@ackj.com")
h.put("JERRY","jerry@jerry.com")
console.log(h.toString())


[ { key: 'PENNY', value: 'penny@penny.com' },
{ key: 'JERRY', value: 'jerry@jerry.com' },
{ key: 'JACK', value: 'jack@jack.com' },
{ key: 'ACKJ', value: 'ackj@ackj.com' },
{ key: 'CKAJ', value: 'ckaj@ckaj.com' },
{ key: 'AKCJ', value: 'ackj@ackj.com' },
{ key: 'MIKE', value: 'mike@mike.com' } ]


## 总结

• 优化计算特征值的方法，推荐使用 djb2 函数
• 使用分离链接法，也称作开链法解决冲突
• 使用线性探测法解决冲突