FreeCodeCamp 高级算法题 - 更新库存
更新库存 (Inventory Update)
题目链接
问题解释
- 这个
function
接收两个二维数组参数 arr1
与 arr2
。其中 arr1
为更新前的现有库存,arr2
为需要更新的内容。返回值为更新后的库存,用二维数组表示
- 如果
arr1
为 [[1, 'a'], [2, 'b']]
,arr2
为 [[10, 'a'], [100, 'b'], [1, 'c']]
,则返回值应为 [[11, 'a'], [102, 'b'], [1, 'c']]
基本解法 - 循环
思路提示
- 这道题也是高级算法中相对容易的一道题,题目的逻辑其实很容易理清,但代码实现上可能会遇到一点小问题
- 两个参数,
arr1
为现有库存,arr2
为需要更新的内容。如果货物存在就更新数量,否则把它加入库存
- 每个货物用一个数组表示,其中第一个元素为数量,第二个为货物名称
- 那么,我们需要做的就是遍历第二个二维数组
arr2
,与第一个二维数组 arr1
中的每一项进行比较:
- 如果
arr2
子数组的第二个元素可以在 arr1
中找到相等的,那我们就把数组中第一个数字相加
- 如果
arr2
子数组的第二个元素在 arr1
中找不到,那我们就把这个子数组添加到 arr1
中
- 最后,再对
arr1
按照子数组第二个元素的字母顺序进行一次排序,输出结果
- 最后的排序,我们可以用数组的
sort
方法。但有一个细节要注意,由于 sort
方法的回调函数返回值有三种情况,分别是大于 0,等于 0 和小于 0。因此,我们需要比较字符串,并返回一个大于 0 的数,和一个小于 0 的数。如果你不理解这一点,请去 MDN 看一下 sort
的文档
- 看到这里,如果你能理解上面说的,请先自己试着完成一下这道题。如果你在实现过程中遇到困难,再回来看看这篇博客后面的部分
实现 - flag 变量
- 写代码的时候你可能会发现,上面说到的思路仿佛很容易,但实现起来却有点麻烦。我们先不讨论最后的返回值,逻辑部分,你可能会想到这样的写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function updateInventory(arr1, arr2) { for (var j = 0; j < arr2.length; j++) { for (var i = 0; i < arr1.length; i++) { if (arr1[i][1] === arr2[j][1]) { arr1[i][0] += arr2[j][0]; } } } }
|
- 请暂时记住这几个位置编号。其中
位置 1
表示找到相同货物,位置 2
表示执行完找相同货物判断,位置 3
表示 arr1
遍历结束,位置 4
表示 arr2
遍历结束。显然,在 位置 4
,我们需要对 arr1
进行排序,并返回
- 我们先遍历
arr2
,对于 arr2
中的每一个元素,我们都要遍历一遍 arr1
来进行比较。暂且不讨论复杂度的问题,我们先来想想如何实现
- 在遍历
arr1
的时候,有两种情况:
- 只要发现第二个元素相等,我们就给
arr1[i][0]
加上 arr2[j][0]
,并跳出当前循环
- 如果遍历完
arr1
仍找不到相等的,那么我们就要把 arr2[j]
添加到 arr1
- 你可能会觉得这个情景很熟悉,事实上这里的思路和 Profile Lookup 那道题很类似。那道题我们可以用
return
解决,因此你可能会想到在 位置 1
加上 return
,但 return
就跳出了整个 function
,显然这样做不行 (注:其实,只需要封装成一个函数就可以用 return
了,这个我们放到后面再说)
- 你还可能想到用
break
。但这个 break
加在哪里都不太合适。如果我们加在 位置 1
,那我们就没法处理上面说的第二种,也就是遍历完 arr1
找不到相等的情况。我们把 push(arr2[j])
写到 位置 2
显然不能实现要求,这样会 push
很多次 arr2[j]
。如果我们写到 位置 3
,那么每次遍历完 arr1
都会 push
一次 arr2[j]
- 如果一定要这么写,有一种方式是可行的,那就是引入一个
flag
变量。每次我们在遍历 arr1
之前初始化它为 false
,一旦进入了 if (arr1[i][1] === arr2[j][1])
我们就给它赋值 true
。遍历完 arr1
的时候,我们根据这个 flag
决定是否要 push(arr2[j])
。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function updateInventory(arr1, arr2) { for (var j = 0; j < arr2.length; j++) { var flag = false; for (var i = 0; i < arr1.length; i++) { if (arr1[i][1] === arr2[j][1]) { arr1[i][0] += arr2[j][0]; flag = true; } } if (!flag) { arr1.push(arr2[j]) } } }
|
- 我们需要在
位置 3
执行是否该 push(arr2[j])
的判断,因为这里我们已经遍历完了 arr1
,也就是说,已经可以得出当前遍历到的 arr2[j]
是否在 arr1
中存在的结论
实现 - 封装函数
- 如果我们想用
return
,也不麻烦,只需要封装一个 “遍历 arr1
“ 的函数就行。传入的参数显然是外层遍历的 arr2[j]
,由于这是某一个件货物,我们记作 item
。至于 arr1
,我们可以直接调用,不需要传入方法
1 2 3 4 5 6 7 8 9
| function compareAndMerge(item) { for (var i = 0; i < arr1.length; i++) { if (arr1[i][1] === item[1]) { arr1[i][0] += item[0]; return; } } arr1.push(item); }
|
- 放到外面的循环中,结果就是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function updateInventory(arr1, arr2) { for (var j = 0; j < arr2.length; j++) { compareAndMerge(arr2[j]); } function compareAndMerge(item) { for (var i = 0; i < arr1.length; i++) { if (arr1[i][1] === item[1]) { arr1[i][0] += item[0]; return; } } arr1.push(item); } }
|
结果排序
- 题目中要求,结果需要按照货物名称的字母顺序排列。事实上,JavaScript 的
>
和 <
就可以比较出字符串的 “大小”。遇到一样的字符,就会继续比较下一位。至于大小,则是根据 ASCII 码来决定的。你可以试试 'a' < 'b'
,以及 'abc' < 'az'
,看一下输出结果
- 请注意,如果我们写成
arr1.sort((a, b) => a[1] > b[1])
是不行的。因为 sort
方法回调函数返回值不应该是 true
或 false
;而应该是正数/负数/0
- 文档中提到,如果返回值小于
0
,那么会把 a
放到 b
的前面;反之,则会把 b
放到 a
的前面。至于等于 0
的情况,文档中提到,理论上应该是保持 a
和 b
顺序不变,但这不一定适用于所有浏览器
- 还是上面的例子,如果我们写成
arr1.sort((a, b) => a[1] > b[1])
,true
会被隐式转换成 1
,即大于 0
的返回值,而 false
会被隐式转换成 0
,即等于 0
的返回值,这样就会导致结果不稳定。你可以运行一下 'gwxutwznyfb'.split('').sort((a, b) => a > b)
,结果 'w'
被排在了第一个
- 因此,最终的排序我们应该这么写:
1 2 3
| return arr1.sort(function(a, b) { return a[1] > b[1] ? 1 : -1; });
|
- 其中,
1
和 -1
为任意正数和负数,我们也可以写成 10000
和 -123
参考链接
代码
中级解法 - 使用数组方法
思路提示
- 我们需要用
arr2
中的子数组的第二个值进行比较,而且它还是一个字符串。字符串是原始类型,也就意味着我们可以通过 indexOf
方法来获取索引。如果是 -1
那就表示不存在,这样我们就可以 push
了
- 那我们只需要把
arr1
中,每个子数组的第二个元素提取出来,成为一个新数组,然后我们就可以获取到字符串的索引了。这个操作,就是用数组的 map
方法
- 当然,最终的排序也是不能省的
参考链接
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function updateInventory(arr1, arr2) { var valueArr = arr1.map(function(e) { return e[1] }); for (var i = 0; i < arr2.length; i++) { var index = valueArr.indexOf(arr2[i][1]) if (index > -1) { arr1[index][0] += arr2[i][0]; } else { arr1.push(arr2[i]); } } return arr1.sort(function(a, b) { return a[1] > b[1] ? 1 : -1; }); }
|