1 散列表
1.1 概念
散列表(Hash table,也叫哈希表),是根据键(Key)而直接访问在内存存储位置的数据结构
主要将键传递一个函数(散列函数),进行一定的计算,获取值存放的位置,通过位置获取数据
1.2 代码实现
function HashTable () {
//存储数据
this.table = [];
//散列函数:将键的每个字母转换为ASCII码,ASCII码再相加,最后把相加的结果与一个数据求余
// HashTable.prototype.hashFunc = function ( key ) {
// let num = 0;
// for(let i=0;i<key.length;i++){
// num+=key.charCodeAt(i);
// }
// return num%37;
// }
//最受社区推崇的散列函数之一djb2HashCode,插入和查询较快,冲突几率小
HashTable.prototype.hashFunc = function (key) {
var hash = 5381; //赋值一个质数
for (var i = 0; i < key.length; i++) {
hash = hash * 33 + key.charCodeAt(i);
}
return hash % 1013; //{4}
};
//向散列表增加一个新的项
//通过散列函数计算的位置有可能出现相同的情况,后面的数据会把之前的覆盖掉
//解决冲突
//方式一:分离链接法
//核心:每一个位置放置一个链表,数据(键,值)存储到链表中
HashTable.prototype.put = function ( key,value ) {
//通过散列函数计算出存储位置
let position = this.hashFunc(key);
//判断给位置是否有链表存在,如果没有就创建一个放到position位置
if(this.table[position]===undefined){
let listLinked = new LinkedList();
this.table[position] = listLinked;
}
//根据键值创建一个对象,放到链表中
this.table[position].append(new ValuePair(key,value));
}
//根据键值从散列表中移除值
HashTable.prototype.remove = function ( key ) {
let position = this.hashFunc(key);
if(this.table[position]!=undefined){
let current = this.table[position].getHead();
while(current.next){
if(current.element.key===key){
//从链表中移除
this.table[position].remove(key);
//当链表为空了,就把散列表该位置设为undefined
if(this.table[position].isEmpty()){
this.table[position] = undefined;
}
return true;
}
current = current.next;
}
//检查是否为第一个或最后一个元素
if(current.element.key===key){
//从链表中移除元素
this.table[position].remove(key);
//当链表为空了,就把散列表该位置设为undefined
if(this.table[position].isEmpty()){
this.table[position] = undefined;
}
return true;
}
}
//如果没有要移除的元素
return false;
}
//返回根据键值检索到的特定的值
HashTable.prototype.get = function ( key ) {
let position = this.hashFunc(key);
//如果该位置有链表,查询链表元素
if(this.table[position]!=undefined){
let current = this.table[position].getHead();
while ( current.next ){
if(current.element.key===key){
return current.element.value;
}
current = current.next;
}
//检测节点是第一个或者最后一个的情况
if(current.element.key===key){
return current.element.value;
}
}
//对应位置没有链表,
return undefined;
}
//用来输出整个散列表
HashTable.prototype.print = function ( ) {
for ( let i = 0 ;i<this.table.length;i++ ) {
//判断是否为undefined
if(this.table[i]!=undefined){
console.log (i+"-" +this.table[ i ] );
}
}
}
//定义一个函数,存储键和值
let ValuePair = function ( key,value ) {
this.key = key;
this.value = value;
//重写toString方法
this.toString = function ( ) {
return '['+this.key+'-'+this.value+']';
}
}
}
1.3 验证
//测试
var hash = new HashTable()
hash.put('Gandalf', 'gandalf@email.com')
hash.put('John', 'john@email.com')
hash.put('Tyrion', 'tyrion@email.com')
hash.put('Aaron', 'aaron@email.com')
hash.put('Donnie', 'donnie@email.com')
hash.put('Ana', 'ana@email.com')
hash.put('Jonathan', 'jonathan@email.com')
hash.put('Jamie', 'jamie@email.com')
hash.put('Sue', 'sue@email.com')
hash.put('Mindy', 'mindy@email.com')
hash.put('Paul', 'paul@email.com')
hash.put('Nathan', 'nathan@email.com')
hash.print();
1.4 实现过程中借助的链表
因为通过散列函数计算的位置有可能出现相同的情况,后面的数据会把之前的覆盖掉 解决:每一个位置放置一个链表,数据(键,值)存储到链表中
作用:解决冲突
function LinkedList() {
//封装一个node类,保存每个节点信息
function Node( element ) {
this.element = element;//当前节点的值
this.next = null;//下一个节点的引用/指针
}
this.length = 0;//链表的长度,bug1 let length
this.head = null; //链表的第一个节点
//链表尾部添加元素
LinkedList.prototype.append = function ( element ) {
//1 根据元素创建节点
let newNode = new Node(element);
//2 判断链表是否为空
if(this.head==null){//链表为空,newNode作为头节点添加进去,
this.head = newNode;
}else{
//2.1 链表不为空,定义变量存储当前节点
let current = this.head;
//2.2 遍历链表,找最后一个节点
while ( current.next ) {
current = current.next;
}
//2.3 新节点添加到当前节点(最后一个节点)的后面
current.next = newNode;
}
//3 链表长度增加1
this.length++;
}
//任意位置插入元素
LinkedList.prototype.insert = function ( position,element ) {
//1 检测边界:如果插入位置无效,插入失败,返回false
if(position<0||position>this.length){
return false;
}
//2 根据元素创建新节点
let newNode = new Node(element);
//3 判断插入位置
if(position==0){
//插入链表的开始位置,newNode.next指向head;并且head指向newNode
newNode.next = this.head;
this.head = newNode;
}else{
//3.1 插入非开始位置
//定义变量存储当前节点和当前节点的位置
let current = head;
let index = 0;
let previous = null;//存储当前节点的上一个节点
//查找position位置的节点
while(index<position){
previous = current;
current = current.next;
index++;
}
//将newNode.next指向current节点,previous.next指向newNode节点
newNode.next = current;
previous.next = newNode;
}
//4 链表长度增加1,并返回true,添加成功
this.length++;
return true;
}
//移除任意位置的元素
LinkedList.prototype.removeAt = function ( position ) {
//1 检测越界:越界不做操作,返回null
if(position<0||position>=this.length){
return null;
}
//定义变量存储当前节点和当前节点的位置
let index = 0;
let current = head;
let previous = null;//存储当前节点的上一个节点
//2 判断移除的位置
if(position==0){
//2.1 移除第一个节点,设置head指向head.next即可
this.head = head.next;
}else{
//2.2 移除非第一个节点
//查找position位置的节点
while(index<position){
previous = current;
current = current.next;
index++;
}
//移除当前节点:previous.next指向current.next
previous.next = current.next;
}
//3 链表长度减1,并返回被移除的元素
this.length--;
return current.element;
}
//获取元素在链表中的位置
LinkedList.prototype.indexOf = function ( element ) {
//1 定义变量存储信息
let current = this.head;
let index =0;//current节点在链表的位置
//2 链表中查找元素
while(current){
//判断当前节点的元素是否等于目标元素
if(current.element==element){
//找到元素,返回位置
return index;
}
current=current.next;
index++;
}
//3 遍历链表都没有找到元素,返回-1
return -1;
}
//删除元素
LinkedList.prototype.reomve = function ( element ) {
let index = this.indexOf(element);//获取节点在链表中的位置
return this.removeAt(index);//根据节点位置移除节点,返回节点的值
}
//获取第一个节点
LinkedList.prototype.getHead = function ( ) {
return this.head;
}
//获取链表的长度
LinkedList.prototype.size = function ( ) {
return this.length;
}
//判断链表是否有节点
LinkedList.prototype.isEmpty = function ( ) {
return this.length==0;
}
//链表的toString方法
LinkedList.prototype.toString = function ( ) {
//定义变量,存储当前节点和节点的值
let current = this.head;
let str = "";
while(current){
str+= current.element+(current.next?",":"");
current = current.next;
}
return str;
}
}/*
** Create by caohai on 2018/10/29
*/