集合、字典和散列表都可以存储不重复的值。
在集合中,我们感兴趣的是每个值本身,并把它当作主要元素。在字典和散列表中,我们用 [键,值] 的形式来存储数据。
集合(Set 类):[值,值]对,是一组由无序且唯一(即不能重复)的项组成的。
字典(Map 类):[键,值]对,也称作映射,其中键名是用来查询特定元素的。
散列(HashTable类/HashMap 类):[键,值]对,是Dictionary类的一种散列表实现方式。散列函数的作用是给定一个键值,然后返回值在表中的地址。散列算法的作用是尽可能快地在数据结构中找到一个值。
在一些编程语言中,还有一种叫作散列集合的实现。散列集合由一个集合构成,但是插入、移除或获取元素时,使用的是散列函数。实现散列集合的不同之处在于,不再添加键值对,而是只插入值而没有键。和集合相似,散列集合只存储唯一的不重复的值。
--------------------------------------------------------------------------
(1)集合方法声明:
首先,使用对象来表示集合。
序号
方法
说明
1
add(value) 向集合添加一个新的项
2
remove(value) 从集合移除一个值
3
has(value)
如果值在结合中,返回 true,否则返回 false
4
clear ( )
移除集合中的所有项
5
size ( )
返回集合所包含元素的数量。与数组的 length 属性类似。
6
values ( )
返回一个包含集合中所有值的数组。

集合的实现:

 function Set() {
var items = {}; /*this.has = function(value) {
return value in items; // in 操作符会在通过对象访问给定属性时返回true,无论该属性存在于实例中还是原型中
}*/ // better way
this.has = function(value) {
return items.hasOwnProperty(value); // hasOwnProperty()方法只在给定属性存在于对象实例中时,才会返回true
}; this.add = function(value) {
if (!this.has(value)) {
items[value] = value; //添加一个值的时候,把它同时作为键和值保存,因为这样有利于查找这个值
reutrn true; // 返回true,表示添加了这个值
}
return false; // 返回false,表示没有添加它
}; this.remove = function(value) {
if (this.has(value)) {
delete items[value]; // 既然用对象来存储集合的items对象,就可以简单地使用delete操作符从items对象中移除属性
return true;
}
return false;
}; this.clear = function() {
items = {};
}; // size实现方法一(只能在现代浏览器中运行)
/*this.size = function() {
return Object.keys(items).length; // Object类有一个keys方法,返回一个包含给定对象所有属性的数组
}*/ // size实现方法二(可以在任何浏览器上运行)
this.size = function() {
var count = 0;
for (var prop in items) { // for...in...用于枚举对象中的属性,包括实例和原型中的属性
if (items.hasOwnProperty(prop)) { // 检查它们是否是对象自身的属性
++count;
}
}
return count;
}; // values实现方法一,同size实现方法一
/*this.values = function() {
return Object.keys(items);
}*/ // values实现方法二,同size实现方法二
this.values = function() {
var keys = [];
for (var key in items) {
keys.push(key);
}
return keys;
};
}

Set.js

(2)字典方法声明:

首先,使用对象来表示集合。
序号
方法
说明
1
set(key, value) 向字典中添加新元素
2
remove(key) 通过使用键值来从字典中移除键值对应的数据值
3
has(key)
如果某个键值存在于这个字典中,则返回 true,反之则返回 false
4
get(key)
通过键值查找特定的数值并返回
5
clear ( )
将这个字典中的所有元素全部删除
6
size ( )
返回字典中所包含元素的数量。与数组的 length 属性类似。
7
keys()
将字典所包含的所有键名以数组形式返回
8
values ( )
将字典所包含的所有数值以数组形式返回

字典的实现:

 function Dictionary() {
var items = {}; this.has = function(value) { // 之所以要先实现该方法,是因为它会被set和remove等其他方法调用
return key in items;
}; 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; // get方法会首先验证我们想要的值是否存在(通过查找key值)
}; this.values = function() {
var values = [];
for (var k in items) {
if (this.has(k)) {
values.push(items[k]);
}
}
return values;
}; this.clear = function() {
items = {};
}; this.size = function() {
return Object.keys(items).length; // Object类有一个keys方法,返回一个包含给定对象所有属性的数组
}; this.keys = function() {
return Object.keys(items);
}; this.getItems = function() {
reutrn items;
};
}

Dictionary.js

(3)散列方法声明:

首先,使用数组来表示集合。
三个基础方法:
序号
方法
说明
1
put(key, value) 向散列表增加一个新的项(也能更新散列表)
2
remove(key) 根据键值从散列表中移除值
3
get(key)
返回根据键值检索到的特定的值

在实现这三个方法之前,要实现的第一个方法是散列函数,它是 HashTable 类中的一个私有方法。

处理散列表的冲突:

有时候,一些键会有相同的散列值。不同的值在散列表中对应相同位置的时候,我们称其为冲突。处理冲突有几种方法:分离链接、线性探查和双散列法。
分离链接:包括为散列表的每一个位置创建一个链表并将元素存储在里面。它是是解决冲突的最简单的方法,但是它在 HashTable 实例之外还需要额外的存储空间。

线性探查:当想向表中某个位置加入一个新元素的时候,如果索引为 index 的位置已经被占据了,就尝试 index+1 的位置。如果 index+1 的位置也被占据了,就尝试 index+2 的位置,以此类推。

一个表现良好的散列函数是由几个方面构成的:插入和检索元素的时间(即性能),当然也包括较低的冲突的可能。

散列表的实现:

 function HashTable() {
var table = []; // 散列函数,是HashTable中的一个私有方法
var loseloseHashCode = function(key) {
var hash = 0;
for (var i=0; i<key.length; i++) {
hash += key.charCodeAt(i); // 给定一个key参数,我们就能根据组成key的每个字符的ASCII码值的和得到一个数字
}
return hash % 37; // 为了得到比较小的数值,我们会使用hash值和一个任意数做除法的余数
}; // 更好的散列函数(这并不是最好的散列函数,但这是最被社区推荐的散列函数之一)
var djb2HashCode = function(key) {
var hash = 5381; // 初始化赋值为一个质数,大多数实现都使用5318
for (var i=0; i<key.length; i++) {
hash += hash * 33 + key.charCodeAt(i); // 将hash与33相乘,当作一个魔力数
}
return hash % 1013; // 将相加的和与另一个随机质数相除
} this.put = function(key, value) {
var position = loseloseHashCode(key); // 给定一个键值,我们需要根据所创建的散列函数计算出它在表中的位置
table[position] = value; // 将value参数添加到用散列函数计算出的对应的位置上
}; this.get = function(key) {
return table[loseloseHashCode(key)]; //loseloseHashCode(key)会返回值的位置
}; this.remove = function(key) {
table[loseloseHashCode(key)] = undefined;
}; /*解决冲突方法一:分离链接*/ // 为了实现一个使用了分离链接的HashTable实例,我们需要一个新的辅助类来表示将要加入LinkedList实例的元素
var ValuePair = function(key, value) {
this.key = key;
this.value = value;
} // 分离链接:重写put方法
this.put = function(key, value) {
var position = loseloseHashCode(key);
// 如果这个位置是第一次被加入元素,我们会在这个位置上初始化一个LinkedList类的实例
if (table[position] == undefined) {
table[position] = new LinkedList();
}
table[position].append(new ValuePair(key, value));
} // 分离链接:重写get方法
this.get = function(key) {
var position = loseloseHashCode(key); if (table[position] !== undefined) { //遍历链表来寻找键/值
var current = 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;
} // 分离链接:重写remove方法
this.remove = function(key) {
var position = 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] = undefined;
}
return true; // 返回true表示这个元素已经被移除
}
} return false; // 返回false表示这个元素在散列表中不存在
} /*解决冲突方法二:线性探查*/
var ValuePair = function(key, value) {
this.key = key;
this.value = value;
} // 线性探查:重写put方法
this.put = function(key, value) {
var position = loseloseHashCode(key); if (table[position] == undefined) {
table[position] = new ValuePair(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 === key) {
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 === key) {
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;
}
} HashTable.js

HashTable.js

参考书籍:《学习JavaScript数据结构与算法》

JavaScript数据结构——集合、字典和散列表的更多相关文章

  1. JavaScript数据结构——集合的实现与应用

    与数学中的集合概念类似,集合由一组无序的元素组成,且集合中的每个元素都是唯一存在的.可以回顾一下中学数学中集合的概念,我们这里所要定义的集合也具有空集(即集合的内容为空).交集.并集.差集.子集的特性 ...

  2. 为什么我要放弃javaScript数据结构与算法(第七章)—— 字典和散列表

    本章学习使用字典和散列表来存储唯一值(不重复的值)的数据结构. 集合.字典和散列表可以存储不重复的值.在集合中,我们感兴趣的是每个值本身,并把它作为主要元素.而字典和散列表中都是用 [键,值]的形式来 ...

  3. JavaScript数据结构——字典和散列表的实现

    在前一篇文章中,我们介绍了如何在JavaScript中实现集合.字典和集合的主要区别就在于,集合中数据是以[值,值]的形式保存的,我们只关心值本身:而在字典和散列表中数据是以[键,值]的形式保存的,键 ...

  4. Python:说说字典和散列表,散列冲突的解决原理

    散列表 Python 用散列表来实现 dict.散列表其实是一个稀疏数组(总是有空白元素的数组称为稀疏数组).在一般书中,散列表里的单元通常叫做表元(bucket).在 dict 的散列表当中,每个键 ...

  5. 《数据结构》C++代码 散列表

    散列表,又名哈希表.Hash表.这是一个神奇的数据结构,它的复杂度是常数级别,由于我非常喜欢这个数据结构,在此简单介绍一下. (没有学过Hash表的同学,我推荐一个教程:http://www.cnbl ...

  6. Javascript中的字典和散列

    function Dictionary() { var items={}; this.set=function (key,value) { items[key]=value; }; this.remo ...

  7. js数据结构与算法——字典与散列表

    <script> //创建字典 function Dictionary(){ var items = {}; this.set = function(key,value){ //向字典添加 ...

  8. JavaScript数据结构-10.字典

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  9. JavaScript 散列表(HashTable)

    TypeScript方式实现源码 // 特性: // 散列算法的作用是尽可能快地在数据结构中找到一个值. 在之前的章节中, 你已经知道如果 // 要在数据结构中获得一个值(使用get方法) ,需要遍历 ...

随机推荐

  1. Hadoop作业JVM堆大小设置优化 [转]

    前段时间,公司Hadoop集群整体的负载很高,查了一下原因,发现原来是客户端那边在每一个作业上擅自配置了很大的堆空间,从而导致集群负载很高.下面我就来讲讲怎么来现在客户端那边的JVM堆大小的设置.我们 ...

  2. .NET C#使用微信公众号登录网站

    适用于:本文适用于有一定微信开发基础的用户 引言:花了300大洋申请了微信公众平台后,发现不能使用微信公众号登录网站(非微信打开)获得微信帐号.仔细研究后才发现还要再花300大洋申请微信开放平台才能接 ...

  3. bzoj 2049: [Sdoi2008]Cave 洞穴勘测

    #include<cstdio> #include<iostream> using namespace std; ][],n,m,fa[],st[]; ]; bool isro ...

  4. <input type="hidden" id="haha" name="wang" value="xiaodong" />

    jsp中一个隐藏的文本框,文本框里的值是:xiaodong id属性和name属性:就是在JavaScript中或者控制器中根据id或name属性取它的value的值 开发人员所需要,又不想让用户看到 ...

  5. 神奇的NOIP模拟赛 T2 LGTB 学分块

    LGTB 学分块 LGTB 最近在学分块,但是他太菜了,分的块数量太多他就混乱了,所以只能分成3 块今天他得到了一个数组,他突然也想把它分块,他想知道,把这个数组分成3 块,块可以为空.假设3 块各自 ...

  6. bzoj 1036 Tree Count

    题目大意:给出一棵树,每个点有一个权值,要求三种操作:1.修改某个点的权值,2.询问x到y路径上各点的权值最大值,3.询问x到y路径上各点的权值之和. #include <cstdio> ...

  7. 戴文的Linux内核专题:02源代码

    转自Linux中国 在下载并解压内核源代码后,用户可以看到许多文件夹和文件.尝试去找一个特定的文件或许是一个挑战.谢天谢地,源代码以一个特定的方式组织的.这使开发者能够轻松找到任何文件或者内核的一部分 ...

  8. 多线程第一次亲密接触 CreateThread与_beginthreadex本质区别

    本文将带领你与多线程作第一次亲密接触,并深入分析CreateThread与_beginthreadex的本质区别,相信阅读本文后你能轻松的使用多线程并能流畅准确的回答CreateThread与_beg ...

  9. 【转】Nginx+Tomcat+Memcached集群Session共享

    cookie是怎样工作的? 例 如,我们创建了一个名字为login的Cookie来包含访问者的信息,创建Cookie时,服务器端的Header如下面所示,这里假设访问者的注册名 是“Michael J ...

  10. matlab和C/C++混合编程--Mex (六)参数传递

    最近的项目需要matlab和C的混合编程,经过一番努力终于完成了项目要解决的问题.现在就将Mex的一些经验总结一下,当然只是刚刚开始,以后随着学习的深入继续添加.首先讲讲写Mex的一些常规规定,然后我 ...