【LeetCode两题选手】算法类题目(7.27)
第一题:全排列(回溯)
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [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 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路:
回溯法 :一种通过探索所有可能的候选解来找出所有的解的算法。如果候选解被确认不是一个解的话(或者至少不是最后一个解),回溯算法会通过在上一步进行一些变化抛弃该解,即回溯并且再次尝试。
使用标记数组来处理填过的数是一个很直观的思路,但是可不可以去掉这个标记数组呢?毕竟标记数组也增加了我们算法的空间复杂度。
答案是可以的,我们可以将题目给定的 n 个数的数组 nums[]划分成左右两个部分,左边的表示已经填过的数,右边表示待填的数,我们在递归搜索的时候只要动态维护这个数组即可。
举个简单的例子,假设我们有 [2, 5, 8, 9, 10] 这 5 个数要填入,已经填到第 3 个位置,已经填了 [8,9] 两个数,那么这个数组目前为 [8, 9 | 2, 5, 10] 这样的状态,分隔符区分了左右两个部分。假设这个位置我们要填 10 这个数,为了维护数组,我们将 2 和 10 交换,即能使得数组继续保持分隔符左边的数已经填过,右边的待填 [8, 9, 10 | 2, 5] 。
作者:LeetCode-Solution 链接:https://leetcode-cn.com/problems/permutations/solution/quan-pai-lie-by-leetcode-solution-2/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
vector<vector<int>> permute(vector<int>& nums) {
if(nums.empty()) return {};
// 存放最后结果
vector<vector<int>> ans;
// 存放某一个排列
vector<int> temp;
// 判断该数字是否被使用过
vector<bool> used(nums.size(),false);
// 进行递归求解
dfs(ans,temp,used,nums);
return ans;
}
void dfs(vector<vector<int>>& ans,vector<int>& temp,vector<bool>& used,
const vector<int>& nums)
{
// 如果当前排列数组的长度等于输入数组的长度
// 该排列已完成
// 将该排列的加入结果中,返回
if(temp.size()==nums.size())
{
ans.push_back(temp);
return;
}
// 循环的进行枚举所有状态
for(int i=0;i<nums.size();++i)
{
// 该数字已经选择过,跳过
if(used.at(i)) continue;
// 选择当前数字
temp.push_back(nums.at(i));
// 记录该数字已被选择
used.at(i)=true;
// 递归选择下一个数字
dfs(ans,temp,used,nums);
// 回溯,撤销当前选择
used.at(i)=false;
temp.pop_back();
}
}
思考
晕,看来要自己去写一下“八皇后问题”了。
题二:二叉搜索树中第K小的元素
给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。
说明: 你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数。
示例 1:
输入: root = [3,1,4,null,2], k = 1
3
/
1 4
2
输出: 1
示例 2:
输入: root = [5,3,6,2,4,null,null,1], k = 3
5
/
3 6
/
2 4
/
1
输出: 3
进阶: 如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k 小的值,你将如何优化 kthSmallest 函数?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/kth-smallest-element-in-a-bst
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
中序遍历二叉搜索树,在遍历的过程中,每经过一个节点将k减1,当k == 0的时候,则找到了第k小的元素(注意边界)。
题解
void recursion(TreeNode* root,int& res,int& k)
{
if(!root)
return;
recursion(root->left,res,k);
k--;
if(k == 0)
{
res = root->val;
return;
}
recursion(root->right,res,k);
}
int kthSmallest(TreeNode* root, int k) {
int res;
recursion(root,res,k);
return res;
}
思考
关于进阶那块,我觉得可以思考一下伸展树:伸展树,据说比AVL树要简单一些 手植这棵自顶向下伸展树,何时亭亭如盖呢?
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- docker一键部署SpringBoot项目
- 【Flutter 专题】95 图解 Dart 单线程实现异步处理之 Task Queue
- 首发基于OpenPAI细化部署 Hadoop 集群
- PhpStorm 集成 WSL 虚拟机中的 PHP 进行单元测试和代码调试
- 【CMake教程】(五)CMake 配置install打包
- 【CMake教程】(四)CMake 配置生成lib或者so的库文件
- 使用 GitHub README profile 展示更多信息
- 算法工程师-SQL进阶:集合之间的较量
- 算法工程师-SQL进阶:神奇的自连接与子查询
- 算法工程师-SQL进阶:温柔的陷阱-NULL
- SpringBoot 异步任务处理
- SpringBoot整合 ActiveMQ快速入门 实现点对点推送
- Tomcat部署SpringBoot war包
- Vue之插槽Slot理解
- Docker六脉神剑 (三) 编写Dockerfile构建nginx镜像并推送到远程仓库给其他人使用