命题:多维数组的排列组合 或 多个数组之间的排列组合
命题场景:
现在有一批手机,其中颜色有['白色','黑色','金色'];内存大小有['16G','32G','64G'],版本有['移动','联通','电信'],要求写一个算法,实现[['白色','16G','移动'], ['白色','16G','联通'] ...]这样的组合,扩张,如果后面还有参数,比如再加一个['国行','港版'],不改程序一样可以执行!
不知道要实现的需求大家听懂了没有,下面我会一步步实现,教大家怎么写:
最后得到的结果是一个数组里面包含若干个数组,看着挺复杂的,我们先实现一个简化版的,数组里面不是数组,而是字符串连接的结果,嗯,先一步步来吧:
第一步,想想通过什么技术来实现,你看这数组之间不断的重组,很容易想到用回调函数,一遍一遍的执行,大致知道用什么技术,接下来就是写思路了,看看下面:
// 执行组合排列的函数 function doExchange(arr){ } //执行 var arr = [['a', 'b', 'c'], [1, 2, 3], ['x', 'y', 'z']]; var arr1 = [['a','b','c']]; //doExchange(arr); console.log(doExchange(arr));
呐,我们建一个函数doExchange(),表示我们执行排序的主函数,然后当执行arr的时候,输出['a1x','a1y' ...]这样的结果,如果是arr1呢?我们需要输出['a','b','c'],这好理解哈,现在的重点就是这个主函数了,下面主要讲主函数的实现过程
// 执行组合排列的函数 function doExchange(arr){ var len = arr.length; // 当数组大于等于2个的时候 if(len >= 2){ }else{ return arr[0]; } }
我们的思路是,当参数里面的数组长度大于2个,比如2个,3个或更多,就执行我们的组合代码,否则只有一个,就直接输出来呗
如果是大于2个呢?我们的思路是先进行第一个和第二个的合并,网上有一种实现方式是,用多层for循环来嵌套,最终得到组合的值,这种估计大家也能感觉到,两三个还可以,多了就呵呵了,这里只是提一下,因为前两个比较好获取,合并之后呢,就相当于是
2个数组合并成一个数组,然后这个数组再放倒参数数组中第一个位置,把原来前两个去掉,相当于是用这个数组替换前两个,没听懂哈,没关系,我们直接看代码:
if(len >= 2){ // 第一个数组的长度 var len1 = arr[0].length; // 第二个数组的长度 var len2 = arr[1].length; // 2个数组产生的组合数 var lenBoth = len1 * len2; // 申明一个新数组,做数据暂存 var items = new Array(lenBoth); // 申明新数组的索引 var index = 0; // 2层嵌套循环,将组合放到新数组中 for(var i=0; i<len1; i++){ for(var j=0; j<len2; j++){ items[index] = arr[0][i] + arr[1][j]; index++; } } }
呐,这里我们先获取第一个和第二个数组的长度,然后计算出它们有多少种组合方式,然后新建一个暂存的数组,用来存我们组合得到的结果,后面就是用双层循环,做字符串连接,放到暂存数组中,没什么好说的
我们的到了前两个的组合结果,依据我们的思路,是要把它和原来数组合并成一个新数组
// 第一个数组的长度 var len1 = arr[0].length; // 第二个数组的长度 var len2 = arr[1].length; // 2个数组产生的组合数 var lenBoth = len1 * len2; // 申明一个新数组,做数据暂存 var items = new Array(lenBoth); // 申明新数组的索引 var index = 0; // 2层嵌套循环,将组合放到新数组中 for(var i=0; i<len1; i++){ for(var j=0; j<len2; j++){ items[index] = arr[0][i] + arr[1][j]; index++; } } // 将新组合的数组并到原数组中 var newArr = new Array(len -1); for(var i=2;i<arr.length;i++){ newArr[i-1] = arr[i]; } newArr[0] = items;
整体的思路就是这样,得到的新数组就是剩下的未组合的数组了,到这里大家应该就豁然开朗了,然后使用递归再次调用这个过程,这样不断组合成新数组,那这个新数组当参数时,如果数组的长度等于1了,就说明组合完了,就会执行后面的else语句,输出出来。
整体代码贴出来感受一下:
// 执行组合排列的函数 function doExchange(arr){ var len = arr.length; // 当数组大于等于2个的时候 if(len >= 2){ // 第一个数组的长度 var len1 = arr[0].length; // 第二个数组的长度 var len2 = arr[1].length; // 2个数组产生的组合数 var lenBoth = len1 * len2; // 申明一个新数组,做数据暂存 var items = new Array(lenBoth); // 申明新数组的索引 var index = 0; // 2层嵌套循环,将组合放到新数组中 for(var i=0; i<len1; i++){ for(var j=0; j<len2; j++){ items[index] = arr[0][i] + arr[1][j]; index++; } } // 将新组合的数组并到原数组中 var newArr = new Array(len -1); for(var i=2;i<arr.length;i++){ newArr[i-1] = arr[i]; } newArr[0] = items; // 执行回调 return doExchange(newArr); }else{ return arr[0]; } } //执行 var arr = [['a', 'b', 'c'], [1, 2, 3], ['x', 'y', 'z']]; var arr1 = [['a','b','c']]; //doExchange(arr); console.log(doExchange(arr));
执行arr和arr1的结果给大家截个图吧:
我们简化版的组合算是完成了,可能也会有类似的需求,但是我们今天要讲的不是这样的需求,而是里面的每一个组合要是一个数组,这个怎么实现呢?
突破点应该就在字符串连接那块了,原来是用的字符串连接,如果我们改成数组连接,不就行了吗?试试:
for(var i=0; i<len1; i++){ for(var j=0; j<len2; j++){ if(arr[0][i] instanceof Array){ items[index] = arr[0][i].concat(arr[1][j]); }else{ items[index] = [arr[0][i]].concat(arr[1][j]); } index++; } }
其他地方都没有改,就只是改了这里,但是这里第一次是要做数组判断的,因为刚开始是字符串,要转换成数组才能连接,这里注意一下,最终的代码如下:
function doExchange(arr){ var len = arr.length; // 当数组大于等于2个的时候 if(len >= 2){ // 第一个数组的长度 var len1 = arr[0].length; // 第二个数组的长度 var len2 = arr[1].length; // 2个数组产生的组合数 var lenBoth = len1 * len2; // 申明一个新数组 var items = new Array(lenBoth); // 申明新数组的索引 var index = 0; for(var i=0; i<len1; i++){ for(var j=0; j<len2; j++){ if(arr[0][i] instanceof Array){ items[index] = arr[0][i].concat(arr[1][j]); }else{ items[index] = [arr[0][i]].concat(arr[1][j]); } index++; } } var newArr = new Array(len -1); for(var i=2;i<arr.length;i++){ newArr[i-1] = arr[i]; } newArr[0] = items; return doExchange(newArr); }else{ return arr[0]; } } // var arr = [['a', 'b', 'c'], [1, 2, 3], ['x', 'y', 'z'],['手机']]; var arr1 = [['a','b','c']]; console.log(doExchange(arr));
得到的结果:
我后面又加了一个手机,同样是可以实现的,到这里就算死写完了。
Flutter 代码:
static doExchange(arr){ var len = arr.length; // 当数组大于等于2个的时候 if(len >= 2){ // 第一个数组的长度 var len1 = arr[0].length; // 第二个数组的长度 var len2 = arr[1].length; // 2个数组产生的组合数 var lenBoth = len1 * len2; // 申明一个新数组,做数据暂存 // var items = new Array(lenBoth); var items = List<dynamic>.filled(lenBoth, ''); // 申明新数组的索引 var index = 0; // 2层嵌套循环,将组合放到新数组中 for(var i=0; i<len1; i++){ for(var j=0; j<len2; j++){ if(arr[0][i] is List){ List<dynamic> _list = List<dynamic>(); _list.addAll(arr[0][i]); _list.addAll([arr[1][j]]); items[index] = _list; }else{ List<dynamic> _list = List<dynamic>(); _list.addAll([arr[0][i]]); _list.addAll([arr[1][j]]); items[index] = _list; } // items[index] = arr[0][i] + arr[1][j]; index++; } } // 将新组合的数组并到原数组中 // var newArr = new Array(len -1); var newArr = List<dynamic>.filled(len -1, List<String>()); for(var i=2;i<arr.length;i++){ newArr[i-1] = arr[i]; } newArr[0] = items; // newArr[0] = items; // 执行回调 return doExchange(newArr); }else{ return arr[0]; } }
//执行 var arr = [['a', 'b', 'c'], ['1', '2', '3'], ['x', 'y', 'z']]; var arr1 = [['a','b','c']]; debugPrint('doExchange-${doExchange(arr)}'); 输出各种组合:doExchange-[[a, 1, x], [a, 1, y], [a, 1, z], [a, 2, x], [a, 2, y], [a, 2, z], [a, 3, x], [a, 3, y], [a, 3, z], [b, 1, x], [b, 1, y], [b, 1, z], [b, 2, x], [b, 2, y], [b, 2, z], [b, 3, x], [b, 3, y], [b, 3, z], [c, 1, x], [c, 1, y], [c, 1, z], [c, 2, x], [c, 2, y], [c, 2, z], [c, 3, x], [c, 3, y], [c, 3, z]]