Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情。
前言
每天一道算法题,死磕算法
从今天开始,准备把 🔥 LeetCode 热题 HOT 100中没有做的题目都做一下,因为这100道题特别适合短时间内提升,熟练掌握这 100 道题,你就已经具备了在代码世界通行的基本能力。
题目
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
分析
第一步从题目中提取关键字
-
1.有效的,这三个字怎么听起来那么熟悉,我们在讲栈的时候,是不是做过一道有效的括号的题目,(不知道的同学请看js算法题解(第十五天)---20. 有效的括号)所以正好拿来用
-
2.一看到组合两个字我们立马联想到回溯,用回溯算法我们要知道3个概念
1.路径(track): 就是当前已经选择的路径
2.选择列表(nums): 就是当前可以选择的路径列表
3.结束条件: 就是当前路径的结束条件模板如下
result = []; let backTrace = function(track, nums){ if(结束条件){ result.push(track); return; } for(let i=0;i<nums.length;i++){ 加入路径 backTrace(track,nums) 从路径中去除 } }所以算法题被我们完成了填空游戏,是不是感觉很好玩
/**
* @param {number} n
* @return {string[]}
*/
// 我之所以用栈这种数据结构是为了复习一下栈的使用,如果做一个知识点,能让我们把其他知识点关联起来,这样不是更好么
const isValid = function (result) {
// 定义哈希值
let obj = {
")": "("
};
// 定义栈
let arr = [];
for (let i = 0; i < result.length; i++){
// 如果是)的话
if (obj[result[i]]) {
// 如果栈为空
if (!arr.length) {
return false;
}
// 取出栈顶的元素
let top = arr[arr.length - 1];
// 如果栈定的元素等于值,那么出栈
if (top === obj[result[i]) {
arr.pop();
} else {
return false;
}
} else {
arr.push(result[i]);
}
}
// 如果循环完,栈里面还有元素,那么为false
if (arr.length) {
return false;
}
return true;
}
var generateParenthesis = function (n) {
let result = [];
if (n===0) {
return result;
}
// 第一个选择是(括号,否则就是失效的
backTrack('',['('],2*n,result,0)
return result;
};
/**
*
* @param {string} track 路径
* @param {array} nums 选择列表
* @param {number} n 结束条件
* @param {array} result 结果
* @returns
*/
const backTrack = function (track, nums, n, result) {
if (track.length === n) {
if (isValid(track)) {
result.push(track);
}
return;
}
for (let i = 0; i < nums.length; i++){
track += nums[i];
backTrack(track, ['(', ')'], n, result)
track = track.substring(0, track.length - 1);
}
}
写时一时爽,看看结果只超过了20%的人
咱们现在的答案之所以时间复杂度高,就是因为把所有的组合答案都放到了结果里面,然后再判断是否有效,但是有些答案从一开始就不对,所以我们没必要把他加到结果里面,这种策略叫做剪枝
总之,剪枝策略,属于算法优化范畴;通常应用在DFS 和 BFS 搜索算法中;剪枝策略就是寻找过滤条件,提前减少不必要的搜索路径。
因为这个题目本身就是一个二叉树,所以我们可以用一个递归二叉树的方法去做,然后再加一个二叉树的结束条件
所以我们的第二套方案是
/**
*
* @param {string} track 路径
* @param {number} left 左括号个数
* @param {number} right 右括号个数
* @param {array} result 结果
* @param {number} n 括号对数
* @returns
*/
const func = function(track,left,right,result,n){
// 只要right>left,那么肯定就是不对的
if(left>n||right>n||right>left){
return;
}
if(track.length === 2*n){
result.push(track);
return;
}
//左子树递归
func(track+"(",left+1,right,result,n);
//右子树递归
func(track+')',left,right+1,result,n);
}
var generateParenthesis = function(n) {
let result = [];
func('',0,0,result,n);
return result;
};
这种方法提交一下,平均超过76%的用户,好吧,知足了,小伙伴们,快来试一试吧