07、JS实现:用回溯法实现数组全排列的算法(一步一步剖析,很详细)

news2024/10/7 16:25:59

回溯法实现数组全排列的算法

  • Ⅰ、回溯法实现数组全排列:
    • 1、题目描述:
    • 2、解题思路:
    • 3、实现代码:
  • Ⅱ、小结:

Ⅰ、回溯法实现数组全排列:

1、题目描述:

给定⼀个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输⼊: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

2、解题思路:

以 [1,2,3] 为例,根据回溯思想,我们的逻辑是:
先从 [1,2,3] 选取⼀个数。
然后继续从 [1,2,3] 选取⼀个数,并且这个数不能是已经选取过的数。
重复这个过程直到选取的数字个数达到了 3。


可能存在的问题:
其一、如何确保这个数不能是已经选取过的数?
答:我们可以直接在已经选取的数字中线性查找,也可以将已经选取的数字中放到 hashset 中,这样就可以在
O(1)的时间来判断是否已经被选取了,只不过需要额外的空间;

3、实现代码:

其一、代码为:


// 此时的 list 是空数组、tempList 是空数组、nums 是没有重复数字的数组;
const backtrack = (list, tempList, nums) => {
  if (tempList.length === nums.length) return list.push([...tempList])
  for (let i = 0; i < nums.length; i++) {
    // 此时 continue 的作用:若 if() 满足条件,则会跳出本次的 for 循环;
    if (tempList.includes(nums[i])) continue
    tempList.push(nums[i])
    backtrack(list, tempList, nums)
    tempList.pop()
  }
}

const permute = (nums) => {
  const list = []
  backtrack(list, [], nums)
  return list
}


// 此时的输出结果为:[ [ 1, 2, 3 ], [ 1, 3, 2 ], [ 2, 1, 3 ], [ 2, 3, 1 ], [ 3, 1, 2 ], [ 3, 2, 1 ] ];
permute([1, 2, 3])




    执行  backtrack(list, [], nums)  函数后代码执行的过程(即:执行 backtrack([], [], [1,2,3]) 函数):
    
    注意:里面涉及多个循环嵌套递归,每次递归的出口为 tempList.length === nums.length,循环的出口为 for() 循环结束;


第一层循环,在 i=0 的情况下:
	if (tempList.includes(nums[i])) continue 的判断不成功;
	执行 tempList.push(nums[0]) 代码后,tempList 数组为 [1];
	backtrack(list, tempList, nums) 执行代码为:backtrack([], [1], [1,2,3])
	
	第二层循环,在 i=0 的情况下:
		if (tempList.includes(nums[i])) continue 的判断成功,因此直接跳出此次的 for() 循环的过程;
		
	第二层循环,在 i=1 的情况下:
		if (tempList.includes(nums[i])) continue 的判断不成功;
		执行 tempList.push(nums[1]) 代码后,tempList 数组为 [1,2];
		backtrack(list, tempList, nums) 执行代码为:backtrack([], [1,2], [1,2,3])
		
		第三层循环,在 i=0 的情况下:
			if (tempList.includes(nums[i])) continue 的判断成功,因此直接跳出此次的 for() 循环的过程;
			
		第三层循环,在 i=1 的情况下:
			if (tempList.includes(nums[i])) continue 的判断成功,因此直接跳出此次的 for() 循环的过程;
			
		第三层循环,在 i=2 的情况下:
			if (tempList.includes(nums[i])) continue 的判断不成功;
			执行 tempList.push(nums[2]) 代码后,tempList 数组为 [1,2,3];
			backtrack(list, tempList, nums) 执行代码为:backtrack([], [1,2,3], [1,2,3])
			if (tempList.length === nums.length) return list.push([...tempList]),list 的数组为[[1,2,3]];
			
			执行 tempList.pop() 代码后,tempList 数组为 [1,2];
			
		执行 tempList.pop() 代码后,tempList 数组为 [1];

	第二层循环,在 i=2 的情况下:
		if (tempList.includes(nums[i])) continue 的判断不成功;
		执行 tempList.push(nums[2]) 代码后,tempList 数组为 [1,3];
		backtrack(list, tempList, nums) 执行代码为:backtrack([[1,2,3]], [1,3], [1,2,3])
		
		第三层循环,在 i=0 的情况下:
			if (tempList.includes(nums[i])) continue 的判断成功,因此直接跳出此次的 for() 循环的过程;
			
		第三层循环,在 i=1 的情况下:
			if (tempList.includes(nums[i])) continue 的判断不成功;
			执行 tempList.push(nums[1]) 代码后,tempList 数组为 [1,3,2];
			backtrack(list, tempList, nums) 执行代码为:backtrack([[1,2,3]], [1,3,2], [1,2,3])
			if (tempList.length === nums.length) return list.push([...tempList]),list 的数组为[[1,2,3],[1,3,2]];
			
			执行 tempList.pop() 代码后,tempList 数组为 [1,3];
			
		第三层循环,在 i=2 的情况下:
			if (tempList.includes(nums[i])) continue 的判断成功,因此直接跳出此次的 for() 循环的过程;			

		执行 tempList.pop() 代码后,tempList 数组为 [1];
		
	执行 tempList.pop() 代码后,tempList 数组为 [];


第一层循环,在 i=1 的情况下:
	if (tempList.includes(nums[i])) continue 的判断不成功;
	执行 tempList.push(nums[1]) 代码后,tempList 数组为 [2];
	backtrack(list, tempList, nums) 执行代码为:backtrack([[1,2,3],[1,3,2]], [2], [1,2,3])
	
	第二层循环,在 i=0 的情况下:
		if (tempList.includes(nums[i])) continue 的判断不成功;
		执行 tempList.push(nums[0]) 代码后,tempList 数组为 [2,1];
		backtrack(list, tempList, nums) 执行代码为:backtrack([[1,2,3],[1,3,2]], [2,1], [1,2,3])
		
		第三层循环,在 i=0 的情况下:
			if (tempList.includes(nums[i])) continue 的判断成功,因此直接跳出此次的 for() 循环的过程;
			
		第三层循环,在 i=1 的情况下:
			if (tempList.includes(nums[i])) continue 的判断成功,因此直接跳出此次的 for() 循环的过程;
			
		第三层循环,在 i=2 的情况下:
			if (tempList.includes(nums[i])) continue 的判断不成功;
			执行 tempList.push(nums[2]) 代码后,tempList 数组为 [2,1,3];
			backtrack(list, tempList, nums) 执行代码为:backtrack([[1,2,3],[1,3,2]], [2,1,3], [1,2,3])
			if (tempList.length === nums.length) return list.push([...tempList]),list 的数组为[[1,2,3],[1,3,2],[2,1,3]];
			
			执行 tempList.pop() 代码后,tempList 数组为 [2,1];
			
		执行 tempList.pop() 代码后,tempList 数组为 [2];
		
	第二层循环,在 i=1 的情况下:
		if (tempList.includes(nums[i])) continue 的判断成功,因此直接跳出此次的 for() 循环的过程;

	第二层循环,在 i=2 的情况下:
		if (tempList.includes(nums[i])) continue 的判断不成功;
		执行 tempList.push(nums[2]) 代码后,tempList 数组为 [2,3];
		backtrack(list, tempList, nums) 执行代码为:backtrack([[1,2,3],[1,3,2],[2,1,3]], [2,3], [1,2,3])
		
		第三层循环,在 i=0 的情况下:
			if (tempList.includes(nums[i])) continue 的判断不成功;
			执行 tempList.push(nums[0]) 代码后,tempList 数组为 [2,3,1];
			backtrack(list, tempList, nums) 执行代码为:backtrack([[1,2,3],[1,3,2],[2,1,3]], [2,3,1], [1,2,3])
			if (tempList.length === nums.length) return list.push([...tempList]),list 的数组为[[1,2,3],[1,3,2],[2,1,3],[2,3,1]];
			
			执行 tempList.pop() 代码后,tempList 数组为 [2,3];
			
		第三层循环,在 i=1 的情况下:
			if (tempList.includes(nums[i])) continue 的判断成功,因此直接跳出此次的 for() 循环的过程;
			
		第三层循环,在 i=2 的情况下:
			if (tempList.includes(nums[i])) continue 的判断成功,因此直接跳出此次的 for() 循环的过程;			
			
		执行 tempList.pop() 代码后,tempList 数组为 [2];
	
	执行 tempList.pop() 代码后,tempList 数组为 [];


第一层循环,在 i=2 的情况下:
	if (tempList.includes(nums[i])) continue 的判断不成功;
	执行 tempList.push(nums[2]) 代码后,tempList 数组为 [3];
	backtrack(list, tempList, nums) 执行代码为:backtrack([[1,2,3],[1,3,2],[2,1,3],[2,3,1]], [3], [1,2,3])
	
	第二层循环,在 i=0 的情况下:
		if (tempList.includes(nums[i])) continue 的判断不成功;
		执行 tempList.push(nums[0]) 代码后,tempList 数组为 [3,1];
		backtrack(list, tempList, nums) 执行代码为:backtrack([[1,2,3],[1,3,2],[2,1,3],[2,3,1]], [3,1], [1,2,3])
		
		第三层循环,在 i=0 的情况下:
			if (tempList.includes(nums[i])) continue 的判断成功,因此直接跳出此次的 for() 循环的过程;
			
		第三层循环,在 i=1 的情况下:
			if (tempList.includes(nums[i])) continue 的判断不成功;
			执行 tempList.push(nums[1]) 代码后,tempList 数组为 [3,1,2];
			backtrack(list, tempList, nums) 执行代码为:backtrack([[1,2,3],[1,3,2],[2,1,3],[2,3,1]], [3,1,2], [1,2,3])
			if (tempList.length === nums.length) return list.push([...tempList]),list 的数组为[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2]];
			
			执行 tempList.pop() 代码后,tempList 数组为 [3,1];
			
		第三层循环,在 i=2 的情况下:
			if (tempList.includes(nums[i])) continue 的判断成功,因此直接跳出此次的 for() 循环的过程;
			
		执行 tempList.pop() 代码后,tempList 数组为 [3];
		
	第二层循环,在 i=1 的情况下:
		if (tempList.includes(nums[i])) continue 的判断不成功;
		执行 tempList.push(nums[1]) 代码后,tempList 数组为 [3,2];
		backtrack(list, tempList, nums) 执行代码为:backtrack([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2]], [3,2], [1,2,3])
		
		第三层循环,在 i=0 的情况下:
			if (tempList.includes(nums[i])) continue 的判断不成功;
			执行 tempList.push(nums[0]) 代码后,tempList 数组为 [3,2,1];
			backtrack(list, tempList, nums) 执行代码为:backtrack([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2]], [3,2,1], [1,2,3])
			if (tempList.length === nums.length) return list.push([...tempList]),list 的数组为[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]];
			
			执行 tempList.pop() 代码后,tempList 数组为 [3,2];
			
		第三层循环,在 i=1 的情况下:
			if (tempList.includes(nums[i])) continue 的判断成功,因此直接跳出此次的 for() 循环的过程;
			
		第三层循环,在 i=2 的情况下:
			if (tempList.includes(nums[i])) continue 的判断成功,因此直接跳出此次的 for() 循环的过程;			
			
		执行 tempList.pop() 代码后,tempList 数组为 [3];

	第二层循环,在 i=2 的情况下:
		if (tempList.includes(nums[i])) continue 的判断成功,因此直接跳出此次的 for() 循环的过程;
	
执行 tempList.pop() 代码后,tempList 数组为 [];


因此,最后 list 的输出结果为:[ [ 1, 2, 3 ], [ 1, 3, 2 ], [ 2, 1, 3 ], [ 2, 3, 1 ], [ 3, 1, 2 ], [ 3, 2, 1 ] ],
而 tempList 的输出结果为:[];


其二、截图为:

在这里插入图片描述

Ⅱ、小结:

其一、哪里有不对或不合适的地方,还请大佬们多多指点和交流!
其二、若有转发或引用本文章内容,请注明本博客地址(直接点击下面 url 跳转) https://blog.csdn.net/weixin_43405300,创作不易,且行且珍惜!
其三、有兴趣的话,可以多多关注这个专栏(Vue(Vue2+Vue3)面试必备专栏)(直接点击下面 url 跳转):https://blog.csdn.net/weixin_43405300/category_11525646.html?spm=1001.2014.3001.5482

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1548811.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DBeaver修改sql语句保存位置

1、dbeaver通过工作空间方式来管理Script的sql脚本以及数据库连接。 工作空间&#xff0c;其实也就是一个文件夹 默认保存路径查看&#xff1a; 文件--> 切换工作空间 --> 其他 sql脚本的保存位置默认在工作空间下的 \General\Scripts 文件夹中。 2、 3、点击浏览&#…

海外业务运营 别让资金支出管控成为开疆拓土的“绊脚石”

据《中国企业出海信心报告》显示,超六成企业有海外业务拓展计划。成熟出海企业将把目光放至新市场,新锐出海企业更聚焦新业务线的开辟。其中,当属高端制造业的出海步伐“迈得早”、“迈得大”。 高端制造业主要包含医药制造业,通用设备制造业,专用设备制造业,汽车制造业,通信设…

如何用Flask中的Blueprints构建大型Web应用

本文分享自华为云社区《构建大型Web应用Flask中的Blueprints指南》&#xff0c;作者&#xff1a; 柠檬味拥抱。 什么是Blueprints&#xff1f; 什么是Blueprints&#xff1f; Blueprints是Flask中的一种模式&#xff0c;用于将应用程序分解为可重用的模块。每个蓝图实际上是…

23届嵌入式被裁,有什么好的就业建议?

最近看到了一个提问&#xff0c;原话如下&#xff1a; 本人23届毕业生&#xff0c;就业方向嵌入式软件&#xff0c;坐标深圳&#xff0c;工作3月公司裁员&#xff0c;目前接近12月开始找工作。 boss上投递简历&#xff0c;校招岗&#xff0c;比较有规模的好公司基本已读不回&am…

浅析扩散模型与图像生成【应用篇】(十二)——DiffI2I

12. DiffI2I: Efficient Diffusion Model for Image-to-Image Translation 该文提出一种基于扩散模型的图像到图像的转换算法&#xff08;DiffI2I&#xff09;,可用于图像修复、超分辨率提升、图像去模糊、语义分割等任务。作者指出一般的扩散模型&#xff0c;虽然在图像生成任…

C++STL学习之unordered_map与unordered_set(底层Hash)

前言&#xff1a;我们前面已经学习论map和set&#xff0c;现在又冒出来一个unordered_map和unordered_set&#xff0c;这两个有啥差别吗&#xff1f;前面我们已经说过&#xff0c;map和set的底层是红黑树&#xff0c;那unordered_map和unordered_set的底层是什么呢&#xff1f;…

基于nodejs+vue“共享书角”图书借还管理系统python-flask-django-php

同时还能为借阅者提供一个方便实用的“共享书角”图书借还管理系统&#xff0c;使得借阅者能够及时地找到合适自己的图书借还信息。管理员在使用本系统时&#xff0c;可以通过后台管理员界面管理借阅者的信息&#xff0c;也可以发布系统公告&#xff0c;让借阅者及时了解图书借…

免杀对抗-C2远控篇CC++SC转换格式UUID标识MAC物理IPV4地址减少熵值

参考文章&#xff1a; https://github.com/INotGreen/Bypass-AMSI https://mp.weixin.qq.com/s/oJ8eHdX8HGuk6dZv0kmFxg https://kyxiaxiang.github.io/2022/12/14/AMSIandEtw https://github.com/S3cur3Th1sSh1t/Amsi-Bypass-Powershell 文章参考&#xff1a; https://www.…

Go——结构体

Go语言中没有类的概念&#xff0c;也不支持类的继承等面向对象的概念。Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性。 一. 类型别名和自定义类型 1.1 自定义类型 在Go语言中有一些基本的数据类型&#xff0c;如string&#xff0c;整型&#xff0c;…

YOLOv9改进策略:block优化 | SEAM提升小目标遮挡物性能

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文改进内容&#xff1a;SEAM提升小目标遮挡物性能&#xff0c;在多个数据集得到很好的验证 改进结构图如下&#xff1a; YOLOv9魔术师专栏 ☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️ ☁️☁️☁️…

docker学习笔记 四-----docker基本使用方法

基础命令奉上&#xff1a; 1、docker命令查询方法 docker --help 获取docker命令帮助 docker search --help 查询docker 子命令search的帮助 2、查询镜像 查询镜像 docker search 192.168.206.100:5000/mysql 查询指定服务器指定镜像 docker search mysql …

Qt Design Studio各个组件怎么用?【长期更新】

写在前面&#xff1a;本文长期更新&#xff0c;建议点赞/收藏/关注~ 在Qt Design Studio中&#xff0c;组件类别有&#xff1a; 每一种&#xff0c;都有其特定的用途和适用场景&#xff1a; 1.My Components 使用时机&#xff1a;当你需要重用自定义的设计元素或者特殊功能…

【wallabag】数字化阅读时代的救星——wallabag来了!

为什么需要 ✨在海量信息涌动的时代&#xff0c;一篇好文章却总是在最不恰当的时候出现。想要留住精彩内容&#xff0c;却苦于当下分身乏术&#xff1f;墙裂推荐你试试wallabag&#xff0c;这款智能稍后阅读神器&#xff0c;让你随时随地畅享深度阅读的乐趣&#xff01; Walla…

2024第六届环境科学与可再生能源国际会议能源 (ESRE 2024) 即将召开!

2024第六届环境科学与可再生能源国际会议 能源 &#xff08;ESRE 2024&#xff09; 即将举行 2024 年 6 月 28 日至 30 日在德国法兰克福举行。ESRE 2024 年 旨在为研究人员、从业人员和专业人士提供一个论坛 从工业界、学术界和政府到研究和 发展&#xff0c;环境科学领域的专…

Altair(澳汰尔) Radioss® 评估和优化动态载荷下的高度非线性问题

Altair&#xff08;澳汰尔&#xff09; Radioss 评估和优化动态载荷下的高度非线性问题 Radioss 是一款超前的分析解决方案&#xff0c;可评估和优化动态载荷下的高度非线性问题。它广泛应用于全球各行各业&#xff0c;能有效提高复杂设计的耐撞性、安全性和可制造性。 30 多…

VRAY渲染设置大神参数(建议收藏)

3dmax效果图云渲染平台——渲染100以3ds Max 2024、VR 6.2、CR 11.2等最新版本为基础&#xff0c;兼容fp、acescg等常用插件&#xff0c;同时LUT滤镜等参数也得到了同步支持。注册填邀请码【7788】可领30元礼包和免费渲染券哦~ 公用&#xff1a;输出大小&#xff1a;一般小图50…

数据结构-----栈、顺序栈、链栈

在软件应用中&#xff0c;栈这种后进先出数据结构的应用是非常普遍的。比如用浏览器上网时&#xff0c;不管什么浏览器都有一个“后退”键&#xff0c;你点击后可以按访问顺序的逆序加载浏览过的网页。即使从一个网页开始&#xff0c;连续点了几十个链接跳转&#xff0c;你点“…

纳斯达克大屏媒体尺寸与投放费用:一次投放需要多少钱?

纳斯达克大屏媒体尺寸与投放费用&#xff1a;一次投放需要多少钱&#xff1f; 1. 纳斯达克图片要求 1.1 像素要求 高度&#xff1a;2336 像素宽度&#xff1a;1832 像素 1.2 分辨率要求 像素比率&#xff1a;1.0 px 72 dpi 1.3 文件格式要求 静态图片格式&#xff1a;.…

vue2 export default写法,computed、methods的使用

<template><div><h2>{{nameAll}}</h2><h2>{{method}}</h2><h2>{{tt()}}</h2><h2>{{firstName}}</h2><h2>更新后赋值数据&#xff1a;{{lastName}}</h2><h2>赋值数据:{{writeValue}}</h2>…

第十三届蓝桥杯省赛C++ A组 Java A组/研究生组《推导部分和》(C++)

【题目描述】 【输入格式】 【输出格式】 【数据范围】 【输入样例】 5 3 3 1 5 15 4 5 9 2 3 5 1 5 1 3 1 2 【输出样例】 15 6 UNKNOWN 【思路】 题解来源&#xff1a;AcWing 4651. $\Huge\color{gold}{推导部分和}$ - AcWing 【代码】 #include<bits/stdc.h> #define…