面试题 04.09. 二叉搜索树序列
从左向右遍历一个数组,通过不断将其中的元素插入树中可以逐步地生成一棵二叉搜索树。
给定一个由不同节点组成的二叉搜索树 root,输出所有可能生成此树的数组。
示例 1:
输入: root = [2,1,3]
输出: [[2,1,3],[2,3,1]]
解释: 数组 [2,1,3]、[2,3,1] 均可以通过从左向右遍历元素插入树中形成以下二叉搜索树
2
/ \
1 3
示例 2:
输入: root = [4,1,null,null,3,2]
输出: [[4,1,3,2]]
提示:
- 二叉搜索树中的节点数在 [0, 1000] 的范围内
- 1 <= 节点值 <= 10^6
- 用例保证符合要求的数组数量不超过 5000
C++代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> BSTSequences(TreeNode* root) {
if (root == nullptr) {
return {{}};
}
deque<TreeNode*> dq; // 双端队列可以两端插入和删除,便于处理回溯;queue只能在队列头部删除,队列尾部插入
dq.push_back(root);
dfs(dq);
return res;
}
void dfs(deque<TreeNode*>& dq) {
// 终止条件
if (dq.empty()) { // 说明状态不会再扩展,path已经选择完了最后一个节点
res.push_back(path);
return;
}
// 遍历选择条件
for (unsigned int i = 0; i < dq.size(); ++i) {
// 作出选择(修改全局变量)
TreeNode* top = dq.front();
dq.pop_front();
path.push_back(top->val);
if (top->left) {
dq.push_back(top->left);
}
if (top->right) {
dq.push_back(top->right);
}
// 递归
dfs(dq);
// 回溯 (恢复全局变量)
if (top->left) {
dq.pop_back();
}
if (top->right) {
dq.pop_back();
}
dq.push_back(top); // 满足队列先进先出条件,不要使用push_front
path.pop_back();
}
}
private:
vector<int> path;
vector<vector<int>> res;
};
参考
- 力扣题解1——每一个节点都必须排在它的子孙结点前面
- 力扣题解2——c++ 比较容易理解 DFS 回溯
- 回溯、dfs、递归三者的关系
- 回溯和DFS本质上是同一种思想,都是搜索算法中的一种,都是利用递归实现的。回溯算法通过试图在所有可能的结果中搜索,然后找到一个符合条件的结果。DFS算法也是利用递归实现的一种搜索算法,与回溯算法不同的是,DFS算法是一种无回溯的搜索算法,即一旦前进到某一步时不再回头。
- 递归是一种程序设计或编程技术,在编写程序时,递归是一种求解问题的方法,它通过将问题分解为更小的子问题,直到子问题不再需要继续分解为止。回溯和DFS算法都是通过递归解决搜索问题的。
- 因此,回溯、DFS和递归三者之间存在着密切的联系和相互依赖关系,可以说回溯和DFS算法都是利用递归实现的,而递归则是回溯和DFS算法的基础。
- 递归和回溯的一般结构:
- 回溯算法模板:
void backtrack(参数) {
if (终止条件) {
存储结果;
return;
}
for (选择:选择列表) {
做出选择;
backtrack(新参数);
撤销选择;
}
}
- 常考的回溯算法题目:
- 八皇后问题
- 数独问题
- 括号生成
- 全排列问题
- 组合总和问题
- 单词搜索问题
- 子集问题
- 电话号码的字母组合问题
- 分割回文串问题
- N 皇后问题
- deque和queue的区别:
queue是一种先进先出(FIFO)的数据结构,只能在队列的尾部插入元素,在队列的头部删除元素。deque是双端队列,可以从队列的两端插入或删除元素。queue适用于需要按照先后顺序进行操作的场景,例如任务调度、消息传递等。deque适用于需要在队列的两端进行操作的场景,例如滑动窗口、游戏开发等。 - deque常用的函数:
- push_front():在deque的前面添加元素
- push_back():在deque的后面添加元素(跟queue的push()功能一样)
- pop_front():删除deque的第一个元素 (跟queue的pop()功能一样)
- pop_back():删除deque的最后一个元素
- front():返回deque的第一个元素 (跟queue一样)
- back():返回deque的最后一个元素 (跟queue一样)
- size():返回deque中元素的数量 (跟queue一样)
- empty():检查deque是否为空 (跟queue一样)
- clear():删除deque中的所有元素