【算法】272-每周一练 之 数据结构与算法(Dictionary 和 HashTable)
这是第五周的练习题,上周忘记发啦,这周是复习 Dictionary 和 HashTable。
下面是之前分享的链接:
【算法】200-每周一练 之 数据结构与算法(Stack)
【算法】213-每周一练 之 数据结构与算法(LinkedList)
【算法】214-每周一练 之 数据结构与算法(Queue)
欢迎关注我的 个人主页 && 个人博客 && 个人知识库 && 微信公众号“前端自习课”
本周练习内容:数据结构与算法 —— Dictionary 和 HashTable
这些都是数据结构与算法,一部分方法是团队其他成员实现的,一部分我自己做的,有什么其他实现方法或错误,欢迎各位大佬指点,感谢。
一、字典和散列表的概念
字典是什么?
字典和集合有什么异同?
什么是散列表和散列函数?
散列表的特点是什么?
解析:
字典是什么?
字典是一种以 键-值对 形式存储数据的数据格式,其中键名用来查询特定元素。
字典和集合有什么异同?
相同:都是用来存储不同元素的数据格式;
区别:集合是以 值-值 的数据格式存储,而字典是以 键-值 的数据格式存储。
什么是散列表和散列函数?
哈希表( Hashtable,也叫散列表),是根据关键码值(·Key value·)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
散列表的特点是什么?
特点:数组和链接优点的结合,查询速度非常的快,几乎是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 对象中(即可定义一个包含key 和value 属性的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):将 key 和 value 存在一个 ValuePair 对象中(即可定义一个包含 key 和 value 属性的 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 的题目。
每一个“在看”,都是对我最大的肯定!
【算法】272-每周一练 之 数据结构与算法(Dictionary 和 HashTable)的更多相关文章
- 每周一练 之 数据结构与算法(LinkedList)
这是第三周的练习题,原本应该先发第二周的,因为周末的时候,我的母亲大人来看望她的宝贝儿子,哈哈,我得带她看看厦门这座美丽的城市呀. 这两天我抓紧整理下第二周的题目和答案,下面我把之前的也列出来: 1. ...
- 每周一练 之 数据结构与算法(Set)
这是第四周的练习题,五一放假结束,该收拾好状态啦. 下面是之前分享的链接: 1.每周一练 之 数据结构与算法(Stack) 2.每周一练 之 数据结构与算法(LinkedList) 2.每周一练 之 ...
- 每周一练 之 数据结构与算法(Queue)
这是第二周的练习题,这里补充下咯,五一节马上就要到了,自己的计划先安排上了,开发一个有趣的玩意儿. 下面是之前分享的链接: 1.每周一练 之 数据结构与算法(Stack) 2.每周一练 之 数据结构与 ...
- 每周一练 之 数据结构与算法(Tree)
这是第六周的练习题,最近加班比较多,上周主要完成一篇 GraphQL入门教程 ,有兴趣的小伙伴可以看下哈. 下面是之前分享的链接: 1.每周一练 之 数据结构与算法(Stack) 2.每周一练 之 数 ...
- 每周一练 之 数据结构与算法(Dictionary 和 HashTable)
这是第五周的练习题,上周忘记发啦,这周是复习 Dictionary 和 HashTable. 下面是之前分享的链接: 1.每周一练 之 数据结构与算法(Stack) 2.每周一练 之 数据结构与算法( ...
- 每周一练 之 数据结构与算法(Stack)
最近公司内部在开始做前端技术的技术分享,每周一个主题的 每周一练,以基础知识为主,感觉挺棒的,跟着团队的大佬们学习和复习一些知识,新人也可以多学习一些知识,也把团队内部学习氛围营造起来. 我接下来会开 ...
- 【算法】273-每周一练 之 数据结构与算法(Tree)
这是第六周的练习题,最近加班比较多. 下面是之前分享的链接: [算法]200-每周一练 之 数据结构与算法(Stack) [算法]213-每周一练 之 数据结构与算法(LinkedList) [算法] ...
- 数据结构和算法(What Why How)
数据结构和算法是什么? 从广义上讲,数据结构就是指一组数据的存储结构.算法就是操作数据的一组方法. 从狭义上讲,是指某些著名的数据结构和算法,比如队列.堆.栈.二分查找.动态规划等. 数据结构和算法有 ...
- JavaScript 数据结构与算法之美 - 非线性表中的树、堆是干嘛用的 ?其数据结构是怎样的 ?
1. 前言 想学好前端,先练好内功,内功不行,就算招式练的再花哨,终究成不了高手. 非线性表(树.堆),可以说是前端程序员的内功,要知其然,知其所以然. 笔者写的 JavaScript 数据结构与算法 ...
随机推荐
- nyoj 52-无聊的小明 (模拟, SET)
52-无聊的小明 内存限制:64MB 时间限制:3000ms Special Judge: No accepted:1 submit:3 题目描述: 这天小明十分无聊,没有事做,但不甘于无 ...
- A Lot of Games(Trie树 + 博弈)
题目链接:http://codeforces.com/contest/455/problem/B 题意:n, k 分别表示 字符串组数 和 比赛次数. 从一个空单词开始, a,b二人分别轮流往单词后 ...
- Docker从入门到掉坑(三):容器太多,操作好麻烦
前边的两篇文章里面,我们讲解了基于docker来部署基础的SpringBoot容器,如果阅读本文之前没有相关基础的话,可以回看之前的教程. Docker 从入门到掉坑 Docker从入门到掉坑(二): ...
- windows 10 上源码编译OpenCV并支持CUDA | compile opencv with CUDA support on windows 10
本文首发于个人博客https://kezunlin.me/post/6580691f/,欢迎阅读! compile opencv with CUDA support on windows 10 Ser ...
- 20191031-3 beta week 1/2 Scrum立会报告+燃尽图 01
此作业要求参见[https://edu.cnblogs.com/campus/nenu/2019fall/homework/9911] 一.小组情况 队名:扛把子 组长:孙晓宇 组员:宋晓丽 梁梦瑶 ...
- 【笔记】总结Springboot和Vue前后端分离的跨域问题
跨域一直是个很玄学的问题,SSM的时候又得前后端一起配置,sb的时候又不用. 前端 axios普通get请求 submitForm() { var v=this; this.$axios({ meth ...
- Asp.Net Core下使用swagger生成api文档
目录 一.前期准备 二.配置Swagger 三.参考 .Net Core中有两个集成NSwag的包,分别为Swashbuckle和NSwag.两者的配置大同小异.这里以NSwag为例. 一.前期准备 ...
- ehcache同步原理
最近研究ehcache同步时发现一个问题: 现有A.B两个服务器,由A服务器向B服务器同步信息,采用RMI方式手动方式进行同步 配置信息如下: <?xml version="1.0&q ...
- 附011.Kubernetes-DNS及搭建
一 Kubernetes DNS介绍 1.1 Kubernetes DNS发展 作为服务发现机制的基本功能,在集群内需要能够通过服务名对服务进行访问,因此需要一个集群范围内的DNS服务来完成从服务名到 ...
- xmake从入门到精通8:切换编译模式
xmake是一个基于Lua的轻量级现代化c/c++的项目构建工具,主要特点是:语法简单易上手,提供更加可读的项目维护,实现跨平台行为一致的构建体验. 本文我们会详细介绍下如何在项目构建过程中切换deb ...