字典和散列表
发表于 2017-12-18 15:56:17 | 分类于 算法 |
集合、字典、散列表
- 都可以存储不重复的值
- 集合,每个值作为主要元素
- 字典、散列表,以 [ 键, 值 ] 对的形式存储数据
- 字典、散列表各自的实现方式有所不同
字典
字典的方法
set(key, value):向字典添加新元素
remove (key):通过使用键值来从字典中移除键值对应的数据值
has(key):如果某个键值存在于这个字典中,则返回 true ,反之,返回 false
get(key):通过键值查找特定的数值并返回
clear():将字典的所有数据全部删除
size():返回字典所包含元素的数量
keys():将字典所包含的所有键名以数组形式返回
values():将字典所包含的所有数值以数组形式返回
创建字典
function Dictionary ( ) {
  	var items = { } ;
  	this.has = function ( key ) {
      	return key in items ;
		//【疑惑】为什么不是在 items 的自身属性中检查
		// hasOwnProperty()
		// 如果输入的 key 是 items 的原型的属性,
		// 而不是 items 的自身属性,是否会造成错误?
		// 例如调用 remove()方法时,会不会出现错误?  
  	} ;
  
  	this.set = function ( key, value ) {
      	items [ key ] = value ;
  	} ;
  
  	this.remove = function (key) {
      	if ( this.has ( key ) ) {
          	delete items [ key ] ;
          	return true ;
      	}
      	return false ;
  	} ;
  
  	this.get = function ( key ) {
      	return this.has ( key ) ? items [ key ] : undefined ;
  	} ;
  
  	this.values = function ( ) {
      	var values = [];
      	for ( var k in items ) {
          	if ( this.has ( k ) ) {
              	values.push ( items [ k ] ) ;
          	}
      	}
      	return values ;
  	} ;
  
  	this.clear = function ( ) {};
  	this.size = function ( ) {};
  	this.keys = function ( ) {} ;
  	this.getItems = function ( ) {
      	return items ;
  	} ;
  
  	
}
散列表
散列表的方法
put(key, value):向散列表增加一个新的项(也能更新散列表)
remove(key):根据键值从散列表中移除值
get(key):返回根据键值检索到的特定的值
创建散列表
function HashTable ( ) {
  	var table = [ ] ;
  
  	//散列函数
  	var loseloseHashCode = function ( key ) {
      	var hash = 0 ;
      	for ( var i = 0; i < key.length; i++ ) {
          	hash += key.charCodeAt ( i ) ;
      	}
      	return hash % 37 ;
  	} ;
  
  	this.put = function ( key, value ) {
      	var position = loseloseHashCode ( key ) ;
      	table [ position ] = value ;
  	} ;
  
  	this.get = function ( key ) {
      	return table [ loseloseHahCode ( key ) ] ;
  	} ;
  
  	this.remove = function ( key ) {
      	table [ loseloseHashCode ( key ) ] = undefined ;
  	} ;
} 
冲突处理
- 分离链接
- 线性探查
- 双散列法
- 对于分离链接、线性探查,只须重写三个方法:put、get、remove
分离链接
为散列表的每一个位置创建一个链表并将元素存储在里面。
这是解决冲突的最简单的方法,但是在 HashTable 实例之外需要额外的存储空间。
// 辅助类,表示将要加入 LinkedList 实例的元素
var ValuePair = function ( key, value ) {
  	this.key = key;
  	this.value = value;
  	this.toString = function() {
      	return "[" + this.key + "-" + this.value + "]";
  	};
};
// put 方法
this.put = function ( key, value ) {
 	var position = loseloseHashCode ( key ) ;
  
  	if ( table [ position ] === undefined ) {
      	talbe [ positon ] = new LinkedList ( ) ;
  	}
  
  	table [ position ].append ( new ValuePair ( key, value ) ) ;
} ;
// get 方法
this.get = function ( key ) {
  	var position = loseloseHashCode ( key ) ;
  	
  	if ( table [ positon ] !== undefined ) {
      	
      	// 遍历链表来寻找键/值
      	var current = table [ position ].getHead ( ) ;
      	
      	while ( current.next ) {
          	if ( current.element.key === key ) {
              	return current.element.value ;
          	}
          	current = current.next ;
      	}
      	
      	// 检查元素在链表第一个或最后一个节点的情况
      	if ( current.elelemt.key === key ) {
          	return current.element.value ;
      	}
  	}
  	return undefined ;
} ;
// remove 方法
this.remove = function ( key ) {
  	var positon = loseloseHashCode ( key ) ;
  	
  	if ( table [ position ] !== undefined ) {
      	
      	var current = table [ position ].getHead ( ) ;
      	while ( current.next ) {
          	if ( current.element.key === key ) {
              	table [ position ].remove ( current.element ) ;
              	if ( table [ position ].isEmpty ( ) ) {
                  	table [ position ] = undefined ;
              	}
              	return true ;
          	}
          	current = current.next ;
      	}
      
      	// 检查是否为第一个或最后一个元素
      	if ( current.element.key === key ) {
          	table [ position ].remove ( current.element ) ;
          	if ( table [ position ].isEmpty () ) {
              	table [ position ].remove ( current.element ) ;
          	}
          	return true ;
      	}
  	} 
  	
  	return false ;
} ;
线性探查
当想向表中某个位置加入一个新元素的时候,如果索引为 index 的位置已被占据,就尝试 index + 1 的位置。
如果 index + 1 的位置也被占据了,就尝试 index + 2 的位置,以此类推。
// put 方法
this.put = function ( key, value ) {
  	var position = loseloseHashCode ( key ) ;
  
  	if ( table [ position ] === undefined ) {
      	table [ position ] = new Value Pair ( key, value ) ;
  	}
  	else {
      	var index = ++position ;
      	while ( table [ index] !== undefined ) {
          	index++ ;
      	}
      	table [ index ] = new ValuePair ( key, value ) ;
  	}
} ;
// get 方法
this.get = function ( key ) {
  	var position = loseloseHashCode ( key ) ;
  
  	if ( table [ position ] !== undefined ) {
      	if ( table [ position ].key === kye ) {
          	return table [ position ].value ;
      	}
      	else {
          	var index = ++ position ;
          	while ( table [ index ] === undefined || table [ index ].key !== key )	{
              	index++ ;
          	}
          	if ( table [ index ].key === key ) {
              	return table [ index ].value ;
          	}
      	}
  	}
  	return undefined ;
} ;
// remove 方法
this.remove = function ( key ) {
  	var position = loseloseHashCode ( key ) ;
  
  	if ( table [ position ] !== undefined ) {
      	if ( table [position ].key === kye ) {
          	table [ index ] = undefined ;
      	}
      	else {
          	var index = ++ position ;
          	while ( table [ index ] === undefined || table [ index ].key !== key )	{
              	index++ ;
          	}
          	if ( table [ index ].key === key ) {
              	table [ index ] = undefined ;
          	}
      	}
  	}
  	return undefined ;
} ;
table [ index ] = undefined ;
更好的散列函数
一个表现良好的散列函数:
- 插入、检索元素的时间(即性能)
- 较低的冲突可能性
// 比 “ loseloseHashCode() ” 更好的散列函数
var djb2HashCode = function ( key ) {
  	var hash = 5381 ; // 初始化一个质数
  	for ( var i = 0; i < key.length; i++ ) {
      	hash = hash*33 + key.charCodeAt ( i );
  	}
  	return hash % 1013 ; // 将使用相加的和与另一个随机质数(比我们认为的散列表的大小要大)相除的余数
} ;