集合
集合比较常见的实现方式是哈希表;
集合通常是由一组无序的,不能重复的元素构成
ES6中已经由Set类表示集合,这里自己重新实现封装
集合的封装
//封装集合类
function Set() {
//属性 js的object对象
this.items = {};
//1.add()方法
Set.prototype.add = function(value) {
//判断集合中是否包含该元素
//为什么这里是this.has而不是this.items.has
if (this.has(value)) {
return false;
}
this.items[value] = value;
return true;
}
//2.has()方法
Set.prototype.has = function(value) {
return this.items.hasOwnProperty(value);
}
//3.remove()方法
Set.prototype.remove = function(value) {
//判断该集合中是否包含该元素
//为什么这里是this.has而不是this.items.has
if (!this.has(value)) {
return false;
} else {
delete this.items[value];
return true;
}
}
//4.clear()方法
Set.prototype.clear = function() {
this.items = {};
}
//5.size()方法
Set.prototype.size = function() {
return Object.keys(this.items).length;
}
//6.values()获取集合中所有的值
Set.prototype.values = function() {
return Object.keys(this.items);
}
}
集合间的操作
- 并集
//集合间的操作
//并集
Set.prototype.union = function(otherSet) {
//创建新的集合
var unionSet = new Set();
//取出A集合中的所有元素并添加到新集合中
var values = this.values();
for (var i = 0; i < values.length; i++) {
unionSet.add(values[i]);
}
//取出B集合的元素并判断是否需要加到新集合中
values = otherSet.values();
for (var i = 0; i < values.length; i++) {
unionSet.add(values[i]);
}
return unionSet;
}
- 交集
//交集
Set.prototype.intersection = function(otherSet) {
var intersectionSet = new Set();
//从A中取出元素判断是否同时存在于B中
//存在则存入新的集合中
var values = this.values();
for (var i = 0; i < values.length; i++) {
var item = values[i];
if (otherSet.has(item)) {
intersectionSet.add(item);
}
}
return intersectionSet;
}
- 差集
//差集
Set.prototype.difference = function(otherSet) {
var differenceSet = new Set();
var values = this.values();
for (var i = 0; i < values.length; i++) {
var item = values[i];
if (!otherSet.has(item)) {
differenceSet.add(item);
}
}
return differenceSet;
}
- 子集
//子集
Set.prototype.subset = function(otherSet) {
var values = this.values();
for (var i = 0; i < values.length; i++) {
var item = values[i];
if (!otherSet.has(item)) {
return false;
}
}
return true;
}
字典
字典的主要特点是一一对应的关系;
存储的是键值对,没有顺序;
字典和映射的关系:有些编程语言中称这种映射关系为字典,有些称这种映射关系为Map
- 属性
this.items = {};
- 添加
//添加
Dictionary.prototype.set = function(key, value) {
this.items[key] = value;
}
- 判断是否有某个key
//判断字典中是否有某个key
Dictionary.prototype.has = function(key) {
return this.items.hasOwnProperty(key);
}
- 删除
//删除
Dictionary.prototype.remove = function(key) {
if (!this.has(key)) {
return false;
} else {
delete this.items[key];
return true;
}
}
- 根据key获取value
//根据key获取value
Dictionary.prototype.get = function(key) {
return this.has(key) ? this.items[key] : undefined;
}
- 获取所有的keys
//获取所有的keys
Dictionary.prototype.keys = function() {
return Object.keys(this.items);
}
- 获取所有的value
//获取所有的value
Dictionary.prototype.has = function(key) {
return Object.value(this.items);
}
- size
//size
Dictionary.prototype.size = function(key) {
return this.keys().length;
}
- clear
//clear
Dictionary.prototype.clear = function(key) {
this.items = {};
}
哈希表
哈希表的结构就是数组,但是它神奇的地方在于对下标值的一种变换,这种变换称之为哈希函数,通过哈希函数可以获取到HashCode
哈希表通常是基于数组进行实现的,但是相对于数组,它有很多的优势:
提供非常快速的插入,删除,查找操作;
哈希表的速度比树还要快
不足:
哈希表中的数据是没有顺序的,所以不能以一种固定的方式(比如从小到大)来遍历其中的元素;
哈希表中的key是不允许重复的,不能放置相同的key,用于保存不同的元素
解决冲突的两种方案
- 链地址法(拉链法)
- 开放地址法
开放地址法就是寻找空白的位置来放置冲突的数据项,有三种方法:
线性探测
线性探测就是从index的位置+1开始线性的查找空位置
二次探测
再哈希法
设计好的哈希函数应具备的优点:快速的计算和均匀的分布
哈希表的封装:
首先封装哈希函数:
//将字符串转换成比较大的数字
//将大的数字hashCode压缩到size范围之内
HashTable.prototype.hashFunc = function(str, size) {
var hashCode = 0;
//霍纳法则,计算hashCode的值
for (var i = 0; i < str.length; i++) {
// str.charCodeAt(i) 获取字符串的unicode编码;
hashCode = 37 * hashCode + str.charCodeAt(i);
}
//取余操作
var index = hashCode % size;
return index;
}
判断质数:
常规算法:
//常规算法
// function isPrime(number) {
// for (var i = 2; i < number; i++) {
// if (number % i == 0) {
// return false;
// }
// }
// return true;
// }
更高效的算法:
HashTable.prototype.isPrime = function(number) {
//获取number的平方根
var temp = parseInt(Math.sqrt(number));
for (var i = 2; i <= temp; i++) {
if (number % i == 0) {
return false;
}
}
return true;
}
获取质数的方法:
//获取质数的方法
HashTable.prototype.getPrime = function(number) {
while (!this.isPrime(number)) {
number++;
}
return number;
}
扩容:
//扩容
HashTable.prototype.resize = function(newlimit) {
//保存旧的数组的内容
var oldstorage = this.storage;
//重置所有属性
this.storage = [];
this.count = 0;
this.limit = newlimit;
//遍历pldstorage中所有的bucket
for (var i = 0; i < oldstorage.length; i++) {
//取出每一个bucket
var bucket = oldstorage[i];
//如果当前bucket是空则不继续直接进行下一次遍历
if (bucket == null) {
continue;
}
//如果不为空 则取出里面的内容并添加到新的容器中
for (var j = 0; j < bucket.length; j++) {
var turple = bucket[j];
this.put(turple[0], turple[1]);
}
}
}
哈希表的封装:
function HashTable() {
//属性
//存储元素的数组
this.storage = [];
//当前哈希表中存放的元素个数
this.count = 0;
//总长度
this.limit = 7;
//方法
//将字符串转换成比较大的数字
//将大的数字hashCode压缩到size范围之内
HashTable.prototype.hashFunc = function(str, size) {
var hashCode = 0;
//霍纳法则,计算hashCode的值
for (var i = 0; i < str.length; i++) {
// str.charCodeAt(i) 获取字符串的unicode编码;
hashCode = 37 * hashCode + str.charCodeAt(i);
}
//取余操作
var index = hashCode % size;
return index;
}
//插入和修改操作
HashTable.prototype.put = function(key, value) {
//根据key值获取index
var index = this.hashFunc(key, this.limit);
//根据index取出相应位置的桶
var bucket = this.storage[index];
//判断是否为空
if (bucket == null) {
bucket = [];
this.storage[index] = bucket;
}
//判断是否是修改数据
for (var i = 0; i < bucket.length; i++) {
var tuple = bucket[i];
if (tuple[0] == key) {
tuple[1] = value;
return;
}
}
//进行添加操作
bucket.push([key, value]);
this.count += 1;
//判断是否需要扩容
if (this.count > this.limit * 0.75) {
var newSize = this.limit * 2;
var newPrime = this.getPrime(newSize);
this.resize(newPrime);
}
}
//根据key获取value
HashTable.prototype.get = function(key) {
//根据key值获取index
var index = this.hashFunc(key, this.limit);
//根据index取出相应位置的桶
var bucket = this.storage[index];
if (bucket == null) {
return null;
} else {
for (var i = 0; i < bucket.length; i++) {
var tuple = bucket[i];
if (tuple[0] == key) {
return tuple[1];
}
}
return null;
}
}
//删除方法
HashTable.prototype.remove = function(key) {
//根据key值获取index
var index = this.hashFunc(key, this.limit);
//根据index取出相应位置的桶
var bucket = this.storage[index];
if (bucket == null) {
return null;
} else {
for (var i = 0; i < bucket.length; i++) {
var tuple = bucket[i];
if (tuple[0] == key) {
bucket.splice(i, 1);
this.count -= 1;
return tuple[1];
//缩小容量
if (this.limit > 7 && this.count < this.limit * 0.25) {
var newSize = Math.floor(this.limit / 2);
var newPrime = this.getPrime(newSize);
this.resize(newPrime);
}
}
}
return null;
}
}
//isEmpty()方法
HashTable.prototype.isEmpty = function() {
return this.count == 0;
}
//size()方法
HashTable.prototype.size = function() {
return this.count;
}
//扩容
HashTable.prototype.resize = function(newlimit) {
//保存旧的数组的内容
var oldstorage = this.storage;
//重置所有属性
this.storage = [];
this.count = 0;
this.limit = newlimit;
//遍历pldstorage中所有的bucket
for (var i = 0; i < oldstorage.length; i++) {
//取出每一个bucket
var bucket = oldstorage[i];
//如果当前bucket是空则不继续直接进行下一次遍历
if (bucket == null) {
continue;
}
//如果不为空 则取出里面的内容并添加到新的容器中
for (var j = 0; j < bucket.length; j++) {
var turple = bucket[j];
this.put(turple[0], turple[1]);
}
}
}
//判断质数
//更高效的方法
HashTable.prototype.isPrime = function(number) {
//获取number的平方根
var temp = parseInt(Math.sqrt(number));
for (var i = 2; i <= temp; i++) {
if (number % i == 0) {
return false;
}
}
return true;
}
//常规算法
// function isPrime(number) {
// for (var i = 2; i < number; i++) {
// if (number % i == 0) {
// return false;
// }
// }
// return true;
// }
//获取质数的方法
HashTable.prototype.getPrime = function(number) {
while (!this.isPrime(number)) {
number++;
}
return number;
}
}