leetcode算法笔记:二叉树,动态规划和回溯法
前言
写的比较匆忙,测试用例是能全部跑通的,不过考虑内存和效率的话,还有许多需要改进的地方,所以请多指教
在二叉树中增加一行
给定一个二叉树,根节点为第1层,深度为 1。在其第 d 层追加一行值为 v 的节点。
添加规则:给定一个深度值 d (正整数),针对深度为 d-1 层的每一非空节点 N,为 N 创建两个值为 v 的左子树和右子树。
将 N 原先的左子树,连接为新节点 v 的左子树;
将 N 原先的右子树,连接为新节点 v 的右子树。
如果 d 的值为 1,深度 d - 1 不存在,则创建一个新的根节点 v,原先的整棵树将作为 v 的左子树。
Input:
A binary tree as following:
4
/ \
2 6
/ \ /
3 1 5 v = 1 d = 2 Output:
4
/ \
1 1
/ \
2 6
/ \ /
3 1 5 来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/add-one-row-to-tree
/**
* @param {number} cd:current depth,递归当前深度
* @param {number} td:target depth, 目标深度
*/
var traversal = function (node, v, cd, td) {
// 递归到目标深度,创建新节点并返回
if (cd === td) {
// return 新节点
}
// 向左子树递归
if (node.left) {
node.left = traversal (node.left, v, cd + 1, td);
}
// 向右子树递归
if (node.right) {
node.right = traversal (node.right, v, cd + 1, td);
}
// 返回旧节点
return node;
};
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @param {number} v
* @param {number} d
* @return {TreeNode}
*/
var addOneRow = function (root, v, td) {
// 从根节点开始递归
traversal (root, v, 1, td);
return root;
};
如果目标节点原来是左子树,那么重置后目标节点是val节点的左子树
如果目标节点原来是右子树,那么重置后目标节点是val节点的右子树


我们再分析题意,题目里说:“如果 d 的值为 1,深度 d - 1 不存在,则创建一个新的根节点 v,原先的整棵树将作为 v 的左子树。”

/**
* @param {v} val,插入节点携带的值
* @param {cd} current depth,递归当前深度
* @param {td} target depth, 目标深度
* @param {isLeft} 判断原目标深度的节点是在左子树还是右子树
*/
var traversal = function (node, v, cd, td, isLeft) {
debugger;
if (cd === td) {
const newNode = new TreeNode (v);
// 如果原来是左子树,重置后目标节点还是在左子树上,否则相反
if (isLeft) {
newNode.left = node;
} else {
newNode.right = node;
}
return newNode;
}
// 处理上述的第1和第2种情况
if (node.left || (node.left === null && cd + 1 === td)) {
node.left = traversal (node.left, v, cd + 1, td, true);
}
if (node.right || (node.right === null && cd + 1 === td)) {
node.right = traversal (node.right, v, cd + 1, td, false);
}
return node;
};
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @param {number} v
* @param {number} d
* @return {TreeNode}
*/
var addOneRow = function (root, v, td) {
// 处理目标深度为1的情况,也就是上述的第3种情况
if (td === 1) {
const n = new TreeNode (v);
n.left = root;
return n;
}
traversal (root, v, 1, td);
return root;
};
单词拆分
给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
说明:
1.拆分时可以重复使用字典中的单词。
2.你可以假设字典中没有重复的单词。
example1
输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。
注意: 你可以重复使用字典中的单词。 example2
输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false 来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/word-break
动态规划
wordDict = ['ab','cd','ef']
s ='abcdef'
并且假设目前我们已经得出了N=1到N=5的情况,而现在需要计算N=6的情况
A序列 + B序列
1.abcdef + ""
2.abcde + f
3.abcd + ef
4.abc + def
5.ab + cdef
6.a + bcdef
注意:当A可拆且B可拆时,则A+B也是可拆分的
从中我们不难发现两点
当A可拆且B可拆时,则A+B也是可拆分的
这6种情况只要有一种组合序列是可拆分的,abcdef就一定是可拆的,也就得出dp[6] = true了

var initDp = function (len) {
let dp = new Array (len + 1).fill (false);
return dp;
};
/**
* @param {string} s
* @param {string[]} wordDict
* @return {boolean}
*/
var wordBreak = function (s, wordDict) {
// 处理空字符串
if (s === '' && wordDict.indexOf ('') === -1) {
return false;
}
const len = s.length;
// 默认初始值全部为false
const dp = initDp (len);
const a = s.charAt (0);
// 初始化动态规划的初始值
dp[0] = wordDict.indexOf (a) === -1 ? false : true;
dp[1] = wordDict.indexOf (a) === -1 ? false : true;
// i:end
// j:start
for (let i = 1; i < len; i++) {
for (let j = 0; j <= i; j++) {
// 序列[0,i] = 序列[0,j] + 序列[j,i]
// preCanBreak表示序列[0,j]是否是可拆分的
const preCanBreak = dp[j];
// 截取序列[j,i]
const str = s.slice (j, i + 1);
// curCanBreak表示序列[j,i]是否是可拆分的
const curCanBreak = wordDict.indexOf (str) !== -1;
// 情况1: 序列[0,j]和序列[j,i]都可拆分,那么序列[0,i]肯定也是可拆分的
const flag1 = preCanBreak && curCanBreak;
// 情况2: 序列[0,i]本身就存在于字典中,所以是可拆分的
const flag2 = curCanBreak && j === 0;
if (flag1 || flag2) {
// 设置bool值,本轮计算结束
dp[i + 1] = true;
break;
}
}
}
// 返回最后结果
return dp[len];
};
全排列
给定一个没有重复数字的序列,返回其所有可能的全排列。
Example
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
] 来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations
基本思想
回溯法
深度优先搜索搞一波,index在递归中向前推进
当index等于数组长度的时候,结束递归,收集到results中(数组记得要深拷贝哦)
两次数字交换的运用,计算出两种情况
想不通没关系,套路一波就完事了
var swap = function (nums, i, j) {
const temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}; var recursion = function (nums, results, index) {
// 剪枝
if (index >= nums.length) {
results.push (nums.concat ());
return;
}
// 初始化i为index
for (let i = index; i < nums.length; i++) {
// index 和 i交换??
// 统计交换和没交换的两种情况
swap (nums, index, i);
recursion (nums, results, index + 1);
swap (nums, index, i);
}
};
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permute = function (nums) {
const results = [];
recursion (nums, results, 0);
return results;
};
leetcode算法笔记:二叉树,动态规划和回溯法的更多相关文章
- python常用算法(7)——动态规划,回溯法
引言:从斐波那契数列看动态规划 斐波那契数列:Fn = Fn-1 + Fn-2 ( n = 1,2 fib(1) = fib(2) = 1) 练习:使用递归和非递归的方法来求解斐波那契数 ...
- 从Leetcode的Combination Sum系列谈起回溯法
在LeetCode上面有一组非常经典的题型--Combination Sum,从1到4.其实就是类似于给定一个数组和一个整数,然后求数组里面哪几个数的组合相加结果为给定的整数.在这个题型系列中,1.2 ...
- 34,Leetcode 组合总和I,II -C++ 回溯法
I 题目描述 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合.candidates 中的数字可以无 ...
- [LeetCode]78. 子集(位运算;回溯法待做)
题目 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集). 说明:解集不能包含重复的子集. 示例: 输入: nums = [1,2,3] 输出: [ [3], [1], ...
- JS算法之八皇后问题(回溯法)
八皇后这个经典的算法网上有很多种思路,我学习了之后自己实现了一下,现在大概说说我的思路给大家参考一下,也算记录一下,以免以后自己忘了要重新想一遍. 八皇后问题 八皇后问题,是一个古老而著名的问题,是回 ...
- LeetCode算法笔记目录
贪心算法: LeetCode翻转矩阵后的得分-Python3<六> LeetCode根据身高重建队列-Python3<七> LeetCode 任务调度器-Python3< ...
- 编程熊讲解LeetCode算法《二叉树》
大家好,我是编程熊. 往期我们一起学习了<线性表>相关知识. 本期我们一起学习二叉树,二叉树的问题,大多以递归为基础,根据题目的要求,在递归过程中记录关键信息,进而解决问题. 如果还未学习 ...
- 【算法笔记】B1024 科学计数法
1024 科学计数法 (20 分) 科学计数法是科学家用来表示很大或很小的数字的一种方便的方法,其满足正则表达式 [+-][1-9].[0-9]+E[+-][0-9]+,即数字的整数部分只有 1 位, ...
- LeetCode -90. 子集 II C++ (回溯法)
class Solution { public: vector<vector<int>> subsetsWithDup(vector<int>& nums) ...
随机推荐
- Ubuntu和开发板用网线直连ping不通的问题
我装的Ubuntu 18.04双系统,在通过网络加载内核和文件系统那一步一直连接不上,uboot里面ping我的主机IP地址,提示: ping failed; host 192.168.1.111 i ...
- Oracle11g安装与基本使用
目录 安装 修改用户密码 配置文件修改 使用PLSQL连接Oracle数据库 如何执行SQL 语句 本教程基于oracle11g和PLSQL进行 下载资源见百度网盘链接:https://pan.bai ...
- CSS中各种布局的背后(*FC)
CSS中各种布局的背后,实质上是各种*FC的组合.CSS2.1中只有BFC和IFC,CSS3 中还增加了FFC和GFC. 盒模型(BoxModel) 上图为W3C标准盒模型,另外还有一种IE盒模型(I ...
- WebSocket学习简书
1.什么是Websocket? WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议. 2.单工,半双工和全双工通信? 在单工通信中,通信的信道是单向的,发送端 ...
- 18.Tomcat基本应用
1.JVM基本介绍 JAVA编译型 ---> 编译 C 编译型---> linux --->编译一次 windows --->编译一次 macos ubuntu 跨平台 移值型 ...
- Vue学习系列(三)——基本指令
前言 在上一篇中,我们已经对组件有了更加进一步的认识,从组件的创建构造器到组件的组成,进而到组件的使用,.从组件的基本使用.组件属性,以及自定义事件实现父子通讯和巧妙运用插槽slot分发内容,进一步的 ...
- Vue躬行记(4)——组件
组件是可复用的Vue实例,拥有属于自己的数据.模板.脚本和样式,可避免繁重的重复性开发.由于组件都是独立的,因此其内部代码不会影响其它组件,但可以包含其它组件,并且相互之间还能通信. 一.注册 在使用 ...
- 基于Spring Boot的问答系统之一:elasticsearch 7.2的hello world入门
好久没有写代码了,最近想做一个基于spring boot + vue + elasticsearch + NLP(语义相关性)的小系统练练手,系统后面可以成为一个聊天机器人,客服系统的原型等等. 所以 ...
- Ubuntu 14.04 sudo免密码的方法| sudo不需要密码
Ubuntu 14.04 sudo免密码的方法| sudo不需要密码 cd /etc/sudoers.d sudo touch nopasswd4sudo sudo vi nopasswd4sudo ...
- SpringBoot整合MybatisPlus3.X之Wrapper(五)
官方文档说明: 以下出现的第一个入参boolean condition表示该条件是否加入最后生成的sql中 以下代码块内的多个方法均为从上往下补全个别boolean类型的入参,默认为true 以下出现 ...