难度中等527收藏分享切换为英文关注反馈
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
注意:
不能使用代码库中的排序函数来解决这道题。
示例:
输入: [2,0,2,1,1,0] 输出: [0,0,1,1,2,2]
进阶:
- 一个直观的解决方案是使用计数排序的两趟扫描算法。
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。 - 你能想出一个仅使用常数空间的一趟扫描算法吗?
用三个指针(p0, p2 和curr)来分别追踪0的最右边界,2的最左边界和当前考虑的元素。
While curr <= p2 :
若 nums[curr] = 0 :交换第 curr个 和 第p0个 元素,并将指针都向右移。cur++
若 nums[curr] = 2 :交换第 curr个和第 p2个元素,并将 p2指针左移 。cur不变。
若 nums[curr] = 1 :将指针curr右移。
题解少了一个状态维持的关键说明:
curr 左边全都是0/1的有序序列!
curr位置是0时,与左边的0位置交换,因为保证curr左边全是0/1, 所以交换过来的必然是0/1,状态维持住了;
curr位置是2时,交换后,curr不能移动,因为一移动,没法保证交换过来的是0/1;所以这里不移动;这时状态也维持住了
class Solution {
public:
void sortColors(vector<int>& nums) {
int l=0, r=nums.size()-1;
int cur=0;
while(cur<=r){
if(nums[cur]==0){
swap(nums[cur],nums[l]);l++;cur++;
}else if(nums[cur]==2){
swap(nums[cur],nums[r]);r--;
}else cur++;
}
return;
}
};
难度中等325
给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
示例:
输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
class Solution {
public:
void get(vector<int>& temp,int num,int k,vector<vector<int>> &ans,int index,int n){
//这个在前
if(num==k){
ans.push_back(temp);return;
}
//这个在后...
if(index==n+1||num>k) return;
temp.push_back(index);
get(temp,num+1,k,ans,index+1,n);
temp.pop_back();
get(temp,num,k,ans,index+1,n);
}
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> ans;
if(n==0||k==0) return ans;
vector<int> temp;
get(temp,0,k,ans,1,n);
return ans;
}
};
难度中等689
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
class Solution {
public:
vector<int> tmp;
void get(int xuan,int sum,int n,vector<vector<int>> &ans,vector<int>& nums){
if(sum==n){
// if(xuan==0) return;
ans.push_back(tmp);return;
}
get(xuan,sum+1,n,ans,nums);
tmp.push_back(nums[sum]);
get(xuan+1,sum+1,n,ans,nums);
tmp.pop_back();
}
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> ans;
// ans.push_back({});
//每一个元素,选或者不选~
get(0,0,nums.size(),ans,nums);
return ans;
}
};
难度中等506
给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
board = [ ['A','B','C','E'], ['S','F','C','S'], ['A','D','E','E'] ] 给定 word = "ABCCED", 返回 true 给定 word = "SEE", 返回 true 给定 word = "ABCB", 返回 false
典型题目,好像有点....
class Solution {
public:
int t[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
bool ans=false;
void get(vector<vector<char>>& board,vector<vector<bool>>& visit,int index,string word,int x,int y,int n,int m){
if(ans) return;
if(index==word.size()-1&&board[x][y]==word[index]){
ans= true;return;
}
if(board[x][y]==word[index]){
visit[x][y]=true;
for(int i=0;i<4;i++){
int tx=x+t[i][0];
int ty=y+t[i][1];
if(tx<0||ty<0||tx>=n||ty>=m||visit[tx][ty]==true) continue;
get(board,visit,index+1,word,tx,ty,n,m);
}
visit[x][y]=false;
}
}
bool exist(vector<vector<char>>& board, string word) {
//这就是简单的dfs吧..
int n=board.size();
int m=board[0].size();
vector<vector<bool>> visit(n,vector<bool>(m,false));
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(visit[i][j]==false){
get(board,visit,0,word,i,j,n,m);
if(ans) return ans;
}
}
}
return false;
}
};
//经常不知道起点终点的...真是笨
class Solution {
int M, N;
public:
bool exist(vector<vector<char>>& board, string word) {
M = board.size();
if(!M) return false;
N = board[0].size();
for(int i = 0; i < M; i++)
for(int j = 0; j < N; j++)
if(board[i][j] == word[0])
if(backtrack(board, word, i, j, 0))
return true;
return false;
}
bool backtrack(vector<vector<char>>& board, string& word, int row, int col, int num)
{
if(row < 0 || row >= M || col < 0 || col >= N || board[row][col] != word[num])
return false;
if(num == word.size()-1)
return true;
char curr = board[row][col];
board[row][col] = '#';
if(backtrack(board, word, row, col + 1, num + 1)) return true;
if(backtrack(board, word, row, col - 1, num + 1)) return true;
if(backtrack(board, word, row + 1, col, num + 1)) return true;
if(backtrack(board, word, row - 1, col, num + 1)) return true;
board[row][col] = curr;
return false;
}
};
难度中等612
给定一个二叉树,返回它的中序 遍历。
示例:
输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
/**
* 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:
void get(TreeNode* root,vector<int> &ans){
if(root==NULL) return;
get(root->left,ans);
ans.push_back(root->val);
get(root->right,ans);
}
vector<int> inorderTraversal(TreeNode* root) {
if(root==NULL) return {};
vector<int> ans;
get(root,ans);
return ans;
}
};
/*
颜色标记法-一种通用且简明的树遍历方法
使用颜色标记节点的状态,新节点为白色,已访问的节点为灰色。
如果遇到的节点为白色,则将其标记为灰色,然后将其右子节点、自身、左子节点依次入栈。
如果遇到的节点为灰色,则将节点的值输出。
作者:hzhu212
链接:https://leetcode-cn.com/problems/binary-tr
ee-inorder-traversal/solution/yan-se-biao-ji-fa
-yi-chong-tong-yong-qie-jian-ming/
*/
解释一下为什么需要“右子节点、自身、左子节点依次入栈”
我们有一棵二叉树:
中
/ \
左 右
前序遍历:中,左,右
中序遍历:左,中,右
后序遍历:左,右,中
本题需要中序遍历。
栈是一种 先进后出的结构,出栈顺序为左,中,右
那么入栈顺序必须调整为倒序,也就是右,中,左
同理,如果是前序遍历,入栈顺序为 右,左,中;后序遍历,入栈顺序中,右,左
这个题解的宗旨就是使用一个颜色或者任何东西,记录节点的访问次数。
在中序中,节点第二次访问会被输出。因此使用颜色,或者hash表来记
录节点的访问次数,次数使用hash表来记录
class Solution {
vector<int>ans;
public:
vector<int> inorderTraversal(TreeNode* root) {
int white = 0;
int gray = 1;
stack<pair<int, TreeNode*>>s;
s.push(make_pair(white,root));
while (!s.empty())
{
int color = s.top().first;
TreeNode* t = s.top().second;
s.pop();
if (t == NULL) continue;
if (color == white)
{
s.push(make_pair(white, t->right));
s.push(make_pair(gray, t));
s.push(make_pair(white, t->left));
}
else ans.push_back(t->val);
}
return ans;
}
};
前序遍历:
在迭代算法中,思路演变成,每到一个节点 A,就应该立即访问它。
因为,每棵子树都先访问其根节点。对节点的左右子树来说,也一定是先访问根。
在 A 的两棵子树中,遍历完左子树后,再遍历右子树。
因此,在访问完根节点后,遍历左子树前,要将右子树压入栈。
栈S;
p= root;
while(p || S不空){
while(p){
访问p节点;
p的右子树入S;
p = p的左子树;
}
p = S栈顶弹出;
}
后序遍历与前序遍历相对称。
思路: 每到一个节点 A,就应该立即访问它。 然后将左子树压入栈,再次遍历右子树。
遍历完整棵树后,结果序列逆序即可。
栈S;
p= root;
while(p || S不空){
while(p){
访问p节点;
p的左子树入S;
p = p的右子树;
}
p = S栈顶弹出;
}
结果序列逆序;
二叉树的中序遍历
思路:每到一个节点 A,因为根的访问在中间,将 A 入栈。然后遍历左子树,接着访问 A,最后遍历右子树。
在访问完 A 后,A 就可以出栈了。因为 A 和其左子树都已经访问完成。
栈S;
p= root;
while(p || S不空){
while(p){
p入S;
p = p的左子树;
}
p = S.top 出栈;
访问p;
p = p的右子树;
}
难度中等745收藏分享切换为英文关注反馈
给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?
示例:
输入: 3 输出: 5 解释: 给定 n = 3, 一共有 5 种不同结构的二叉搜索树: 1 3 3 2 1 \ / / / \ \ 3 2 1 1 3 2 / / \ \ 2 1 2 3
动态规划,其实从二叉搜索树的中序来入手
本题思路解释
由于1,2...n这个数列是递增的,所以我们从任意一个位置“提起”这课树,都满足二叉搜索树的这个条件:左边儿子数小于爸爸数,右边儿子数大于爸爸数
从1,2,...n数列构建搜索树,实际上只是一个不断细分的过程
例如,我要用[1,2,3,4,5,6]构建
首先,提起"2"作为树根,[1]为左子树,[3,4,5,6]为右子树
现在就变成了一个更小的问题:如何用[3,4,5,6]构建搜索树?
比如,我们可以提起"5"作为树根,[3,4]是左子树,[6]是右子树
现在就变成了一个更更小的问题:如何用[3,4]构建搜索树?
那么这里就可以提起“3”作为树根,[4]是右子树
或"4"作为树根,[3]是左子树
可见n=6时的问题是可以不断拆分成更小的问题的
假设f(n) = 我们有n个数字时可以构建几种搜索树
我们可以很容易得知几个简单情况 f(0) = 1, f(1) = 1, f(2) = 2
(注:这里的f(0)可以理解为=1也可以理解为=0,这个不重要,我们这里理解为=1,即没有数字时只有一种情况,就是空的情况)
那n=3时呢?
我们来看[1,2,3]
如果提起1作为树根,左边有f(0)种情况,右边f(2)种情况,左右搭配一共有f(0)*f(2)种情况
如果提起2作为树根,左边有f(1)种情况,右边f(1)种情况,左右搭配一共有f(1)*f(1)种情况
如果提起3作为树根,左边有f(2)种情况,右边f(0)种情况,左右搭配一共有f(2)*f(0)种情况
容易得知f(3) = f(0)*f(2) + f(1)*f(1) + f(2)*f(0)
同理,
f(4) = f(0)*f(3) + f(1)*f(2) + f(2)*f(1) + f(3)*f(0)
f(5) = f(0)*f(4) + f(1)*f(3) + f(2)*f(2) + f(3)*f(1) + f(4)*f(0)
......
发现了咩?
对于每一个n,其式子都是有规律的
每一项两个f()的数字加起来都等于n-1
既然我们已知f(0) = 1, f(1) = 1
那么就可以先算出f(2),再算出f(3),然后f(4)也可以算了...
计算过程中可以把这些存起来,方便随时使用
最后得到的f(n)就是我们需要的解了
作者:xiao-yan-gou
链接:https://leetcode-cn.com/problems/unique-binary-search-trees/solution/er-cha-sou-suo-shu-fu-xi-li-zi-jie-shi-si-lu-by-xi/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
假设n个节点存在二叉排序树的个数是G(n),令f(i)为以i为根的二叉搜索树的个数,则
G(n)=f(1)+f(2)+f(3)+f(4)+...+f(n)
当i为根节点时,其左子树节点个数为i-1个,右子树节点为n-i,则
f(i)=G(i−1)∗G(n−i)
综合两个公式可以得到 卡特兰数 公式
G(n)=G(0)∗G(n−1)+G(1)∗(n−2)+...+G(n−1)∗G(0)
作者:guanpengchn
链接:https://leetcode-cn.com/problems/unique-b
inary-search-trees/solution/hua-jie-suan-fa-96-
bu-tong-de-er-cha-sou-suo-shu-b/
class Solution {
public:
int numTrees(int n) {
vector<int> G(n + 1, 0);
G[0] = 1;
G[1] = 1;
for (int i = 2; i <= n; ++i) {
for (int j = 1; j <= i; ++j) {
G[i] += G[j - 1] * G[i - j];
}
}
return G[n];
}
};
难度中等698收藏分享切换为英文关注反馈
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入: 2 / \ 1 3 输出: true
示例 2:
输入: 5 / \ 1 4 / \ 3 6 输出: false 解释: 输入为: [5,1,4,null,null,3,6]。 根节点的值为 5 ,但是其右子节点值为 4 。
//主要是max和min的值可以就是根的值,那么递归的时候直接用就行。
bool isValidBST(TreeNode* root, TreeNode* min,TreeNode* max){
if(root==NULL) return true;
if(min!=NULL&&root->val<=min->val) return false;
if(max!=NULL&&root->val>=max->val) return false;
return isValidBST(root->left,min,root)&&
isValidBST(root->right,root,max);
}
bool isValidBST(TreeNode* root){
return isValidBST(root,NULL,NULL);
}
//还有一种方法是使用中序遍历
//因为中序遍历的结果总是 升序。
注意& 或者用全局变量
class Solution {
public:
bool isBSTUtil(TreeNode* root,long long& prev){
if(root){
if(!isBSTUtil(root->left,prev)) return false;
// 当前结点小于等于它的直接前驱顶点,返回false
if(root->val<=prev) return false;
prev=root->val;
return isBSTUtil(root->right,prev);
}
return true;
}
bool isValidBST(TreeNode* root) {
long long prev = LONG_MIN;
return isBSTUtil(root, prev);
}
};
作者:novice2master
链接:https://leetcode-cn.com/problems/validate-binary-search-tree/solution/bao-zhun-sheng-guo-guan-fang-ti-jie-by-novice2mast/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
难度简单948收藏分享切换为英文关注反馈
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3]
是对称的。
1 / \ 2 2 / \ / \ 3 4 4 3
但是下面这个 [1,2,2,null,3,null,3]
则不是镜像对称的:
1 / \ 2 2 \ \ 3 3
class Solution {
public:
bool isSame(TreeNode* root1,TreeNode* root2){
if(root1==NULL&&root2==NULL) return true;
if(root1==NULL||root2==NULL) return false;
if(root1->val!=root2->val) return false;
return isSame(root1->left,root2->right)&&isSame(root1->right,root2->left);
}
bool isSymmetric(TreeNode* root) {
if(root==NULL) return true;
return isSame(root->left,root->right);
}
};