这是第五周的练习题,上周忘记发啦,这周是复习 Dictionary 和 HashTable

下面是之前分享的链接:

欢迎关注我的 个人主页 && 个人博客 && 个人知识库 && 微信公众号“前端自习课”

本周练习内容:数据结构与算法 —— Dictionary 和 HashTable

这些都是数据结构与算法,一部分方法是团队其他成员实现的,一部分我自己做的,有什么其他实现方法或错误,欢迎各位大佬指点,感谢。

一、字典和散列表的概念

  1. 字典是什么?

  2. 字典和集合有什么异同?

  3. 什么是散列表和散列函数?

  4. 散列表的特点是什么?


解析:

  1. 字典是什么?

字典是一种以 键-值对 形式存储数据的数据格式,其中键名用来查询特定元素。

  1. 字典和集合有什么异同?

相同:都是用来存储不同元素的数据格式;

区别:集合是以 值-值 的数据格式存储,而字典是以 键-值 的数据格式存储。

  1. 什么是散列表和散列函数?

哈希表(Hash table,也叫散列表),是根据关键码值(·Key value·)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表

  1. 散列表的特点是什么?

特点:数组和链接优点的结合,查询速度非常的快,几乎是O(1)的时间复杂度,并且插入和删除也容易。

二、请实现一个字典

set(key,value):向字典中添加新元素。

delete(key):通过使用键值从字典中移除键值对应的值。

has(key):如果某个键值存在于这个字典中,则返回 true,否则返回 false。

get(key):使用键值查找对应的值并返回。

clear():删除字典中的所有元素。

size():返回字典包含的元素数量,与数组的 length 属性类似。

keys():将字典的所有键名以数组的形式返回。

values():将字典包含的所有数值以数组形式返回。

使用示例:

let dictionary = new Dictionary();

dictionary.set("Gandalf", "gandalf@email.com");
dictionary.set("John", "johnsnow@email.com");
dictionary.set("Tyrion", "tyrion@email.com"); console.log(dictionary.has("Gandalf"));
console.log(dictionary.size()); console.log(dictionary.keys());
console.log(dictionary.values());
console.log(dictionary.get("Tyrion")); dictionary.delete("John"); console.log(dictionary.keys());
console.log(dictionary.values());

提示:Web 端优先使用 ES6 以上的语法实现。


解析:

// 二、请实现一个字典
class Dictionary {
constructor(){
this.items = []
}
/**
* 向字典中添加新元素
* @param {*} key 添加的键名
* @param {*} value 添加的值
*/
set (key, value) {
if ( !key ) return new Error('请指定插入的key')
this.items[key] = value
} /**
* 查询某个键值存在于这个字典
* @param {*} key 查询的键名
* @return {Boolean} 是否存在
*/
has (key) {
return key in this.items
} /**
* 通过使用键值从字典中移除键值对应的值
* @param {*} key 移除的键名
* @return {Boolean} 是否移除成功
*/
delete (key) {
if(!key || !this.has(key)) return false
delete this.items[key]
return true
} /**
* 使用键值查找对应的值并返回
* @param {*} key 查找的键名
* @return {*} 查找的结果
*/
get (key) {
return this.has(key) ? this.items[key] : undefined
} /**
* 删除字典中的所有元素
*/
clear () {
this.items = {}
} /**
* 将字典的所有键名以数组的形式返回
* @return {Array} 所有键名的数组
*/
keys () {
return Object.keys(this.items)
} /**
* 将字典的所有键值以数组的形式返回
* @return {Array} 所有键值的数组
*/
values () {
let result = []
for(let k in this.items){
if(this.has[k]){
result.push(this.items[k])
}
}
return result
} /**
* 返回字典包含的元素数量
* @return {Number} 元素数量
*/
size () {
const values = this.values()
return values.length
}
}

三、请实现一个散列表

put(key,value):向散列表增加/更新一个新的项。

remove(key):根据键值从散列表中移除值。

get(key):根据键值检索到特定的值。

print():打印散列表中已保存的值。

散列表内部的散列算法:

function hashCode(key) {
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash += key.charCodeAt(i);
}
return hash % 37;
}

使用示例:

const hashTable = new HashTable();

hashTable.put("Gandalf", "gandalf@email.com");
hashTable.put("John", "johnsnow@email.com");
hashTable.put("Tyrion", "tyrion@email.com");
hashTable.print();

解析:

// 三、请实现一个散列表
class HashTable {
constructor(){
this.table = []
}
/**
* 散列函数
* @param {*} key 键名
*/
hashCode(key) {
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash += key.charCodeAt(i);
}
return hash % 37;
}
/**
* 向散列表增加/更新一个新的项
* @param {*} key 添加的键名
* @param {*} value 添加的值
*/
put (key, value) {
let position = this.hashCode(key)
this.table[position] = value
} /**
* 根据键值从散列表中移除值
* @param {*} key 移除的键名
* @return {Boolean} 是否成功移除
*/
remove (key) {
if ( !key ) return false
let position = this.hashCode(key)
this.table[position] = undefined
return true
} /**
* 根据键值检索到特定的值
* @param {*} key 查找的键名
* @return {*} 查找的值
*/
get (key) {
let position = this.hashCode(key)
return this.table[position]
} /**
* 打印散列表中已保存的值
* @return {*} 散列表的值
*/
print () {
return this.table
}
}

四、请利用之前已实现的链表,实现一个分离链接的散列表

分离链接是为散列表的每一个位置创建一个链表储存元素的方式来处理散列表中的冲突:

请实现新的散列表方法:

put(key,value):将 key 和value存在一个ValuePair对象中(即可定义一个包含keyvalue属性的ValuePair` 类),并将其加入对应位置的链表中。

get(key):返回键值对应的值,没有则返回 undefined

remove(key):从散列表中移除键值对应的元素。

print():打印散列表中已保存的值。

提示:先找到元素储存位置所对应的链表,再找到对应的值。

const hashTable = new HashTable();

hashTable.put("Gandalf", "gandalf@email.com");
hashTable.put("Tyrion", "tyrion@email.com");
hashTable.put("Aaron", "aaron@email.com");
hashTable.put("Ana", "ana@email.com");
hashTable.put("Mindy", "mindy@email.com");
hashTable.put("Paul", "paul@email.com"); hashTable.print(); console.log(hashTable.get("Tyrion"));
console.log(hashTable.get("Aaron")); hashTable.remove("Tyrion"); hashTable.print();

解析:

// 链表的实现代码省略 可以查看之前的代码
let ValuePair = function (key, value){
this.key = key
this.value = value
this.toString = function(){
return `[${this.key} - ${this.value}]`
}
}
class HashTable {
constructor(){
this.table = []
}
/**
* 散列函数
* @param {*} key 键名
*/
hashCode(key) {
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash += key.charCodeAt(i);
}
return hash % 37;
}
/**
* 向散列表增加/更新一个新的项
* @param {*} key 添加的键名
* @param {*} value 添加的值
*/
put (key, value) {
let position = this.hashCode(key)
if(this.table[position] == undefined){
this.table[position] = new LinkedList()
}
this.table[position].append(new ValuePair(key, value))
} /**
* 根据键值从散列表中移除值
* @param {*} key 移除的键名
* @return {Boolean} 是否成功移除
*/
remove (key) {
let position = this.hashCode(key)
if ( !key || this.table[position] === undefined ) return false
let current = this.table[position].getHead()
while(current.next){
if(current.element.key === key){
this.table[position].remove(current.element)
if(this.table[position].isEmpty){
this.table[position] = undefined
}
return true
}
current = current.next
}
} /**
* 根据键值检索到特定的值
* @param {*} key 查找的键名
* @return {*} 查找的值
*/
get (key) {
let position = this.hashCode(key)
if(!key || this.table[position] === undefined) return undefined
let current = this.table[position].getHead()
while(current.next()){
if(current.element.key === key){
return current.element.value
}
current = current.next
}
} /**
* 打印散列表中已保存的值
* @return {*} 散列表的值
*/
print () {
return this.table
}
}

五、实现一个线性探查的散列表

线性探查是解决散列表中冲突的另一种方法,当向表中某一个位置加入新元素的时候,如果索引为 index 的位置已经被占据了,就尝试 index+1 的位置。如果 index+1 的位置也被占据,就尝试 index+2,以此类推。

请实现散列表:

put(key,value):将 keyvalue 存在一个 ValuePair 对象中(即可定义一个包含 keyvalue 属性的 ValuePair 类)并分配到散列表。

get(key):返回键值对应的值,没有则返回 undefined

remove(key):从散列表中移除键值对应的元素。

提示:移除一个元素,只需要将其赋值为 undefined。

使用示例:

const hashTable = new HashTable();

hashTable.put("Gandalf", "gandalf@email.com");
hashTable.put("Tyrion", "tyrion@email.com");
hashTable.put("Aaron", "aaron@email.com");
hashTable.put("Ana", "ana@email.com");
hashTable.put("Mindy", "mindy@email.com");
hashTable.put("Paul", "paul@email.com"); hashTable.print(); console.log(hashTable.get("Tyrion"));
console.log(hashTable.get("Aaron")); hashTable.remove("Tyrion"); hashTable.print();

解析:

let ValuePair = function (key, value){
this.key = key
this.value = value
this.toString = function(){
return `[${this.key} - ${this.value}]`
}
}
class HashTable {
constructor(){
this.table = []
}
/**
* 散列函数
* @param {*} key 键名
*/
hashCode(key) {
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash += key.charCodeAt(i);
}
return hash % 37;
}
/**
* 向散列表增加/更新一个新的项
* @param {*} key 添加的键名
* @param {*} value 添加的值
*/
put (key, value) {
let position = this.hashCode(key)
if(this.table[position] == undefined){
this.table[position] = new ValuePair(key, value)
}else{
let index = ++position
while(this.table[index] !== undefined){
index ++
}
this.table[index] = new ValuePair(key, value)
}
} /**
* 根据键值从散列表中移除值
* @param {*} key 移除的键名
* @return {Boolean} 是否成功移除
*/
remove (key) {
let position = this.hashCode(key)
if( !key || this.table[position] === undefined ) return undefined
if(this.table[position].key === key){
this.table[index] = undefined
}else{
let index = ++position
while(
this.table[index] === undefined ||
this.table[index].key !== key
){
index ++
}
if(this.table[index].key === key){
this.table[index] = undefined
}
}
} /**
* 根据键值检索到特定的值
* @param {*} key 查找的键名
* @return {*} 查找的值
*/
get (key) {
let position = this.hashCode(key)
if( !key || this.table[position] === undefined ) return undefined
if(this.table[position].key === key){
return this.table[position].value
}else{
let index = ++position
while(
this.table[index] === undefined ||
this.table[index].key !== key
){
index ++
}
if(this.table[index].key === key){
return this.table[index].value
}
}
} /**
* 打印散列表中已保存的值
* @return {*} 散列表的值
*/
print () {
return this.table
}
}

下周预告

下周将练习 Tree 的题目。

每周一练 之 数据结构与算法(Dictionary 和 HashTable)的更多相关文章

  1. 每周一练 之 数据结构与算法(Set)

    这是第四周的练习题,五一放假结束,该收拾好状态啦. 下面是之前分享的链接: 1.每周一练 之 数据结构与算法(Stack) 2.每周一练 之 数据结构与算法(LinkedList) 2.每周一练 之 ...

  2. 每周一练 之 数据结构与算法(Tree)

    这是第六周的练习题,最近加班比较多,上周主要完成一篇 GraphQL入门教程 ,有兴趣的小伙伴可以看下哈. 下面是之前分享的链接: 1.每周一练 之 数据结构与算法(Stack) 2.每周一练 之 数 ...

  3. 每周一练 之 数据结构与算法(LinkedList)

    这是第三周的练习题,原本应该先发第二周的,因为周末的时候,我的母亲大人来看望她的宝贝儿子,哈哈,我得带她看看厦门这座美丽的城市呀. 这两天我抓紧整理下第二周的题目和答案,下面我把之前的也列出来: 1. ...

  4. 每周一练 之 数据结构与算法(Queue)

    这是第二周的练习题,这里补充下咯,五一节马上就要到了,自己的计划先安排上了,开发一个有趣的玩意儿. 下面是之前分享的链接: 1.每周一练 之 数据结构与算法(Stack) 2.每周一练 之 数据结构与 ...

  5. 每周一练 之 数据结构与算法(Stack)

    最近公司内部在开始做前端技术的技术分享,每周一个主题的 每周一练,以基础知识为主,感觉挺棒的,跟着团队的大佬们学习和复习一些知识,新人也可以多学习一些知识,也把团队内部学习氛围营造起来. 我接下来会开 ...

  6. 【算法】272-每周一练 之 数据结构与算法(Dictionary 和 HashTable)

    这是第五周的练习题,上周忘记发啦,这周是复习 Dictionary 和 HashTable. 下面是之前分享的链接: [算法]200-每周一练 之 数据结构与算法(Stack) [算法]213-每周一 ...

  7. 【算法】273-每周一练 之 数据结构与算法(Tree)

    这是第六周的练习题,最近加班比较多. 下面是之前分享的链接: [算法]200-每周一练 之 数据结构与算法(Stack) [算法]213-每周一练 之 数据结构与算法(LinkedList) [算法] ...

  8. Python数据结构与算法--List和Dictionaries

    Lists 当实现 list 的数据结构的时候Python 的设计者有很多的选择. 每一个选择都有可能影响着 list 操作执行的快慢. 当然他们也试图优化一些不常见的操作. 但是当权衡的时候,它们还 ...

  9. 《数据结构与算法JavaScript描述》

    <数据结构与算法JavaScript描述> 基本信息 作者: (美)Michael McMillan 译者: 王群锋 杜欢 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:9 ...

随机推荐

  1. (C#)WPF:.h(头文件)、.lib(静态链接库文件)和.dll(动态链接库文件)之间的区别与联系

    静态链接库(Lib)与动态链接库(DLL)的区别 静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件:动态链接就是把调用的函数所在文件模块(DLL)和 ...

  2. 力扣(LeetCode)删除排序链表中的重复元素 个人题解

    给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次. 这题思路比较简单,同样是快慢针的思路. 用一个整数类型val对应最新的只出现过一次的那个值, 如果节点的下一个节点的值和这个对应则不做别 ...

  3. ubuntu 16.04上源码编译glog和gflags 编写glog-config.cmake和gflags-config.cmake | compile glog and glags on ubuntu 16.04

    本文首发于个人博客https://kezunlin.me/post/977f5125/,欢迎阅读! compile glog and glags on ubuntu 16.04 Series comp ...

  4. cglib测试例子和源码详解

    目录 简介 为什么会有动态代理? 常见的动态代理有哪些? 什么是cglib 使用例子 需求 工程环境 主要步骤 创建项目 引入依赖 编写被代理类 编写MethodInterceptor接口实现类 编写 ...

  5. 线程中synchronized关键字和lock接口的异同

    一.synchronized关键字 1.可以用来修饰代码块 synchronized (this) { // 同步的关键字 this 表示当前线程对象 if (num == 0) { break; } ...

  6. Gemini.Workflow 双子工作流高级教程:数据库-设计文档

    数据库设计文档 数据库名:Workflow_New 序号 表名 说明 1 WF_Activity wf_Activity 2 WF_ActivityInstance wf_ActivityInstan ...

  7. React躬行记(14)——测试框架

    测试不仅可以发现和预防问题,还能降低风险.减少企业损失.在React中,涌现了多种测试框架,本节会对其中的Jest和Enzyme做详细的讲解. 一.Jest Jest是由Facebook开源的一个测试 ...

  8. 转帖:30多条mysql数据库优化方法,千万级数据库记录查询轻松解决

    地址:http://www.ihref.com/read-16422.html

  9. 这是一个测试 hello world

    第一次写博客,冒着生命危险尝试一下,发说说 搞了半天,发现原创文章在随笔发,我以为在文章目录发,白白在文章那里建了分类,太难了吧我

  10. docker数据目录迁移

    一.描述 docker默认的数据目录为/var/lib/docker,有的系统数据盘挂载在其他目录下,比如/home为数据盘,这时需要将docker数据目录更改,除了修改docker配置文件的方法,还 ...