组合筛选vue_学会这个套路,彻底掌握排列组合。【会点算法的前端更早下班】...

本文主要是介绍组合筛选vue_学会这个套路,彻底掌握排列组合。【会点算法的前端更早下班】...,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

前段时间在掘金看到一个热帖 《今天又懒得加班了,能写出这两个算法吗?带你去电商公司写商品中心》,里面提到了一个比较有意思故事,大意就是一个看似比较简单的电商 sku 的全排列组合算法,但是却有好多人没能顺利写出来。有一个毕业生小伙子在面试的时候给出了思路,但是进去以后还是没写出来,羞愧跑路~

其实排列组合是一个很经典的算法,也是对递归回溯法的一个实践运用,本篇文章就以带你学习一个标准「排列组合求解模板」,耐心看完,你会有更多收获。

需求

需求描述起来很简单,有这样三个数组:

let names = ["iPhone X", "iPhone XS"]

let colors = ["黑色", "白色"]

let storages = ["64g", "256g"]

需要把他们的所有组合穷举出来,最终得到这样一个数组:

;[
  ["iPhone X", "黑色", "64g"],
  ["iPhone X", "黑色", "256g"],
  ["iPhone X", "白色", "64g"],
  ["iPhone X", "白色", "256g"],
  ["iPhone XS", "黑色", "64g"],
  ["iPhone XS", "黑色", "256g"],
  ["iPhone XS", "白色", "64g"],
  ["iPhone XS", "白色", "256g"],
]

由于这些属性数组是不定项的,所以不能简单的用三重的暴力循环来求解了。

思路

如果我们选用递归回溯法来解决这个问题,那么最重要的问题就是设计我们的递归函数。

思路分解

以上文所举的例子来说,比如我们目前的属性数组就是:namescolorsstorages,首先我们会处理 names 数组,很显然对于每个属性数组,都需要去遍历它,然后一个一个选择后再去和 下一个数组的每一项进行组合。

我们设计的递归函数接受两个参数:

  • index 对应当前正在处理的下标,是 names 还是 colors 或是 storage
  • prev 上一次递归已经拼接成的结果,比如 ['iPhone X', '黑色']

进入递归函数:

  1. 处理属性数组的下标0:假设我们在第一次循环中选择了 iPhone XS,那此时我们有一个未完成的结果状态,假设我们叫它 prev,此时 prev = ['iPhone XS']

  2. 处理属性数组的下标1:那么就处理到 colors 数组的了,并且我们拥有 prev,在遍历 colors 的时候继续递归的去把 prev 拼接成 prev.concat(color),也就是 ['iPhone XS', '黑色'] 这样继续把这个 prev 交给下一次递归。

  3. 处理属性数组的下标2:那么就处理到 storages 数组的了,并且我们拥有了 name + colorprev,在遍历 storages 的时候继续递归的去把 prev 拼接成 prev.concat(storage),也就是 ['iPhone XS', '黑色', '64g'],并且此时我们发现处理的属性数组下标已经到达了末尾,那么就放入全局的结果变量 res 中,作为一个结果。

编码实现

let names = ["iPhone X", "iPhone XS"]

let colors = ["黑色", "白色"]

let storages = ["64g", "256g"]

let combine = function (...chunks) {
  let res = []

  let helper = function (chunkIndex, prev) {
    let chunk = chunks[chunkIndex]
    let isLast = chunkIndex === chunks.length - 1
    for (let val of chunk) {
      let cur = prev.concat(val)
      if (isLast) {
        // 如果已经处理到数组的最后一项了 则把拼接的结果放入返回值中
        res.push(cur)
      } else {
        helper(chunkIndex + 1, cur)
      }
    }
  }

  // 从属性数组下标为 0 开始处理
  // 并且此时的 prev 是个空数组
  helper(0, [])

  return res
}

console.log(combine(names, colors, storages))

递归树图

画出以 iPhone X 这一项为起点的递归树图,当然这个问题是一个多个根节点的树,请自行脑补 iPhone XS 为起点的树,子结构是一模一样的。

4c1e6bcdf2334f3695b5958346fa99de.png

万能模板

为什么说这种接法是排列组合的「万能模板呢」?来看一下 LeetCode 上的 77. 组合 问题,这是一道难度为 medium 的问题,其实算是比较有难度的问题了:

问题

给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。

示例:

输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

解答

let combine = function (n, k) {
  let ret = []

  let helper = (start, prev) => {
    let len = prev.length
    if (len === k) {
      ret.push(prev)
      return
    }

    for (let i = start; i <= n; i++) {
      helper(i + 1, prev.concat(i))
    }
  }
  helper(1, [])
  return ret
}

可以看出这题和我们求解电商排列组合的代码竟然如此相似。只需要设计一个接受 start排列起始位置、prev上一次拼接结果为参数的递归 helper函数,

然后对于每一个起点下标 start,先拼接上 start位置对应的值,再不断的再以其他剩余的下标作为起点去做下一次拼接。当 prev 这个中间状态的拼接数组到达题目的要求长度 k后,就放入结果数组中。

剪枝

在这个解法中,有一些递归分支是明显不可能获取到结果的,我们每次递归都会循环到 不停的尝试 <= n的所有项,尝试作为start,假设我们要求的数组长度 k = 3,最大值 n = 4

而我们以 prev = [1],再去以 n = 4start 作为递归的起点,那么显然是不可能得到结果的,因为 n = 4 的话就只剩下 4这一项可以拼接了,最多也就拼接成 [1, 4],不可能满足 k = 3 的条件。

所以在进入递归之前,就果断的把这些“废枝”给减掉。

let combine = function (n, k) {
  let ret = []

  let helper = (start, prev) => {
    let len = prev.length
    if (len === k) {
      ret.push(prev)
      return
    }

    // 还有 rest 个位置待填补
    let rest = k - prev.length
    for (let i = start; i <= n; i++) {
      if (n - i + 1         continue
      }
      helper(i + 1, prev.concat(i))
    }
  }
  helper(1, [])
  return ret
}

相似题型

当然,力扣中可以套用这个模板的相似题型还有很多,而且大多数难度都是 medium的,比如快手的面试题子集 II-90,可以看出排列组合的递归解法还是有一定的难度的。

我在维护的 LeetCode 题解仓库 中已经按标签筛选好 「递归与回溯」类型的几道题目和解答了,感兴趣的小伙伴也可以一起攻破它们。

总结

排列组合问题并不是空中楼阁,在实际工作中也会经常遇到这种场景,掌握了递归回溯的标准模板当然不是为了让你死记硬背套公式,而是真正的理解它。遇到需要递归解决的问题。

  1. 画出递归树状图,找出递归公式。
  2. 对于不可能达成条件的分支递归,进行合理的「剪枝」。

希望阅读完本篇文章的你,能对递归和排列组合问题有进一步的理解和收获。

04ac673a6740da35f4b8af66132e1bdd.png

其他推荐

V8 垃圾回收原来这么简单?

初中级前端的高级进阶指南汇总

实战vue-ssr服务端渲染的单页应用

uni-app 结合云函数开发小程序博客(三)

uni-app 结合云函数开发小程序博客(二)

uni-app 结合云函数开发小程序博客(一)

作为前端,工作中复杂的需求,如何解决的?

Vue3 尝鲜 Hook + TypeScript 取代 Vuex 实现图书管理小型应用

应用动态规划和贪心算法高效实现瀑布流布局

如何在React中实现keep-alive?

深入理解HTTP报文每个知识点!

深入理解HTTP每个知识点!

React 和 Vue 都在用的 FLIP 思想是什么?

懂这些JavaScript的骚操作,你可以更牛B

JavaScript重构技巧-让别人看懂你写的代码

Vue3 的诞生的故事

当一个大前端有了服务器时会做些什么?

从零解读Vue3.0源码响应式系统

互联网人相亲现场,该如何battle

7个关于 CSS backgroundImage 好用的技巧

你还用首屏加载时间衡量Web 性能体验和质量?

如何使用Chrome来分析运行时的性能、内存问题 ?[太实用了]

超有料的前端性能优化总结(建议收藏)

一文读懂 babel7 的配置文件加载逻辑

【前端进阶】Nginx 负载均衡和缓存服务实战

CSS高级特效的必备技巧

图文并茂深度解析浏览器渲染原理,包看懂超值得收藏

Vue 的生命周期之间到底做了什么事清?(源码详解)

深度解析文件上传全过程

如何写出被Team Leader喜欢的JS 代码 前端面试128问汇总(含超详细答案) 你不一定知道的CSS 中的auto 可以任意插拔随意组合的业务级前端模块化神器! 超详细webpack构建方式补给! 超详细webpack基础补给! 当你升级到前端的TL时,如何快速打造用于中小团队的前端基建 线上项目出现问题时如何快速调试定位问题?学习 React Hooks 可能会遇到的五个灵魂问题 你确定你真的懂CSS吗? 太齐了!前端的学习及工作资料只收藏这份就够了! 大厂都在用的高级缓存方案 我这个页面居然用了10G的GPU?!! 微前端在企业级应用中的实践(华为项目经验传授) 原来微信支付软件架构是这样哒!!! 有这个!你还愁不会写正则吗? 看我如何把node接口耗时降低23%! 高级前端工程师是怎样高效部署前端应用? 【撩妹教程】如何教公司新来的女实习生小姐姐什么是闭包? 前端如何在繁忙的业务中提升自己 全链路日志如何实现? 大厂的高性能小程序原来是这样弄的!测试一下你离前端专家这个称号还有多远?

93b9334913d2a487f4932d8a96ea5616.png

a21ae389f997c828ab0df4cf12036da8.png点在看的人特别帅/美 c088336172af56f122598475429a19a6.gif

这篇关于组合筛选vue_学会这个套路,彻底掌握排列组合。【会点算法的前端更早下班】...的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/840801

相关文章

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Vue中组件之间传值的六种方式(完整版)

《Vue中组件之间传值的六种方式(完整版)》组件是vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用,针对不同的使用场景,如何选择行之有效的通信方式... 目录前言方法一、props/$emit1.父组件向子组件传值2.子组件向父组件传值(通过事件形式)方

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

css中的 vertical-align与line-height作用详解

《css中的vertical-align与line-height作用详解》:本文主要介绍了CSS中的`vertical-align`和`line-height`属性,包括它们的作用、适用元素、属性值、常见使用场景、常见问题及解决方案,详细内容请阅读本文,希望能对你有所帮助... 目录vertical-ali

浅析CSS 中z - index属性的作用及在什么情况下会失效

《浅析CSS中z-index属性的作用及在什么情况下会失效》z-index属性用于控制元素的堆叠顺序,值越大,元素越显示在上层,它需要元素具有定位属性(如relative、absolute、fi... 目录1. z-index 属性的作用2. z-index 失效的情况2.1 元素没有定位属性2.2 元素处

Python实现html转png的完美方案介绍

《Python实现html转png的完美方案介绍》这篇文章主要为大家详细介绍了如何使用Python实现html转png功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 1.增强稳定性与错误处理建议使用三层异常捕获结构:try: with sync_playwright(

Vue 调用摄像头扫描条码功能实现代码

《Vue调用摄像头扫描条码功能实现代码》本文介绍了如何使用Vue.js和jsQR库来实现调用摄像头并扫描条码的功能,通过安装依赖、获取摄像头视频流、解析条码等步骤,实现了从开始扫描到停止扫描的完整流... 目录实现步骤:代码实现1. 安装依赖2. vue 页面代码功能说明注意事项以下是一个基于 Vue.js

CSS @media print 使用详解

《CSS@mediaprint使用详解》:本文主要介绍了CSS中的打印媒体查询@mediaprint包括基本语法、常见使用场景和代码示例,如隐藏非必要元素、调整字体和颜色、处理链接的URL显示、分页控制、调整边距和背景等,还提供了测试方法和关键注意事项,并分享了进阶技巧,详细内容请阅读本文,希望能对你有所帮助...

Nginx实现前端灰度发布

《Nginx实现前端灰度发布》灰度发布是一种重要的策略,它允许我们在不影响所有用户的情况下,逐步推出新功能或更新,通过灰度发布,我们可以测试新版本的稳定性和性能,下面就来介绍一下前端灰度发布的使用,感... 目录前言一、基于权重的流量分配二、基于 Cookie 的分流三、基于请求头的分流四、基于请求参数的分

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.