一个if else的优化小思考题

题目
有一组未知数量与值的数字,要求把他们分成三份, 使每份的和尽量相等.

思路

  1. 对数组进行降序排序
  2. 创建三个数组,记录每个数组的当前之和
  3. 下个数分配给当前之和最小的数组.

版本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硬套一下这道题


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 一、基本数据类型 注释 单行注释:// 区域注释:/* */ 文档注释:/** */ 数值 对于byte类型而言...
    龙猫小爷阅读 4,213评论 0 16
  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,074评论 0 2
  • 平平淡淡才是常态,所有珍贵的道理都藏在平淡,琐碎中
    雅nmmm阅读 153评论 0 0
  • 盛夏的下午,如果每天都是吊床的这种视角,我想我会更幸福。
    浅爱深喜欢阅读 200评论 0 0
  • 黄澄澄的一望无际的麦浪,小山似的麦垛。一个丰年。 小学生都放麦假,去拾麦穗儿。颗粒归仓。 然后,马车人车排成长龙,...
    兰兮阅读 630评论 3 7