目录
哈希算法思想
利用对象属性作为key值,通过唯一个key值,访问对应的存储的值。相当于形成映射关系,便于直接查找值。
例如一个数组有a、b、c三数,我们可以借用一个对象obj,让obj["a"]=1、obj["b"]=1、obj["c"]=1,当我们需要查找该数组中是否包含d的时候就不需要遍历每个数,只需要判断obj["d"]是否等于1即可。
题目
两数之和
给出一个整型数组 numbers 和一个目标值 target,请在数组中找出两个加起来等于目标值的数的下标,返回的下标按升序排列。
(注:返回的数组下标从1开始算起,保证target一定可以由数组里面2个数字相加得到)
输入:[3,2,4],6
返回值:[2,3]
说明:因为 2+4=6 ,而 2的下标为2 , 4的下标为3 ,又因为 下标2 < 下标3 ,所以返回[2,3]
输入:[20,70,110,150],90
返回值:[1,2]
说明:20+70=90
原题链接
解析
核心思想
由于需要判断两数相加等于目标值的,所以可以采用减法,用目标值减去当前数对数组的每一位判断是否满足条件,得出下面注释的方法。
通过hash算法优化,将目标值减去当前数作为属性,当前数的序号作为值,当数组中有对应属性的值时即满足相加为目标值,返回序号。
答案
// 不使用hash算法,直接穷举
// function twoSum(numbers, target) {
// let tmp = -1
// for (let i = 0; i < numbers.length; i++) {
// tmp = numbers.findIndex((v, index) => v === target - numbers[i] && i !== index)
// if (tmp !== -1) {
// return [i + 1, tmp + 1]
// }
// }
// }
function twoSum(numbers, target) {
let map = {}
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] in map) {
return [map[numbers[i]], i + 1];
} else {
map[target - numbers[i]] = i + 1;
}
}
}
给定差值的组合
hash算法
题目描述
给定一个数组,每个元素的值时唯一的,找出其中两个元素相减等于给定差值diff的所有不同组合的个数。
组合是无序的,如:(1,4)和(4,1)表示的是同一个组合。
解答要求
时间限制:1000ms,内存限制:256MB
输入
输入三行:
第一行为一个整数,表示给定差值diff;范围[-50000,50000]
第二行也为一个数字,表示数组的长度;范围[2,102400]
第三行为该数组,由单个空格分割的一组数字组成;其中元素的值范围[-20,102400]
用例保证第三行数字和空格组成的字符串长度不超过64999
输出
1个整数,表示满足要求的不同组合的个数
样例
输入样例1
3
5
1 3 2 5 4
输出样例1
2
提示样例1
数组为[1 3 2 5 4],差值diff为3,其中4-1=3,5-2=3,共2个组合满足要求,因此输出2
输入样例2
-1
3
1 2 3
输出样例2
2
提示样例2
其中1-2=-1,2-3=-1,共2个组合满足要求,因此输出2。
解析
注意方法一中时间复杂度为n²,因为forEach中判断了一轮,includes中对数组同样也判断了一轮。而方法二直接用哈希算法,通过映射的值去查找,去掉了第二轮的判断,时间复杂度为n。
核心思想
方法一:先把数组从大到小排序,然后利用forEach对数组的每一位进行判断,减去绝对值diff后用includes函数判断是否存在于原数组中。
方法二:利用对象的属性,每次添加一个数,使hash对象的属性和值与数相同,直接通过对象的属性名(该数减去diff或加上diff)对应的值是否为undefined来判断差值结果是否存在于原数组中。
答案
//不使用哈希排序的方法,输入数组过长时,会超时。
// const proc = (arr, diff) => {
// let sum = 0
// arr.sort((a, b) => b - a)
// diff = Math.abs(diff)
// arr.forEach((v1, i1) => {
// if (arr.length > i1 + 2) {
// if (arr.includes(v1 - diff, i1 + 1)) {
// sum++
// }
// }
// })
// return sum
// }
const proc = (arr, diff) => {
if (diff === 0) {
return 0
}
let sum = 0
let hash = {}
for (let i = 0; i < arr.length; i++) {
const num1 = arr[i];
hash[num1] = num1;
if (hash[num1 - diff] != undefined) {
sum++
}
if (hash[num1 + diff] != undefined) {
sum++
}
}
return sum
}
console.log(proc([1, 3, 2, 5, 4], 3))
最佳路径
hash算法、递归、DFS(深度优先算法)
题目描述
给定单板间的有向连接关系:如单板101到单板102存在连接,权值为10,其连接关系描述为101 102 10;单板102到单板101存在连接,权值为6,其连接关系描述为:102 101 6.
基于单板间的有向连接关系,从起始单板到目的单板可能有0或多条可达的有向路径,某条路径权值是该路径上各连接的权值之和。
给定夺组格式为起始单板目的单板的查询命令,请依次计算并返回其最佳路径权值;若某条最佳路径不存在,则该条最佳路径权值返回-1
最佳路径的规则如下:
经过的连接个数最小;
若符合规则1的路径存在多条,选择权值最小的;
解答要求
时间限制:1000ms,内存限制:256MB
输入
首行为两个正整数n m
n是单板间连接关系的数量,取值范围:[1,500]
m是查询命令的个数,取值范围:[1,500]
接下来n行,每行是连接关系,单板编号取值范围:[1,999],权值取值范围:[1,100];都为正整数
接下来m行,每行是需要查询最佳路径权值的起始单板、目的单板的编号。
输出
m行,每行一个整数,表示对应起始单板、目的单板的最佳路径权值的查询结果
样例
输入样例1
2 2
100 101 10
102 101 5
100 101
102 100
输出样例1
10
-1
提示样例1
输出第一行:单板100到单板101,最小经过1次跳转就可达,其权值为10,输出10输出第二行:单板102到单板100,没有路径可达,因此输出-1
解析
1.注意这里用到es6中set集合进行判断是否为走过的路径,然后由于是深度优先,且每次调用后都会删除添加的路径,所以不用去浅复制。
2.注意当大于已知解法经过的线段步数时可以不用继续走了。
核心思想
每次走一步,将以下一步为起点的线段存入数组中递归走多步
递归步骤
1.根据递归思想首找到f(x)表示什么,f(x)要表示当前位置,目标位置,经过线段次数,权重和、走过的位置(为避免重复)。
2.找到f(x)和f(x-1)的联系, f(source, target, count, sum, set),其中source根据走的线段更新,目标位置不变,count每次加一,sum加上走的线段权重,set中添加走过线段的终点。f(x)到f(x-1)根据以source为起点的线段条数走多步。
3.找出口,当走的步数大于已知解法的经过线段次数时返回,小于时,source和target相等即找到了正确路径。
答案
function dfs(source, target, count, sum, set) {
//countGlobal记录了已有成功的经过的线段次数,如果大于就不进行运算了,减少多余的次数
if (countGlobal >= count) {
//出口,当起点为终点时即找到了
if (source == target) {
//如果经过有向线段小于已有解法,则直接赋值
if (count < countGlobal) {
countGlobal = count
sumGlobal = sum
}
//如果等于,则比较权值weight
if (count === countGlobal) {
sumGlobal = Math.min(sumGlobal, sum)
}
return
}
//没有以当前source为起点的线段了,即走到终点了
if (!pathMap[source]) {
return
}
const nextList = pathMap[source]
//以source为起点的线段有多条则需要每条都走一遍
for (let i = 0; i < nextList.length; i++) {
const { snkBoard, weight } = nextList[i]
//判断路径是否走过了
if (!set.has(snkBoard)) {
//添加当前走的路径
set.add(snkBoard)
dfs(snkBoard, target, count + 1, sum + weight, set)
//删除之前走的路径,用于下次循环
set.delete(snkBoard)
}
}
}
}
const pathMap = {}
let countGlobal = 0
let sumGlobal = 0
const getBestRoute = (boardsList, connnectionsList) => {
//利用hash算法来搜索指定起点的路径
for (let con of connnectionsList) {
if (!pathMap[con.srcBoard]) {
pathMap[con.srcBoard] = []
}
pathMap[con.srcBoard].push({ snkBoard: con.snkBoard, weight: con.weight })
}
const res = []
//对每个指定的路径进行计算
for (let pair of boardsList) {
let { srcBoard: source, snkBoard: target } = pair
//记录经过的有向线段个数
countGlobal = Number.MAX_VALUE
//记录最小的weght总量
sumGlobal = Number.MAX_VALUE
//记录经过了的点,避免重复走
const set = new Set()
set.add(source)
dfs(source, target, 0, 0, set)
if (sumGlobal === Number.MAX_VALUE) {
res.push(-1)
} else {
res.push(sumGlobal)
}
}
return res
}
console.log(getBestRoute(
[{ srcBoard: 100, snkBoard: 101 }, { srcBoard: 102, snkBoard: 100 }],
[{ srcBoard: 100, snkBoard: 101, weight: 10 }, { srcBoard: 102, snkBoard: 101, weight: 5 }])
)