JavaScript数据结构——集合、字典和散列表

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

序号 方法 说明
1add(value)向集合添加一个新的项
2remove(value)从集合移除一个值
3 has(value)如果值在结合中,返回 true,否则返回 false
4 clear ( )移除集合中的所有项
5 size ( )返回集合所包含元素的数量。与数组的 length 属性类似。
6 values ( ) 返回一个包含集合中所有值的数组。

集合的实现:

《JavaScript数据结构——集合、字典和散列表》
《JavaScript数据结构——集合、字典和散列表》

 1 function Set() {
 2     var items = {};
 3 
 4     /*this.has = function(value) {
 5         return value in items;    // in 操作符会在通过对象访问给定属性时返回true,无论该属性存在于实例中还是原型中
 6     }*/
 7 
 8     // better way
 9     this.has = function(value) {
10         return items.hasOwnProperty(value);    // hasOwnProperty()方法只在给定属性存在于对象实例中时,才会返回true
11     };
12 
13     this.add = function(value) {
14         if (!this.has(value)) {
15             items[value] = value;    //添加一个值的时候,把它同时作为键和值保存,因为这样有利于查找这个值
16             reutrn true;    // 返回true,表示添加了这个值
17         }
18         return false;    // 返回false,表示没有添加它
19     };
20 
21     this.remove = function(value) {
22         if (this.has(value)) {
23             delete items[value];    // 既然用对象来存储集合的items对象,就可以简单地使用delete操作符从items对象中移除属性
24             return true;
25         }
26         return false;
27     };
28 
29     this.clear = function() {
30         items = {};
31     };
32 
33     // size实现方法一(只能在现代浏览器中运行)
34     /*this.size = function() {
35         return Object.keys(items).length;    // Object类有一个keys方法,返回一个包含给定对象所有属性的数组
36     }*/
37 
38     // size实现方法二(可以在任何浏览器上运行)
39     this.size = function() {
40         var count = 0;
41         for (var prop in items) {    // for...in...用于枚举对象中的属性,包括实例和原型中的属性
42             if (items.hasOwnProperty(prop)) {    // 检查它们是否是对象自身的属性
43                 ++count;
44             }
45         }
46         return count;
47     };
48 
49     // values实现方法一,同size实现方法一
50     /*this.values = function() {
51         return Object.keys(items);
52     }*/
53 
54     // values实现方法二,同size实现方法二
55     this.values = function() {
56         var keys = [];
57         for (var key in items) {
58             keys.push(key);
59         }
60         return keys;
61     };
62 }

Set.js

 

(2)字典方法声明:

首先,使用
对象来表示集合。

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

字典的实现:

《JavaScript数据结构——集合、字典和散列表》
《JavaScript数据结构——集合、字典和散列表》

 1 function Dictionary() {
 2     var items = {};
 3 
 4     this.has = function(value) {    // 之所以要先实现该方法,是因为它会被set和remove等其他方法调用
 5         return key in items;
 6     };
 7 
 8     this.set = function(key, value) {
 9         items[key] = value;
10     };
11 
12     this.remove = function(key) {
13         if (this.has(key)) {
14             delete items[key];
15             return true;
16         }
17         return false;
18     };
19 
20     this.get = function(key) {
21         return this.has(key) ? items[key] : undefined;    // get方法会首先验证我们想要的值是否存在(通过查找key值)
22     };
23 
24     this.values = function() {
25         var values = [];
26         for (var k in items) {
27             if (this.has(k)) {
28                 values.push(items[k]);
29             }
30         }
31         return values;
32     };
33 
34     this.clear = function() {
35         items = {};
36     };
37 
38     this.size = function() {
39         return Object.keys(items).length;    // Object类有一个keys方法,返回一个包含给定对象所有属性的数组
40     };
41 
42     this.keys = function() {
43         return Object.keys(items);
44     };
45 
46     this.getItems = function() {
47         reutrn items;
48     };
49 }

Dictionary.js

 

(3)散列方法声明:

首先,使用
数组来表示集合。 三个基础方法:

序号 方法 说明
1put(key, value)向散列表增加一个新的项(也能更新散列表)
2remove(key)根据键值从散列表中移除值
3 get(key)返回根据键值检索到的特定的值

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

处理散列表的冲突:

有时候,一些键会有相同的散列值。不同的值在散列表中对应相同位置的时候,我们称其为
冲突。处理冲突有几种方法:分离链接、线性探查和双散列法。
分离链接:包括为散列表的每一个位置创建一个链表并将元素存储在里面。它是是解决冲突的最简单的方法,但是它在 HashTable 实例之外还需要额外的存储空间。
线性探查:当想向表中某个位置加入一个新元素的时候,如果索引为 index 的位置已经被占据了,就尝试 index+1 的位置。如果 index+1 的位置也被占据了,就尝试 index+2 的位置,以此类推。 一个表现良好的散列函数是由几个方面构成的:插入和检索元素的时间(即性能),当然也包括较低的冲突的可能。

散列表的实现:

《JavaScript数据结构——集合、字典和散列表》
《JavaScript数据结构——集合、字典和散列表》

  1 function HashTable() {
  2     var table = [];
  3 
  4     // 散列函数,是HashTable中的一个私有方法
  5     var loseloseHashCode = function(key) {
  6         var hash = 0;
  7         for (var i=0; i<key.length; i++) {
  8             hash += key.charCodeAt(i);    // 给定一个key参数,我们就能根据组成key的每个字符的ASCII码值的和得到一个数字
  9         }
 10         return hash % 37;    // 为了得到比较小的数值,我们会使用hash值和一个任意数做除法的余数
 11     };
 12 
 13     // 更好的散列函数(这并不是最好的散列函数,但这是最被社区推荐的散列函数之一)
 14     var djb2HashCode = function(key) {
 15         var hash = 5381;    // 初始化赋值为一个质数,大多数实现都使用5318
 16         for (var i=0; i<key.length; i++) {
 17             hash += hash * 33 + key.charCodeAt(i);    // 将hash与33相乘,当作一个魔力数
 18         }
 19         return hash % 1013;    // 将相加的和与另一个随机质数相除
 20     }
 21 
 22     this.put = function(key, value) {
 23         var position = loseloseHashCode(key);    // 给定一个键值,我们需要根据所创建的散列函数计算出它在表中的位置
 24         table[position] = value;    // 将value参数添加到用散列函数计算出的对应的位置上
 25     };
 26 
 27     this.get = function(key) {
 28         return table[loseloseHashCode(key)];    //loseloseHashCode(key)会返回值的位置
 29     };
 30 
 31     this.remove = function(key) {
 32         table[loseloseHashCode(key)] = undefined;
 33     };
 34 
 35 
 36     /*解决冲突方法一:分离链接*/
 37 
 38     // 为了实现一个使用了分离链接的HashTable实例,我们需要一个新的辅助类来表示将要加入LinkedList实例的元素
 39     var ValuePair = function(key, value) {
 40         this.key = key;
 41         this.value = value;
 42     }
 43 
 44     // 分离链接:重写put方法
 45     this.put = function(key, value) {
 46         var position = loseloseHashCode(key);
 47         // 如果这个位置是第一次被加入元素,我们会在这个位置上初始化一个LinkedList类的实例
 48         if (table[position] == undefined) {        
 49             table[position] = new LinkedList();
 50         }
 51         table[position].append(new ValuePair(key, value));
 52     }
 53 
 54     // 分离链接:重写get方法
 55     this.get = function(key) {
 56         var position = loseloseHashCode(key);
 57 
 58         if (table[position] !== undefined) {
 59 
 60             //遍历链表来寻找键/值
 61             var current = table[position].getHead();
 62 
 63             while (current.next) {
 64                 if (current.element.key === key) {
 65                     return current.element.value;
 66                 }
 67                 current = current.next;
 68             }
 69 
 70             // 检查元素在链表第一个或最后一个节点的情况
 71             if (current.element.key === key) {
 72                 return current.element.value;
 73             }
 74         }
 75         return undefined;
 76     }
 77 
 78     // 分离链接:重写remove方法
 79     this.remove = function(key) {
 80         var position = loseloseHashCode(key);
 81 
 82         if (table[position] !== undefined) {
 83 
 84             var current = table[position].getHead();
 85             while (current.next) {
 86                 if (current.element.key === key) {
 87                     table[position].remove(current.element);
 88                     if (table[position].isEmpty()) {
 89                         table[position] = undefined;
 90                     }
 91                     return true;
 92                 }
 93                 current = current.next;
 94             }
 95 
 96             // 检查是否为第一个或最后一个元素
 97             if (current.element.key === key) {
 98                 table[position].remove(current.element);
 99                 if (table[position].isEmpty()) {
100                     table[position] = undefined;
101                 }
102                 return true;    // 返回true表示这个元素已经被移除
103             }
104         }
105 
106         return false;    // 返回false表示这个元素在散列表中不存在
107     }
108 
109 
110 
111     /*解决冲突方法二:线性探查*/
112     var ValuePair = function(key, value) {
113         this.key = key;
114         this.value = value;
115     }
116 
117     // 线性探查:重写put方法
118     this.put = function(key, value) {
119         var position = loseloseHashCode(key);
120         
121         if (table[position] == undefined) {
122             table[position] = new ValuePair(key, value);
123         } else {
124             var index = ++position;
125             while (table[index] != undefined) {
126                 index++;
127             }
128             table[index] = new ValuePair(key, value);
129         }
130     }
131 
132     // 线性探查:重写get方法
133     this.get = function(key) {
134         var position = loseloseHashCode(key);
135 
136         if (table[position] !== undefined) {
137             if (table[position].key === key) {
138                 return table[position].value;
139             } else {
140                 var index = ++position;
141                 while (table[index] === undefined || table[index].key !== key) {
142                     index++;
143                 }
144                 if (table[index].key === key) {    //只是为了确认一下
145                     return table[index].value;
146                 }
147             }
148         }
149         return undefined;
150     }
151 
152     // 分离链接:重写remove方法
153     this.remove = function(key) {
154         var position = loseloseHashCode(key);
155 
156         if (table[position] !== undefined) {
157             if (table[position].key === key) {
158                 table[index] = undefined;
159             } else {
160                 var index = ++position;
161                 while (table[index] === undefined || table[index].key !== key) {
162                     index++;
163                 }
164                 if (table[index].key === key) {    //只是为了确认一下
165                     table[index] = undefined;
166                 }
167             }
168         }
169         return undefined;
170     }
171 }
172 
173 HashTable.js

HashTable.js

 

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

 

    原文作者:算法小白
    原文地址: https://www.cnblogs.com/Ruth92/p/5296606.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞