字典和散列表

发表于 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 ; // 将使用相加的和与另一个随机质数(比我们认为的散列表的大小要大)相除的余数
} ;