js实现多维数组组合算法

前言

在这里插入图片描述

最近在搞一个航线管理系统,如图,需要根据起始点,中转点和终点绘制航线图。这就涉及到点与点之间的组合算法。用专业点的说法,就是多维数组之间的元素组合。就拿上面的航线图来说,我们可以抽象为下面的数组:

[
	['香港','常州'],
	['成都'],
	['乌鲁木齐','敦煌']
]

通过一个简单的数学计算,可以得到共有2x1x2=4条线。但是如何得到这具体四条线分别包含哪些节点呢?像这种问题,一般都会涉及到递归的算法。下面我们就来实现一下。

方案一:多维数组转换为二维数组进行计算

一、分析

直接计算多维(超过二维)数组会比较绕。我们可以将这个问题分解为多个二维数组进行叠加运算。拿上面的例子来说,可以先用['香港','常州']['成都']进行组合,得到:

[
	['香港','成都'],
	['常州','成都']
]

然后再用这个临时数组与[‘乌鲁木齐’,‘敦煌’]组合,得到:

[
	['香港','成都','乌鲁木齐'],
	['常州','成都','敦煌'],
	['香港','成都','乌鲁木齐'],
	['常州','成都','敦煌']
]

整个的实现流程可以概括为下图:
转为二维数组

因此,我们可以先这样实现:

二、简单的实现

const dataList=[
	['香港','常州'],
	['成都'],
	['乌鲁木齐','敦煌']
]

/**
 * 数组组合
 * @param arr1 基础数组
 * @param arr2 待组合数组
 * @returns {*[]}
 */
const comb = (arr1, arr2) => {
  let index = 0
  const res = []
  for (let a1 of arr1) {
    for (let a2 of arr2) {
      if (Array.isArray(a1)) {
        res[index++] = [...a1, a2]
      } else {
        res[index++] = [a1, a2]
      }
    }
  }
  return res
}
const res1=comb(dataList[0],dataList[1])
console.log(res1) // [['香港','成都'],['常州','成都']]

至此,我们组合了起始点和中转点。那还剩下终点哇,我们就这样组合:

const res2=comb(comb(dataList[0],dataList[1]),dataList[2])

如果还有点,以此类推:

const res2=comb(comb(comb(dataList[0],dataList[1]),dataList[2]),dataList[3])...

但是我们不能直接这样用,如果中间点类型多了,那么集合从二维变成N维还这样写的话,会写死人。
仔细看一下求解模式,还是有点规律的,因为后面都是在不断地调用comb方法,递归嵌套。

三、优化实现

/**
 * 多维数组组合算法
 * @param arr1 基础数组
 * @param arr2 待组合数组
 * @param list 总数组
 * @param depth 当前组合深度,可以看作组合时,指向待组合数组在总数组中的下标
 * @returns {*[]|[]|*}
 */
const combWrapper = (arr1, arr2, list, depth = 0) => {
  if (depth < 2) { // 前两个直接组合
    return comb(arr1, arr2)
  } else {
    const res = comb(arr1, arr2)
    if (depth < list.length) { // 收敛条件为深度不能超过总数组长度
      return combWrapper(res, list[++depth - 1], list, depth)
    } else {
      return res
    }
  }
}

console.log(combWrapper(dataList[0], dataList[1], dataList, 2)) // 这里的深度从2也就是总数组中第三个元素开始组合

但是这样写的话,参数太多,我们还可以精简:

const _combWrapper = (list, depth = 2, arr1, arr2) => {
  if (!arr1) {
    arr1 = list[0]
  }
  if (!arr2) {
    arr2 = list[1]
  }
  if (depth < 2) { // 前两个直接组合
    return comb(arr1, arr2)
  } else {
    const res = comb(arr1, arr2)
    if (depth < list.length) { // 收敛条件为深度不能超过总数组长度
      return _combWrapper(list, ++depth, res, list[depth - 1])
    } else {
      return res
    }
  }
}

console.log(_combWrapper(dataList))

利用js方法参数不用写全的特性,可以只传入一个原始数组,就可以自动组合了。
!需要注意的是上面代码++的位置!

方案二:深度遍历算法

我们直接上代码看看:

const temp=[]
const results=[]
const comb=(arr,depth=0)=>{
	const list=arr[depth]
	for(let data of list){
		temp[depth]=data
		if(depth !== arr.length-1){
			comb(arr,++depth)
		}else{
			results.push(temp)
		}
	}
}
comb(dataList)
console.log('res',results)

一、分析

首先,光从代码量上,这种方法就比前面的少很多。最外层有两个数组集合:temp和results。temp用于临时存储每个起始->中转->终点的线路。results存储的是最终的结果。temp被赋值时是根据depth的下标来的。depth会在每次递归的时候加1,直到递归完所有的数据深度。
深度法
与转为二维数组的方式对比的话,前者一开始的线路只有两个元素,然后后面递归的时候不断增加。而深度遍历的话,每次遍历完成都有最终深度长度的元素。但是上面的代码真的玩美吗?

二、异常剖析

首先,思路是没问题的。问题的关键在于js的变量的引用。
我们声明的temp对象相对comb方法是个全局变量,并且是个数组不是普通基础数据类型。根据depth来确定被赋值的元素的位置。但是我们在后面将这个depth自增了,会导致temp[depth]的指向的地址变了。再者,results永远都是在push这个temp对象。可以变相看作是一种强引用。这样得到结果,就是最终所有的result都是同一个最后的temp。针对第一个depth的问题,我们可以将++depth变为depth+1或者设置一个临时变量,对这个临时变量自增。对于后者的push,我们可以对temp进行深度拷贝。而js里面最简单明了的深度拷贝方式,就是利用JSON的序列化和反序列化进行操作:

const comb0 = (arr, depth = 0) => {
  const currentArr = arr[depth]
  currentArr.forEach(c => {
    temp[depth] = c
    if (depth !== arr.length - 1) {
      comb0(arr, depth + 1)
      // let a=depth
      // comb0(arr, ++a)
    } else {
      res.push(JSON.parse(JSON.stringify(temp))) // 深度拷贝temp
    }
  })
}

方案三:利用js的数组的reduce方法实现

什么是reduce函数?

定义和用法
reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
reduce() 可以作为一个高阶函数,用于函数的 compose。
注意: reduce() 对于空数组是不会执行回调函数的。

举个栗子

加入我们想统计一个数组里面的所有元素之和:

const list1 = [1, 2, 3, 4, 5, 6, 7]
const res__ = list1.reduce((pre, current) => {
  return pre + current
})
console.log(res__) // 28

那这个又和我们的多维数组组合有啥联系呢?
reduce本身类似filte,map这些遍历方法。请注意,reduce的前两个参数。第一个prev是表示截至遍历到当前的累计结果。也就是你的return里面结果的一个集合。第二个则是当前遍历需要执行的对象。这样的话,我们可以改写一下方案一:

const comb = (arr1, arr2) => {
  let index = 0
  const res = []
  for (let a1 of arr1) {
    for (let a2 of arr2) {
      if (Array.isArray(a1)) {
        res[index++] = [...a1, a2]
      } else {
        res[index++] = [a1, a2]
      }
    }
  }
  return res
}

const doComb=(arr)=>{
	return arr.reduce((prev,current)=>{
		return comb(prev,current)
	)
}

console.log(doComb(dataList))

是不是简洁了很多。

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 降维是指将一个多维数组转换为一个较低维度的数组,下面是三种常见的方法: 1. 展平法:将多维数组展开成一维数组。这种方法的实现非常简单,只需要使用numpy库的`ravel()`或`flatten()`函数即可。比如,如果有一个形状为(2, 3, 4)的三维数组a,可以使用以下代码将其展平成一个一维数组b: ``` python import numpy as np a = np.random.rand(2, 3, 4) b = a.ravel() # 或者 b = a.flatten() ``` 2. 合并法:将多维数组合并成一个二维数组,其中每一行代表原数组的一个元素。这种方法需要使用numpy库的`reshape()`函数,将原数组重新排列成一个二维数组。比如,如果有一个形状为(2, 3, 4)的三维数组a,可以使用以下代码将其转换为一个形状为(2*3*4, 1)的二维数组b: ``` python import numpy as np a = np.random.rand(2, 3, 4) b = a.reshape(-1, 1) ``` 3. 折叠法:将多维数组沿着某个轴折叠成一个较低维度的数组。这种方法需要使用numpy库的`reshape()`函数,将原数组沿着某个轴折叠成一个较低维度的数组。比如,如果有一个形状为(2, 3, 4)的三维数组a,可以使用以下代码将其沿着第一维折叠成一个形状为(2, 12)的二维数组b: ``` python import numpy as np a = np.random.rand(2, 3, 4) b = a.reshape(2, -1) ``` ### 回答2: 多维数组降维意味着将多维数组转换为一维数组。以下是三种方式实现多维数组降维的方法: 方法一:循环遍历 可以利用循环遍历多维数组的每个元素,然后依次将每个元素添加到一维数组中。具体步骤如下: 1. 创建一个空的一维数组。 2. 使用嵌套的循环遍历多维数组的每个元素。 3. 在循环中,将每个元素添加到一维数组中。 4. 循环结束后,一维数组中即保存了多维数组的所有元素。 方法二:使用flatten函数或者库函数 一些编程语言或者库提供了直接将多维数组降维的函数。如Python中的numpy库的flatten函数,可以直接将多维数组降维为一维数组。具体步骤如下: 1. 导入包含flatten函数的库。 2. 调用flatten函数,将多维数组作为参数传入。 3. 函数返回一个降维后的一维数组。 方法三:递归 通过递归调用的方式,可以将多维数组降维为一维数组。具体步骤如下: 1. 创建一个空的一维数组。 2. 编写递归函数,函数接收一个多维数组作为输入参数。 3. 在递归函数中,遍历多维数组的每个元素。 4. 如果元素是数组类型,则递归调用函数处理该数组。 5. 如果元素不是数组类型,则将元素添加到一维数组中。 6. 递归函数的返回值即为降维后的一维数组。 以上是三种实现多维数组降维的方法,根据编程语言和具体要求可以选择适合的方法。 ### 回答3: 实现多维数组降维的方式有很多种,下面我将介绍三种常见的方式。 1. 循环遍历方式:通过嵌套循环遍历多维数组的每一个元素,并将其添加到一个新的一维数组中。例如,对于一个二维数组arr,可以使用两层for循环,将arr[i][j]的元素添加到一个一维数组中,即result.append(arr[i][j])。这样就可以将多维数组降维为一维数组。 2. 使用内置函数方式:许多编程语言提供了内置函数来处理多维数组,其中包括降维操作。例如,在Python中可以使用numpy库的flatten()函数来实现多维数组的降维。例如,对于一个二维数组arr,可以使用result = arr.flatten()来将其降维为一维数组。 3. 使用递归方式:递归是一种通过自身调用来解决问题的方法。对于多维数组的降维,可以使用递归的方式来遍历每一个元素,并将其添加到一个新的一维数组中。例如,对于一个多维数组arr,可以编写一个递归函数flatten(arr),函数内部遍历arr的每一个元素,若元素是一个数组,则递归调用flatten()函数;若元素不是一个数组,则将其添加到一维数组中。最终返回一个降维后的一维数组。 以上是三种常见的方式,可以根据具体的需求和编程语言的特点选择合适的方式进行多维数组的降维操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值