题目
有一组未知数量与值的数字,要求把他们分成三份, 使每份的和尽量相等.
思路
- 对数组进行降序排序
- 创建三个数组,记录每个数组的当前之和
- 下个数分配给当前之和最小的数组.
版本1.0
var arr = [5, 2, 6, 88, 6, 95, 15, 45, 22];
// 进行排序
arr = arr.sort(function(a, b) {
return b - a
});
// 分为三组,记录当前之和
var oneArr = [],
twoArr = [],
threeArr = [];
// 记录当前之和的
oneArr.sum = 0;
twoArr.sum = 0;
threeArr.sum = 0;
for(var i = 0; i < arr.length; i++) {
if(oneArr.sum <= twoArr.sum) {
if(oneArr.sum <= threeArr.sum) {
oneArr.sum += arr[i];
oneArr.push(arr[i]);
} else {
threeArr.sum += arr[i];
threeArr.push(arr[i]);
}
} else {
if(twoArr.sum <= threeArr.sum) {
twoArr.sum += arr[i];
twoArr.push(arr[i]);
} else {
threeArr.sum += arr[i];
threeArr.push(arr[i]);
}
}
}
console.log('oneArr',oneArr,'twoArr',twoArr,'threeArr',threeArr);
觉得很难看,
一个是一堆if else 很难看, 另一个是 这个名字和数据结构很难看
能不能优化一下?
版本2.0 switch + Math.min
var arr = [5, 2, 6, 88, 6, 95, 15, 45, 22];
arr = arr.sort(function(a, b) {
return b - a
});
var oneArr = [],
twoArr = [],
threeArr = [],
a = 0,
b = 0,
c = 0,
item;
for(var i = 0; i < arr.length; i++) {
item = arr[i];
switch (Math.min(Math.min(a,b),c)){
case a:
a += item;
oneArr.push(item);
break;
case b:
b += item;
twoArr.push(item);
break;
case c:
c += item;
threeArr.push(item);
break;
}
}
console.log('oneArr',a,oneArr,'twoArr',b,twoArr,'threeArr',c,threeArr);
其实如果不是思路层面的变化,
那么优化的关键就是如何存储数据的问题,
如何设立数据, 如何获取数据, 如何让数据之间进行联系标记的问题.
而我们想要的效果是分支比较少, 又或者层级比较浅.
明显 oneArr 和 a, twoArr 和 b, threeArr 和 c 之间是有联系的.
这种联系在版本1.0中是直接用对象属性的方式进行了标记, 但没能直接用上.
在版本2.0中是在 switch 里进行联系, 实际上不算从数据层面上进行了紧密相连.
怎样让 oneArr 和 a 两个之间进行联系的问题, 需要通过a 找到 oneArr
在对象这种数据结构中, 确实存在if else
比如 father.son => 123, 实际上 father + ['son'] 就可以找到 123 这个值.
在这里 father 可以看做是一种空间(变量),
'son' 可以看做是一个条件, 或者是一个条件的结果.
想不出来..对象版本...
思考一个问题
我想让oneArr 和 a 两个变量之间, 能够互相找到对方,
即,我找到oneArr时,就能找到a, 找到a时就能找到oneArr,
怎么做?
第一种思路, 两者之间在概念上的关系, 即算法上的关系.
在这道题中, a 其实是 oneArr的各项之和.
所以通过oneArr是可以计算出a 的,
但反过来, 通过a 是无法逆算出 oneArr这个数组的.
第二种思路, 我们可以通过诸如对象这种数据形式, 让他们俩产生联系.
// 由于 oneArr 和 a 都是引用值, 所以这是可能的
var oneArr = [1,2,3];
var a = {};
oneArr.a = a;
a.oneArr = oneArr;
// 则真正用来存和的a 实际上就要变成 sum;
a.sum = oneArr.reduce(function (a,b) {
return a += b
});
但似乎很少这样做, 因为从某种角度来讲,这是种死循环.
或者比较容易形成死循环.
更常见的对象结构应该是这样 (也就是版本1.0的样子)
var obj = {};
obj.arr = [1,2,3];
obj.sum = oneArr.reduce(function (a,b) {
return a += b
});
我之前通常想到的就是用这种结构去产生联系,
可严格来讲, arr 并不能找到sum , sum 也并不能找到 arr,
obj能够找到他们俩, 如果sum,和arr 能够找到obj , 实际上就能找到彼此.
但在这门语言里是不存在, 子找父的. 继承关系中, 能够找到prototype,
但在一般对象关系中则不存在.
又或者好像还有类似这种,通过字符串产生间接联系的
var a = 'some';
var arr[a] = [1,2,3];
var sum[a] = 6;
arr 和 sum 没有任何直接关系, 不过他们拥有同一个属性 'some',
即, 通过'some' 这个字符串, 就可以到各自的空间中, 取出对应的数据.
-------------------------------------------------
把例题改一下, 就会出现, 下面这种看起来很恶心的东西出来.
var objArr = {};
var objSum = {};
var keyArr = ['one','two','thr'];
for(var i = 0; i < keyArr.length; i++) { // 初始化
objArr[keyArr[i]] = [];
objSum[keyArr[i]] = 0;
}
for(var i = 0; i < arr.length; i++) {
item = arr[i];
switch (Math.min(Math.min(objSum[keyArr[0]],objSum[keyArr[1]]),objSum[keyArr[2]])){
case objSum[keyArr[0]]:
objSum[keyArr[0]] += item;
objArr[keyArr[0]].push(item);
break;
case objSum[keyArr[1]]:
objSum[keyArr[1]] += item;
objArr[keyArr[1]].push(item);
break;
case objSum[keyArr[2]]:
objSum[keyArr[2]] += item;
objArr[keyArr[2]].push(item);
break;
}
}
es6中出现了一个map的东西, 从这篇文章的角度来讲,
map比对象的优点在于, 对象的属性必须只能是个字符串,
比如
obj['some'] = 123;
obj.mike 实际上就等同于 obj['mike']
对于obj 这个对象而言, 后面中括号中基本都是字符串,
即使不是字符串, 内部也会使用 toString 进行转化.
比如
obj[{}] = 111; / 存值
obj[{}] / 返回 111
obj[{a:1}] / 返回111
obj['[object Object]'] / 返回的也是111, 所有对象都会返回111
因为在存取时, 对象会调用 toString 方法,
而所有对象的 toString 方法返回的都是'[object Object]',
比如
obj[[]] = 222; / 存值
obj[''] / 返回 222
因为 [] 调用 toString 会返回 ''
Array的原型链上的toString 方法似乎会调用 join方法.
比如
obj[1] = 333; / 此时的1 的数据类型是 Number
obj[1] / 返回333 很正常
obj['1'] / 返回333 表明他存进去之前, 1 也经历了 toString 的过程.
总之, 对象后面的值必须是字符串类型.
而map 就不同了, 他可以识别各种类型,
并且能够识别引用值的地址.
var m = new Map();
/存值
var a = 1;
var b = '1';
m.set(a,'aaa');
m.set(b,'bbb');
/取值
m.get(a) ; / 返回'aaa'
m.get(b) : / 返回'bbb'
m.get(1) ; / 返回'aaa'
m.get('1') : / 返回'bbb'
表明能够识别类型, 起码不会发生内部toString转化的现象.
然后是引用值的问题
var a = [];
var b = {};
/ 存值
m.set(a,'ccc');
m.set(b,'ddd');
/ 取值
m.get(a) / 返回'ccc'
m.get(b) / 返回'ddd'
/ 问题来了
m.get([]) / 返回undefined
m.get({}) / 返回 undefined
其实不难理解, 因为引用值实际上存的是地址,
我接连写三个 空数组 [],[],[]
这三个数组看似相同, 实际上每个的地址都不同.
所以像下面这样是没有用的
m.set([1,2],'quick');
m.get([1,2]) / 返回的不是quick 而是 undefined
所以当你想用map 并用引用值来充当key的时候,
一般就是要用一个变量来接收这个地址.否则,你是找不到这个地址的.
但是返回来却是这样的, 只要一个引用值的地址不变,
而引用值"内部"的值变化是不会产生影响的.
比如
var obj = {age : 18};
m.set(obj,'mike');
m.get(obj); / 返回mike , 没毛病.
更改一下 obj
obj.age = 19; / 注意, obj本身的地址是没有更新的, 除非用等号重新赋值.
m.get(obj); / 返回 mike, 也没毛病.
回过头我们用map硬套一下这道题
网友评论