十五周算法训练营——回溯算法

news2024/12/23 17:26:14

今天是十五周算法训练营的第十周,主要讲回溯算法专题。(欢迎加入十五周算法训练营,与小伙伴一起卷算法)

解决一个回溯问题,实际上就是一个决策树的遍历过程,只需要思考三个问题:

  1. 路径:也就是已经做出的选择

  2. 选择列表:也就是你当前可以做的选择

  3. 结束条件:也就是到达决策树底层,无法再做选择的条件

全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:

输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

function permute(nums) {
    const result = [];

    /**
     * 路径:记录到track中
     * 选择列表:不存在与nums中的哪些数
     * 结束条件:当nums中的元素全部在track中出现
     */
    const backtrack = (track, nums) => {
        // 结束条件
        if (track.length === nums.length) {
            result.push([...track]);
            return;
        }

        for (let i = 0; i < nums.length; i++) {
            if (track.includes(nums[i])) {
                continue;
            }

            // 做选择
            track.push(nums[i]);
            // 进入下一层
            backtrack(track, nums);
            // 取消选择
            track.pop();
        }
    };

    const track = [];

    backtrack(track, nums);

    return result;
}

const nums = [1, 2, 3];
console.log(permute(nums));

全排列II

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

示例 1:

输入:nums = [1,1,2] 输出: [[1,1,2], [1,2,1], [2,1,1]]

function permuteUnique(nums) {
    const result = [];

    /**
     * 路径:记录到track中
     * 选择列表:当前没有被访问过,且前一个值若为相通知,且被访问过
     * 结束条件:全部值都被访问了一遍
     */
    const backtrack = (sortNums, track, visits) => {
        if (track.length === sortNums.length) {
            result.push([...track]);
            return;
        }

        for (let i = 0; i < sortNums.length; i++) {
            // i > 0 && nums[i] === nums[i - 1] && !visits[i - 1]
            // 上述一行的判断是当前值和前一个值相等,但是前一个值没有被访问过,表示前一个值不在该值路径的上几层,表示在同一层前面出现过,所以该值要跳过
            if (visits[i] || (i > 0 && sortNums[i] === sortNums[i - 1] && !visits[i - 1])) {
                continue;
            }

            // 做选择
            track.push(sortNums[i]);
            visits[i] = 1;

            // 选择下一层
            backtrack(sortNums, track, visits);

            // 取消选择
            track.pop();
            visits[i] = 0;
        }
    };

    const track = [];

    nums.sort((a, b) => a - b);

    const visits = (new Array(nums.length)).fill(0);

    backtrack(nums, track, visits);

    return result;
}

const nums = [1, 1, 2];
console.log(permuteUnique(nums));

N皇后

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

示例 1:

98bcc7fbcd21f26be2aa29c1e7494a2a.jpeg
img

输入:n = 4 输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]] 解释:如上图所示,4 皇后问题存在两个不同的解法。

// 该问题本质上和全排列问题差不多,决策树上的每一层表示棋盘上的每一行,每个节点可以做出的选择是在该行的任意一列放置一个皇后
function solveNQueens(n) {
    const result = [];

    // 判断是否可以在[row, col]放置皇后
    const isValid = (board, row, col) => {
        // 判断该列是否存在皇后
        for (let i = 0; i < row; i++) {
            if (board[i][col] === 'Q') {
                return false;
            }
        }

        // 判断左上方是否存在皇后冲突
        let leftRow = row - 1;
        let leftCol = col - 1;
        while (leftRow >= 0 && leftCol >= 0) {
            if (board[leftRow][leftCol] === 'Q') {
                return false;
            }
            leftRow--;
            leftCol--;
        }

        // 判断右上是否存在皇后冲突
        let rightRow = row - 1;
        let rightCol = col + 1;
        while (rightRow >= 0 && rightCol < n) {
            if (board[rightRow][rightCol] === 'Q') {
                return false;
            }
            rightRow--;
            rightCol++;
        }

        return true;
    };
    /**
     * 路径:board中小于row的那些行都已经成功放置了皇后
     * 选择列表:第row行的所有列都是放置皇后的选择
     * 结束条件:row超过board的最后一行
     */

    const backtrack = (board, row) => {
        // 触发结束条件
        if (row === board.length) {
            result.push(board.map(item => {
                return item.join('');
            }));
            return;
        }

        for (let col = 0; col < n; col++) {
            // 排除不合法的选择
            if (!isValid(board, row, col)) {
                continue;
            }

            // 做选择
            board[row][col] = 'Q';

            // 进行下一层
            backtrack(board, row + 1);

            // 取消选择
            board[row][col] = '.';
        }
    };

    const board = new Array(n);
    for (let i = 0; i < n; i++) {
        board[i] = (new Array(n)).fill('.');
    }

    backtrack(board, 0);

    return result;
}

const n = 4;
console.log(solveNQueens(n));

组合总和

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例 1:

输入:candidates = [2,3,6,7], target = 7 输出:[[2,2,3],[7]] 解释: 2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。 7 也是一个候选, 7 = 7 。 仅有这两种组合。

function combinationSum(candidates, target) {
    const result = [];

    /**
     * 路径:记录在track中
     * 选择列表:index及其之后的元素
     * 结束条件:其和为target,则进行存储,超过,则不存储结束
     */
    const backtrack = (sortNums, track, trackNum, index) => {
        if (trackNum === target) {
            result.push([...track]);
            return;
        } else if (trackNum > target) {
            return;
        }

        for (let i = index; i < sortNums.length; i++) {
            // 做选择
            track.push(sortNums[i]);
            trackNum += sortNums[i];

            // 进入下一层
            backtrack(sortNums, track, trackNum, i);

            // 取消选择
            track.pop();
            trackNum -= sortNums[i];
        }
    };

    const track = [];
    let trackNum = 0;

    candidates.sort((a, b) => a - b);

    backtrack(candidates, track, trackNum, 0);

    return result;
}

const candidates = [2,3,6,7];
const target = 7;
console.log(combinationSum(candidates, target));

子集II

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

示例 1:

输入:nums = [1,2,2] 输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]

function subsetsWithDup(nums) {
    const result = [];

    /**
     * 路径:记录在track中
     * 选择列表:index及其之后的元素
     * 结束条件:将结果进行存储
     */
    const backtrack = (sortNums, track = [], index = 0) => {
        result.push([...track]);

        for (let i = index; i < sortNums.length; i++) {
            // 对于排序后相同的元素应该跳过
            if (i > index && sortNums[i] === sortNums[i - 1]) {
                continue;
            }
            // 做选择
            track.push(sortNums[i]);
            // 进入下一层
            backtrack(sortNums, track, i + 1);
            // 取消选择
            track.pop();
        }
    };

    const track = [];
    nums.sort((a, b) => a - b);
    backtrack(nums, track, 0);

    return result;
}

const nums = [1, 2, 2];
console.log(subsetsWithDup(nums));

递增子序列

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。

数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

示例 1:

输入:nums = [4,6,7,7] 输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]

function findSubsequences(nums) {
    const result = [];

    const backtrack = (track, start) => {
        // 如果其长度大于等于2,则算一个子序列
        if (track.length >= 2) {
            result.push([...track]);
        }

        const map = new Map();
        for (let i = start; i < nums.length; i++) {
            // 判断是否合法
            // 同一层只能用一次
            if (map.has(nums[i])) {
                continue;
            }
            // 后一层比前一层大
            if (nums[i] < track[track.length - 1]) {
                continue;
            }
            map.set(nums[i], true);
            // 做选择
            track.push(nums[i]);
            // 进入下一层
            backtrack(track, i + 1);
            // 取消选择
            track.pop();
        }
    };

    const track = [];
    backtrack(track, 0);

    return result;
}

const nums = [1, 2, 3 ,1,1,1,1];
console.log(findSubsequences(nums));

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

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

相关文章

市值暴涨8000亿,马斯克告诉了美国同行,为啥需要中国市场?

马斯克访华仅仅40多个小时&#xff0c;却带动了股价连涨5天&#xff0c;涨幅最高达到20%&#xff0c;市值飙涨8000亿元人民币&#xff0c;马斯克也因此再度问鼎全球首富之位&#xff0c;凸显出中国之行给他带来的巨大好处。 一、中国市场带动了特斯拉的辉煌 2018年马斯克为产能…

依据换行符分割字符串numpy.char.splitlines()含换行符与回车符的区别

【小白从小学Python、C、Java】 【等级考试500强双证书考研】 【Python-数据分析】 依据换行符分割字符串 numpy.char.splitlines() 含换行符与回车符的区别 [太阳]选择题 以下说法错误的是&#xff1a; import numpy as np a "I\nLove\rChina\r\nforever" print(&q…

APACHE-ATLAS-2.1.0 - 安装HIVE HOOK(六)

写在前面 本博文以获取HIVE元数据为例&#xff0c;进行流程和源码的分析。 请提前安装好HADOOP和HIVE的环境&#xff0c;用于测试。 ATLAS官网&#xff1a;https://atlas.apache.org/#/HookHive ATLAS支持的元数据源 什么是Hive Hook&#xff08;钩子&#xff09; HOOK是一种…

可视化的三种图结构方案 (canvas、fabric、G6)

原生 canvas、fabric 以及 G6&#xff0c;三种方案各有优劣势 原生 canvasfabricG6优点灵活、自由、可定制化非常强封装了 canvas 的 api&#xff0c;使用简单灵活提供了复杂树、图等 api&#xff0c;只需要按照文档配置即可缺点开发复杂、耗时对于构建大型树、图等复杂、耗时…

chatgpt赋能python:选择函数:Python实现之道

选择函数&#xff1a;Python 实现之道 什么是选择函数&#xff1f; 在 SEO 中&#xff0c;选择函数是指搜索引擎在对网站内容进行排名时所采用的一种规则。选择函数由搜索引擎定义&#xff0c;其目的在于建立一个算法来确定哪些网站会出现在搜索结果的前几页中。对于网站管理…

百度视频质量评测的实践之路

视频编解码技术日新月异&#xff0c;新的编解码技术赋予视频业务新的应用场景和新的用户视听体验。同时&#xff0c;视频作为带宽消耗大户&#xff0c;如何在视听体验和视频带宽之间取得最优的平衡是一个永恒的话题。视频质量评测主要用来回答&#xff1a;体验是否改善、带宽是…

chatgpt赋能python:如何用Python制作动画?

如何用Python制作动画&#xff1f; Python作为一种优秀的编程语言&#xff0c;可以用于不同领域的编程。其中&#xff0c;Python也可以被用于创建动画。使用Python的主要好处之一是其强大的Matplotlib库&#xff0c;它可以帮助我们更轻松地创建可视化效果。 什么是Matplotlib…

chatgpt赋能python:Python如何取出List中的数据

Python如何取出List中的数据 在Python中&#xff0c;列表&#xff08;List&#xff09;是一种非常常见的数据类型&#xff0c;它可以存储任意类型的数据&#xff0c;并且可以按照下标索引来访问其中的元素。本篇文章将介绍如何使用Python来取出List中的数据。 常规方法 在Py…

Android BlueToothBLE入门(一)——低功耗蓝牙介绍

学更好的别人&#xff0c; 做更好的自己。 ——《微卡智享》 本文长度为3150字&#xff0c;预计阅读8分钟 前言 距上篇文章发布都一个多月了&#xff0c;先声明&#xff0c;我可不会停更。这么长时间没更新文章&#xff0c;其实原因就三点&#xff1a; 原因一是工作上事确实多&…

JavaScript之事件(十)

JavaScript之事件 1、事件绑定2、事件类型3、事件的传播4、事件对象1、事件对象常用的属性2、事件对象常用的方法 事件可用于处理、验证用户输入、用户动作和浏览器动作。 1、事件绑定 事件绑定就是给HTML标签绑定特定的事件&#xff0c;当该事件触发&#xff0c;则会执行相应的…

一款射频芯片的layout设计指导案例-篇章2

《一款射频芯片的layout设计指导案例-篇章1》中&#xff0c;我们阐述了RTL8762元件布局顺序、DC/DC电路元件布局走线、电源Bypass布局规范、外部flash布局走线、RF布局走线&#xff0c; 本篇阐述晶振、ESD、板层等相关指导建议—— 40MHz晶振布局走线规范 在没有结构限制情况下…

chatgpt赋能python:Python如何在同一行输入三个数?

Python如何在同一行输入三个数&#xff1f; Python语言是一门广泛使用的编程语言&#xff0c;被广泛应用于数据分析、机器学习、Web开发、科学计算、人工智能等领域。但是&#xff0c;有时候我们需要在同一行输入多个变量或数字&#xff0c;这可能给一些初学者带来一些困惑。本…

暑期实习开始啦「GitHub 热点速览」

作者&#xff1a;HelloGitHub-小鱼干 无巧不成书&#xff0c;刚好最近有小伙伴在找实习&#xff0c;而 GitHub 热榜又有收录实习信息的项目在榜。所以&#xff0c;无意外本周特推就收录了这个实习项目&#xff0c;当然还有国内版本。除了应景的实习 repo 之外&#xff0c;还有帮…

快手 | 后端Java实习生 | 一面

目录 1.Redis缓存和MySQL数据一致性如何保证&#xff1f;2.你使用缓存&#xff0c;在高并发的情况下&#xff0c;如果多个缓存同时过期了怎么办&#xff1f;3.消息队列积压了怎么办&#xff1f;4.jdk1.8之后Java内存模型分别哪几个部分&#xff1f;每个部分用一句话概括一下&am…

GaussDB云数据库SQL应用系列-定时任务管理

前言 GaussDB数据库定时任务主要可以用于实现定期的备份、统计信息采集、数据汇总、数据清理与优化等&#xff0c;它是指在指定的时间间隔内自动执行一次或多次SQL语句的程序。 一、GaussDB数据库定时任务介绍 GaussDB数据库兼容Oracle定时任务功能主要通过DBE_TASK高级功能…

POSTGRESQL PSQL 命令中如何使用变量带入查询和函数

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

chatgpt赋能python:Python如何删除已经写好的代码?

Python如何删除已经写好的代码&#xff1f; 在编程中&#xff0c;大多数时候写出来的代码都是需要保留的。但是&#xff0c;随着项目的发展和需求的变化&#xff0c;有些代码可能就没有用了。这时&#xff0c;我们需要删除这些无用的代码&#xff0c;以保持程序的简洁性和效率…

Nginx、location匹配、Rewrite重写模块

Nginx、location匹配、Rewrite重写模块 一、常用的Nginx正则表达式二、location匹配概述1、location大致可以分为三类2、location常用的匹配规则3、location 优先级4、location 示例说明5、实际网站使用中&#xff0c;至少有三个匹配规则定义 三、rewrite重写1、rewrite 跳转场…

记录--JavaScript 中有趣的 9 个常用编码套路

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 1️⃣ set对象&#xff1a;数组快速去重 常规情况下&#xff0c;我们想要筛选唯一值&#xff0c;一般会想到遍历数组然后逐个对比&#xff0c;或者使用成熟的库比如lodash之类的。 不过&#xff0c;ES…

Spark RDD分区

文章目录 一、RRD分区&#xff08;一&#xff09;RDD分区概念&#xff08;二&#xff09;RDD分区作用 二、RDD分区数量&#xff08;一&#xff09;RDD分区原则&#xff08;二&#xff09;影响分区的因素&#xff08;三&#xff09;使用parallelize()方法创建RDD时的分区数量1、…