Leetcode hot 100之回溯O(N!):选择/DFS

目录

框架:排列/组合/子集

元素无重不可复选

全排列

子集

组合:[1, n] 中的 k 个数

分割成回文串

元素无重不可复选:排序,多条值相同的只遍历第一条

子集/组合

先进行排序,让相同的元素靠在一起,如果发现 nums[i] == nums[i-1],则跳过 

排列

元素无重可复选

子集/组合:sum=target

排列:去除 used 剪枝

N皇后


如果不能成功,那么返回的时候我们就还要把这个位置还原。这就是回溯算法,也是试探算法。

解决一个回溯问题,实际上就是一个决策树的遍历过程

1、路径:已选择。

2、选择列表:可选择。

3、结束条件:无选择。

框架:排列/组合/子集

result = []
function backtrack(路径, 选择列表):if 满足结束条件:result.add(路径)result.push([...path])或者result.push(path.slice())//path还会改变,所以不能传引用地址returnfor 选择 in 选择列表:做选择pushbacktrack(路径, 选择列表)撤销选择pop数组(有一定的剪枝,不用判断是否use)
const backtrack = (start) => {// 回溯算法标准框架for (let i = start; i < nums.length; i++) {// 做选择track.push(nums[i]);// 回溯遍历下一层节点backtrack(i + 1);// 撤销选择track.pop();}};backtrack(0);
图
function backtrack(nums, used, track, res) {for (let i = 0; i < nums.length; i++) {if (used[i]) {continue;}track.push(nums[i]);used[i] = true;backtrack(nums, used, track, res);track.pop();used[i] = false;}
}全排列中
做选择/撤销选择 可用if(path.includes(item)) continue;代替

元素无重不可复选

全排列

key:

  1. path.length == string.length
  2. path.includes(item)
const _permute = string => {const res = [];const backtrace = path => {if(path.length == string.length){res.push(path);return;}for(const item of string) {if(path.includes(item)) continue;backtrace(path + item);}};backtrace('');return res;
}

子集

输入一个无重复元素的数组 nums,其中每个元素最多使用一次,请你返回 nums 的所有子集

比如输入 nums = [1,2,3],算法应该返回如下子集:

[ [],[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3] ]

/*** @param {number[]} nums* @return {number[][]}*/
var subsets = function(nums) {// 用于存储结果const res = [];// 用于记录回溯路径const track = [];/*** 回溯算法的核心函数,用于遍历子集问题的回溯树* @param {number} start - 控制树枝的遍历,避免产生重复子集*/const backtrack = (start) => {// 前序遍历位置,每个节点的值都是一个子集res.push([...track]);// 回溯算法标准框架for (let i = start; i < nums.length; i++) {// 做选择track.push(nums[i]);// 回溯遍历下一层节点backtrack(i + 1);// 撤销选择track.pop();}};backtrack(0);return res;
};

组合:[1, n] 中的 k 个数

返回范围 [1, n] 中所有可能的 k 个数的组合,剪枝

let result = []
let path = []
var combine = function(n, k) {result = []combineHelper(n, k, 1)return result
};
const combineHelper = (n, k, startIndex) => {if (path.length === k) {result.push([...path])return}for (let i = startIndex; i <= n - (k - path.length) + 1; ++i) {path.push(i)combineHelper(n, k, i + 1)path.pop()}
}

分割成回文串

  • 组合问题:选取一个a之后,在bcdef中再去选取第二个,选取b之后在cdef中再选取第三个.....。
  • 切割问题:切割一个a之后,在bcdef中再去切割第二段,切割b之后在cdef中再切割第三段.....。

/*** @param {string} s* @return {string[][]}*/
const isPalindrome = (s, l, r) => {for (let i = l, j = r; i < j; i++, j--) {if(s[i] !== s[j]) return false;}return true;
}var partition = function(s) {const res = [], path = [], len = s.length;backtracking(0);return res;function backtracking(startIndex) {if(startIndex >= len) {res.push(Array.from(path));return;}for(let i = startIndex; i < len; i++) {if(!isPalindrome(s, startIndex, i)) continue;path.push(s.slice(startIndex, i + 1));backtracking(i + 1);path.pop();}}
};

元素无重不可复选:排序,多条值相同的只遍历第一条

子集/组合

nums = [1,2,2],你应该输出:

[ [],[1],[2],[1,2],[2,2],[1,2,2] ]

如果一个节点有多条值相同的树枝相邻,则只遍历第一条,剩下的都剪掉,不要去遍历:

先进行排序,让相同的元素靠在一起,如果发现 nums[i] == nums[i-1],则跳过 

排列

// 注意:javascript 代码由 chatGPT🤖 根据我的 java 代码翻译,旨在帮助不同背景的读者理解算法逻辑。
// 本代码还未经过力扣测试,仅供参考,如有疑惑,可以参照我写的 java 代码对比查看。/*** @param {number[]} nums* @return {number[][]}*/var permuteUnique = function(nums) {let res = [];let track = [];let used = new Array(nums.length).fill(false);// 先排序,让相同的元素靠在一起nums.sort((a, b) => a - b);backtrack(nums, used, track, res);return res;
};/*** @param {number[]} nums* @param {boolean[]} used* @param {number[]} track* @param {number[][]} res*/
function backtrack(nums, used, track, res) {if (track.length === nums.length) {res.push(track.slice());return;}for (let i = 0; i < nums.length; i++) {if (used[i]) {continue;}// 新添加的剪枝逻辑,固定相同的元素在排列中的相对位置if (i > 0 && nums[i] === nums[i - 1] && !used[i - 1]) {continue;}track.push(nums[i]);used[i] = true;backtrack(nums, used, track, res);track.pop();used[i] = false;}
}

元素无重可复选

子集/组合:sum=target

想让每个元素被重复使用,我只要把 i + 1 改成 i 即可

给之前的回溯树添加了一条树枝,在遍历这棵树的过程中,一个元素可以被无限次使用

这棵回溯树会永远生长下去,所以我们的递归函数需要设置合适的 base case 以结束算法,即路径和大于 target 时就没必要再遍历下去了 

排列:去除 used 剪枝

N皇后

在 n * n 的棋盘上要摆 n 个皇后,
要求:任何两个皇后不同行,不同列不在同一条斜线上,
求给一个整数 n ,返回 n 皇后的摆法数。

要求:空间复杂度 O(1) ,时间复杂度O(n!)

  1. 要确定皇后的位置,其实就是确定列的位置,因为行已经固定了
  2. 进一步讲,也就是如何摆放 数组arr [0,1,2,3,...,n-1]
  3. 如果没有【不在同一条斜线上】要求,这题其实只是单纯的全排列问题
  4. 在全排列的基础上,根据N皇后的问题,去除一些结果
  • arr :n个皇后的列位置

  • res :n皇后排列结果

  • ruler: 记录对应的列位置是否已经占用(也是是否有皇后),如果有,那么设为1,没有设为0

  • setPos :哈希集合,标记正斜线(从左上到右下)位置,如果在相同正斜线上,坐标(x,y)满足 y-x 都相同,(y1 - x1)应该等于(y2 - x2)

  • setCon :哈希集合,标记反正斜线(从y右上到左下)位置,如果在相同反斜线上,坐标(x,y)满足 x+y 都相同,(x1 + y1)应该等于(x2 + y2)

  • 是否在同一斜线上,其实就是这两个点的所形成的斜线的斜率是否为±1。点P(a,b) ,点Q(c,d)

    (1)斜率为1 (d-b)/(c-a) = 1,横纵坐标之差相等

    (2)斜率为-1 (d-b)/(c-a) = -1 ,等式两边恒等变形 a+b = c + d ,横纵坐标之和相等

/**** @param n int整型 the n* @return int整型*/
function Nqueen(n) {let res = []; //二维数组,存放每行Q的列坐标let isQ = new Array(n).fill(0); //记录该列是否有Qlet setPos = new Set(); //标记正对角线let setCon = new Set(); // 标记反对角线//给当前row找一个colconst backTrace = (row, path) => {if (path.length === n) {res.push(path);return;}for (let col = 0; col < n; col++) {if (isQ[col] == 0 &&!setPos.has(row - col) &&!setCon.has(row + col)) {path.push(col);isQ[col] = 1;setPos.add(row - col);setCon.add(row + col);backTrace(row + 1, path);path.pop();isQ[col] = 0;setPos.delete(row - col);setCon.delete(row + col);}}};backTrace(0, []);return res.length;
}
module.exports = {Nqueen: Nqueen,
};

动态规划的暴力求解阶段就是回溯算法。只是有的问题具有重叠子问题性质,可以用 dp table 或者备忘录优化,将递归树大幅剪枝,这就变成了动态规划。

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

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

相关文章

VMProtect使用教程(VC++MFC中使用)

VMProtect使用教程(VCMFC中使用) VMProtect是一种商业级别的代码保护工具&#xff0c;可以用于保护VC MFC程序。以下是使用VMProtect保护VC MFC程序的步骤&#xff1a; 1. 下载并安装VMProtect,C包含库及目录。 2. 在VC MFC项目中添加VMProtectSDK.h头文件&#xff0c;并在需…

Android ncnn-android-yolov8-seg源码解析 : 实现人像分割

1. 前言 上篇文章&#xff0c;我们已经将人像分割的ncnn-android-yolov8-seg项目运行起来了&#xff0c;后续文章我们会抽取出Demo中的核心代码&#xff0c;在自己的项目中&#xff0c;来接入人体识别和人像分割功能。 先来看下效果&#xff0c;整个图像的是相机的原图&#…

番外--Task2:

任务&#xff1a;root与普通用户的互切&#xff08;区别&#xff09;&#xff0c;启动的多用户文本见面与图形界面的互切命令&#xff08;区别&#xff09;。 输入图示命令&#xff0c;重启后就由图形界面转成文本登录界面&#xff1b; 输入图示命令&#xff0c;重启后就由文本…

java实验(头歌)--类的继承以及抽象类的定义和使用

文章目录 第一题第二题第三题 第一题 import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Scanner;//把main 函数的给替换了 public static vo…

MybatisPlus01

MybatisPlus01 1.MybatisPlus初体验 1.1首先要引入MybatisPlus的依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version></dependency>1.2定义Mapp…

【JavaEE初阶】 Thread类及常见方法

文章目录 &#x1f334;Thread类的概念&#x1f333;Thread 的常见构造方法&#x1f384;Thread 的几个常见属性&#x1f340;start()-启动一个线程&#x1f332;中断一个线程&#x1f6a9;实例一&#x1f6a9;实例二&#x1f6a9;实例三 &#x1f38d;join()-等待一个线程&…

想要开发一款游戏, 需要注意什么?

开发一款游戏是一个复杂而令人兴奋的过程。游戏开发是指创建、设计、制作和发布电子游戏的过程。它涵盖了从最初的概念和创意阶段到最终的游戏发布和维护阶段的各个方面。 以下是一些需要注意的关键事项&#xff1a; 游戏概念和目标&#xff1a; 确定游戏开发的核心概念和目标…

【SpringBoot】| Thymeleaf 模板引擎

目录 Thymeleaf 模板引擎 1. 第一个例子 2. 表达式 ①标准变量表达式 ②选择变量表达式&#xff08;星号变量表达式&#xff09; ③链接表达式&#xff08;URL表达式&#xff09; 3. Thymeleaf的属性 ①th:action ②th:method ③th:href ④th:src ⑤th:text ⑥th:…

TCP相关面试题

TCP相关面试题 题目1 介绍一下TCP三次握手的过程 介绍TCP三次握手应该从3个方面进行回答&#xff0c;分别是数据包名称&#xff0c;客户端与服务端的状态变化&#xff0c;数据包的序号变化。而不能只是简单回答发送的数据包名称。 TCP三次握手的过程如下&#xff1a; 从数据…

NSSCTF [BJDCTF 2020]easy_md5 md5实现sql

开局一个框 啥都没有用 然后我们进行抓包 发现存在提示 这里是一个sql语句 看到了 是md5加密后的 这里也是看了wp 才知道特殊MD5 可以被识别为 注入的万能钥匙 ffifdyopmd5 加密后是 276F722736C95D99E921722CF9ED621C转变为字符串 后是 or6 乱码这里就可以实现 注入 所…

定时任务详解

1、定时任务 在公司做项目时&#xff0c;经常遇到要用到定时任务的事情&#xff0c;但是对定时任务不熟练的时候会出现重复任务的情况&#xff0c;不深入&#xff0c;这次将定时任务好好学习分析一下 定时任务的原理 假如我将一个现场通过不断轮询的方式去判断&#xff0c;就…

理解一致性哈希算法

摘要&#xff1a;一致性哈希是什么&#xff0c;使用场景&#xff0c;解决了什么问题&#xff1f; 本文分享自华为云社区《16 张图解 &#xff5c; 一致性哈希算法》&#xff0c;作者&#xff1a;小林coding。 如何分配请求&#xff1f; 大多数网站背后肯定不是只有一台服务器…

文件格式转换

把我的悲惨故事说给大家乐呵乐呵&#xff1a;老板让运营把一些数据以json格式给我&#xff0c;当我看到运营在石墨文档上编辑的时候我人都傻了&#xff0c;我理解运营的艰难&#xff0c;可我也是真的难啊&#xff0c;在石墨文档编辑的眼花缭乱的&#xff0c;很多属性都错乱了(诸…

[SWPUCTF 2021 新生赛]sql - 联合注入

这题可以参考文章&#xff1a;[SWPUCTF 2021 新生赛]easy_sql - 联合注入||报错注入||sqlmap 这题相比于参考文章的题目多了waf过滤 首先&#xff0c;仍然是网站标题提示参数是wllm 1、fuzz看哪些关键字被过滤&#xff1a;空格、substr、被过滤 2、?wllm-1/**/union/**/selec…

微信小程序 movable-area 区域拖动动态组件演示

movable-area 组件在小程序中的作用是用于创建一个可移动的区域&#xff0c;可以在该区域内拖动视图或内容。这个组件常用于实现可拖动的容器或可滑动的列表等交互效果。 使用 movable-area 组件可以对其内部的 movable-view 组件进行拖动操作&#xff0c;可以通过设置不同的属…

消息驱动 —— SpringCloud Stream

Stream 简介 Spring Cloud Stream 是用于构建消息驱动的微服务应用程序的框架&#xff0c;提供了多种中间件的合理配置 Spring Cloud Stream 包含以下核心概念&#xff1a; Destination Binders&#xff1a;目标绑定器&#xff0c;目标指的是 Kafka 或者 RabbitMQ&#xff0…

信息增益,经验熵和经验条件熵——决策树

目录 1.经验熵 2.经验条件熵 3.信息增益 4.增益比率 5.例子1 6.例子2 在决策树模型中&#xff0c;我们会考虑应该选择哪一个特征作为根节点最好&#xff0c;这里就用到了信息增益 通俗上讲&#xff0c;信息增益就是在做出判断时&#xff0c;该信息对你影响程度的大小。比…

抖音seo源代码开源部署----基于开放平台SaaS服务

抖音SEO搜索是什么&#xff1f; 抖音SEO搜索是指在抖音平台上进行搜索引擎优化&#xff08;Search Engine Optimization&#xff09;的一种技术手段。 通过优化抖音账号、发布内容和关键词等&#xff0c;提高抖音视频在搜索结果中的排名&#xff0c;从而增加视频曝光量和用户点…

ValueError: high is out of bounds for int32 报错

问题描述&#xff1a; 笔者在Windows 64位平台跑一个在Ubuntu上运行正常的程序时&#xff0c;出现了以下报错&#xff1a; 具体为&#xff1a; seed np.random.randint(0, 2 ** 32) # make a seed with numpy generatorFile "mtrand.pyx", line 763, in numpy.ra…

Moonbeam Ignite强势回归

参与Moonbeam上最新的流动性计划 还记得新一轮的流动性激励计划吗&#xff1f;Moonbeam Ignite社区活动带着超过300万枚GLMR奖励来啦&#xff01;体验新项目&#xff0c;顺便薅一把GLMR羊毛。 本次Moonbeam Ignite活动的参与项目均为第二批Moonbeam生态系统Grant资助提案中获…