leetcode Ch3-DFS & Backtracking I
一、树的遍历
【非递归版】
1. 后序
class Solution
{
public:
vector<int> postorderTraversal(TreeNode *root)
{
vector<int> answer;
stack<pair<TreeNode*,int>> s;
s.push(make_pair(root,));
while(!s.empty())
{
TreeNode *now=s.top().first;
if(now==NULL)
{
s.pop();
}
else
{
switch(s.top().second++)
{
case :
s.push(make_pair(now->left,));
break;
case :
s.push(make_pair(now->right,));
break;
case :
s.pop();
answer.push_back(now->val);
break;
}
}
}
return answer;
}
};
这里用pair中的first存放指针,second存放一个int值。这个int值是用来标记当前节点的孩子(子树)的访问情况,值取0表示该节点的左孩子、右孩子都尚未访问;值取1表示左孩子已访问,右孩子尚未访问;值取2表示左右孩子都已访问过。这样,当值为2时就可以把当前结点的值输出(后序的定义)。
update1: 记得判断栈顶元素对应的first指针非空。
2.先序/前序
如果按基于上面后序的代码模式来改写,也可以轻松的改为先序。只是没有必要这么繁琐。后面有个不基于改写的、简化版的。
改写版的代码如下
class Solution
{//trying to change postorder to preorder
public:
vector<int> preorderTraversal(TreeNode *root)
{
vector<int> answer;
stack<pair<TreeNode*,int>> s;
s.push(make_pair(root,));
while(!s.empty())
{
TreeNode *now=s.top().first;
if(now==NULL)
{
s.pop();
}
else
{
if(s.top().second==)//this can guarantee the node will not be repeatedly pushed into answer.
answer.push_back(now->val);
switch(s.top().second++)
{
case :
s.push(make_pair(now->left,));
break;
case :
s.push(make_pair(now->right,));
break;
case :
s.pop();
//answer.push_back(now->val);
break;
}
}
}
return answer;
}
};
这样的改写是有效的,但是其实不必这么麻烦。因为对后序来说,必须要保存一个int值记录状态信息,从而在节点的左右子树都访问过后才能输出该节点的value。但是对前序而言,实际上只要遇到节点就输出其value即可,然后把该节点左右孩子压入栈,之后该节点就没有存在价值了。因此,前序不用像后序那样用一个int来记录状态信息。 这里需要注意的是,在将节点的左右孩子压入栈时,应该先压入其右孩子,再压入其左孩子。
Explanation
简化版代码如下:
class Solution
{
public:
vector<int> preorderTraversal(TreeNode* root)
{
vector<int> answer;
stack<TreeNode*> s;
s.push(root);
while(!s.empty())
{
TreeNode* now=s.top();
s.pop();//don't forget!
if(now!=NULL)
{
answer.push_back(now->val);
s.push(now->right);
s.push(now->left);
}
}
return answer;
}
};
update 8.9
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> s;
s.push(root);
while (!s.empty()) {
TreeNode* cur = s.top();
if (cur == NULL) {
s.pop();
} else {
result.push_back(cur->val);
s.pop();
s.push(cur->right);
s.push(cur->left);
}
}
return result;
}
3. 中序
同样,对于中序也可以用后序那种模板式的代码来改写。改写版代码如下:
class Solution
{
public:
vector<int> inorderTraversal(TreeNode* root)
{
vector<int> answer;
stack<pair<TreeNode*,int>> s;
s.push(make_pair(root,));
while(!s.empty())
{
TreeNode* now=s.top().first;
if(now==NULL)
s.pop();
else
{
switch(s.top().second++)
{
case :
s.push(make_pair(now->left,));
break;
case :
s.pop();
answer.push_back(now->val);
s.push(make_pair(now->right,));
break;
}
}
}
return answer;
}
};
【递归版】
class Solution
{
public:
vector<int> inorderTraversal(TreeNode* root)
{
vector<int> answer;
helper(root,answer);
return answer;
}
void helper(TreeNode* root,vector<int> &answer)
{
if(root!=NULL)
{
helper(root->left,answer);
answer.push_back(root->val);
helper(root->right,answer);
}
}
};
【Divide & Conquer版】(在树相关的问题中更常用)
preorder:
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
if (root == NULL) {
return result;
} // Divide
vector<int> left = preorderTraversal(root->left);
vector<int> right = preorderTraversal(root->right); // Conquer
result.push_back(root->val);
result.insert(result.end(), left.begin(), left.end());
result.insert(result.end(), right.begin(), right.end());
return result;
}
};
Morris版
二、N-Queens
class Solution
{
public:
vector<vector<string>> solveNQueens(int n)
{
vector<vector<string>> result;
N=n;
columns=vector<int>(n,);
anti_diag=vector<int>(*n,);
main_diag=vector<int>(*n,);
C=vector<int>(n,);
dfs(result,);
return result;
}
private:
vector<int> columns;
vector<int> main_diag;
vector<int> anti_diag;
vector<int> C;
int N; void dfs(vector<vector<string>> &result,int row)//如果把result也定义在class里当成员变量,那dfs就只用row一个参数就行了。
{
if(row==N)
{
vector<string> tmp;
for(int i=;i<N;i++)
{
string str(N,'.');
str[C[i]]='Q';
tmp.push_back(str);
}
result.push_back(tmp);
return;
}
for(int j=;j<N;j++)
{
bool ok=columns[j]== && anti_diag[j+row]== && main_diag[row-j+N]==;
if(!ok) continue;
C[row]=j;
columns[j]=anti_diag[row+j]=main_diag[row-j+N]=;
dfs(result,row+);
columns[j]=anti_diag[row+j]=main_diag[row-j+N]=;
}
}
};
需要注意的:除了每条横线、竖线上不能有1个以上的queen之外,斜线上也不能有1个以上的queen。 斜线具体需要分为平行于主对角线的这2n-1条线和平行于反对角线的2n-1条线。 对于平行于主对角线main_diag的这些斜线上的点而言,行与列之和(row+j)是定值,其和分布在0~2N-。 对于平行于反对角线anti_diag的这些斜线上的点而言,行与列之差(row-j)是定值,其差分布在-(N-)~(N-)。为了将其转换为数组下标方便计算,将其加上N,这样就转换为分布在1~2N-. 在写N皇后问题的dfs函数时意识到,其实有时候dfs里的参数很多主要是因为这些变量是定义在主角函数里的(比如这里的solveNQueens)。如果把这些变量都定义在class里面(通常放到private区域里),dfs就不用传这么多参数了。
Explanation
class Solution
{
public:
int totalNQueens(int n)
{
N=n;
columns=vector<int>(n,);
anti_diag=vector<int>(*n,);
main_diag=vector<int>(*n,);
C=vector<int>(n,);
int count=;
dfs(count,);
return count;
}
private:
vector<int> columns;
vector<int> main_diag;
vector<int> anti_diag;
vector<int> C;
int N;
//int count=0;
void dfs(int &count,int row)
{
if(row==N)
{
count++;
return;
}
for(int j=;j<N;j++)
{
bool ok=columns[j]== && anti_diag[row+j]== && main_diag[row-j+N]==;
if(!ok) continue;
columns[j]=anti_diag[j+row]=main_diag[row-j+N]=;
C[row]=j;
dfs(count,row+);
columns[j]=anti_diag[row+j]=main_diag[row-j+N]=;
}
}
};
需要注意的是,if(!ok)时应当continue,不是return。
三、
1. Maximum Depth of Binary Tree
[Divide & Conquer 版本][推荐]
class Solution {
public:
int maxDepth(TreeNode* root) {
if (root == NULL) {
return ;
}
int left = maxDepth(root->left);
int right = maxDepth(root->right);
return max(left, right) + ;
}
};
[简洁版本] 时间复杂度 O(n), 空间复杂度O(logn)
class Solution
{
public:
int maxDepth(TreeNode* root)
{
if(root==NULL) return ;
return max(maxDepth(root->left),maxDepth(root->right))+;
}
};
[模版式dfs版本]
class Solution
{
public:
int maxDepth(TreeNode* root)
{
if(!root) return ;
helper(root,);
return max;
}
private:
int max=;
void helper(TreeNode* root, int count)
{
if(root==NULL)
{
if(count->max)
max=count-;
return;
}
helper(root->left,count+);
helper(root->right,count+);
}
};
非递归版:基于前序遍历的基础之上,加上一个记录各节点高度的unordered_map和一个变量max即可。(所以说,熟练掌握树的非递归遍历后,可以在其基础上略加修改即可达到很多其他要求。)
class Solution
{
public:
int maxDepth(TreeNode* root)
{
if(!root) return ;
stack<TreeNode*> s;
s.push(root);
unordered_map<TreeNode*,int> umap;
umap[root]=;
while(!s.empty())
{
TreeNode* now=s.top();
s.pop();
if(now!=NULL)
{
if(!now->left && !now->right)
{
if(umap[now]>max)
max=umap[now];
}
s.push(now->right);
if(now->right!=NULL)//可删掉,不影响
umap[now->right]=umap[now]+;
s.push(now->left);
if(now->left!=NULL)//可删掉,不影响
umap[now->left]=umap[now]+;
}
}
return max;
}
private:
int max=;
};
2. Minimum Depth of Binary Tree
递归版:
[模版式版本]
class Solution
{
public:
int minDepth(TreeNode* root)
{
if(root==NULL) return ;
helper(root,);
return minD;
}
private:
int minD=INT_MAX;
void helper(TreeNode* root,int count)
{
if(!root->left && !root->right)
{
if(count<minD)
minD=count;
return;
}
if(root->left)
helper(root->left,count+);
if(root->right)
helper(root->right,count+);
}
};
注意:求minDepth时有很多小细节容易忽略和写错,要多注意。maxDepth里可以通过求所有NULL节点的上一层的高度的最大值来作为maxD,但是minDepth不能用此法,因为一个节点的最小高度不等于min(minDepth(root->left),minDepth(root->right))+1()
update 8.10:
class Solution {
public:
int minDepth(TreeNode* root) {
if (root == NULL) {
return ;
}
return getMin(root);
}
int getMin(TreeNode* root) {
if (root == NULL) {
return INT_MAX;
}
if (root->left == NULL && root->right == NULL) {
return ;
}
return min(getMin(root->left), getMin(root->right)) + ;
}
};
这里用了一个trick。若root==NULL,则视为其minDepth是INT_MAX。通过这种方式来忽略那些只有左子树或只有右子树为空的节点的干扰。
非递归版:(与求maxDepth的大体类似)
class Solution
{
public:
int minDepth(TreeNode* root)
{
if(root==NULL) return ;
stack<TreeNode*> s;
s.push(root);
unordered_map<TreeNode*,int> umap;
umap[root]=;
int minD=INT_MAX;
while(!s.empty())
{
TreeNode* now=s.top();
s.pop();
if(now!=NULL)
{
if(!now->left && !now->right)
{
if(umap[now]<minD)
minD=umap[now];
}
s.push(now->right);
umap[now->right]=umap[now]+;
s.push(now->left);
umap[now->left]=umap[now]+;
}
}
return minD;
}
};
四、
1. Same Tree
class Solution {
public:
bool isSameTree(TreeNode* p,TreeNode* q)
{
if(p==NULL && q==NULL)
return true;
if(p==NULL || q==NULL) //能运行到这说明上个if的判定不成立,即p和q并不都为NULL.当一个为NULL一个不为NULL时,说明结构不同,即不相等。
return false;
if(p->val!=q->val)
return false;
return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}
};
2. Symmetric Tree
这里面直接就调用了same tree的代码(微加修改),isSameTree函数的改动之处就在于最后是p的left与q的right比较,p的right与q的left比较。
class Solution
{
public:
bool isSymmetric(TreeNode* root)
{
if(root==NULL) return true;
return isSameTree(root->left,root->right);
}
private:
bool isSameTree(TreeNode* p,TreeNode* q)
{
if(p==NULL && q==NULL) return true;
if(p==NULL || q==NULL) return false;
if(p->val!=q->val) return false;
return isSameTree(p->left,q->right)&&isSameTree(p->right,q->left);//left和right比较
}
};
五、
1. Populating Next Right Pointers in Each Node
class Solution
{
public:
void connect(TreeLinkNode* root)
{
if(root==NULL) return;
if(root->left!=NULL)
root->left->next=root->right;
if(root->right!=NULL)
root->right->next=root->next==NULL?NULL:root->next->left;
connect(root->left);
connect(root->right);
}
};
2. Populating Next Right Pointers in Each Node II
class Solution
{
public:
void connect(TreeLinkNode* root)
{
TreeLinkNode* tempChild=new TreeLinkNode();
while(root)//root从上到下遍历每一层(纵向)
{
TreeLinkNode* cur=tempChild;
while(root)//root从左到右遍历某个特定的层(横向)
{
if(root->left){cur->next=root->left;cur=cur->next;}
if(root->right){cur->next=root->right;cur=cur->next;}
root=root->next;//root向右移动
}
root=tempChild->next;//root跳到下一层
tempChild->next=NULL;
}
}
};
六、
1. Convert Sorted Array to Binary Search Tree
class Solution
{
public:
TreeNode* sortedArrayToBST(vector<int>& num)
{
return helper(num,,num.size()-);
}
private:
TreeNode* helper(vector<int> &num,int start,int end)
{
if(start>end) return NULL;
int mid=start+(end-start)/;
TreeNode* root=new TreeNode(num[mid]);
root->left=helper(num,start,mid-);
root->right=helper(num,mid+,end);
return root;
}
};
值得注意的是,如果把helper参数列表里的vector<int> &num的引用去掉,变成vector<int> num, 那么会发生TLE. 所以在用vector之类的容器时加引用还是很重要的。
2. Convert Sorted List to Binary Search Tree
class Solution
{
public:
TreeNode* sortedListToBST(ListNode* head)
{
int count=calLen(head);
return helper(head,,count-);
}
private:
int calLen(ListNode* head)
{
int count=;
while(head!=NULL)
{
head=head->next;
count++;
}
return count;
}
TreeNode* helper(ListNode* head, int start, int end)
{
if(start>end) return NULL;
int mid=start+(end-start)/;
ListNode *p=head;
for(int i=start;i<mid;i++)
p=p->next;
TreeNode* root=new TreeNode(p->val);
root->left=helper(head,start,mid-);
root->right=helper(p->next,mid+,end);
return root;
}
};
注意细节问题。
code 1: Divide & Conquer
class Solution {
public:
bool isBalanced(TreeNode* root) {
return maxDepth(root) != -;
}
int maxDepth(TreeNode* root) {
if (root == NULL) {
return ;
}
int left = maxDepth(root->left);
int right = maxDepth(root->right);
if (left == - || right == - || abs(left - right) > ) {
return -;
}
return max(left, right) + ;
}
};
code 2:
class Solution
{
public:
bool isBalanced(TreeNode* root)
{
if(!root) return true;
return (isBalanced(root->left) && isBalanced(root->right) && balancedNode(root));
}
private:
bool balancedNode(TreeNode* root)
{
int dif=height(root->left)-height(root->right);
if(dif>=- && dif<=) return true;
else return false;
}
int height(TreeNode* root)
{
if(root==NULL) return ;
return(max(height(root->left),height(root->right))+);
}
};
八、
非递归版本:
class Solution
{
public:
int sumNumbers(TreeNode* root)
{
if(root==NULL) return ;
postorderTraversal(root);
return result;
}
private:
int result=;
void postorderTraversal(TreeNode* root)
{
stack<pair<TreeNode*,int>> s;
s.push(make_pair(root,));
string str;
while(!s.empty())
{
TreeNode* now=s.top().first;
if(now==NULL) s.pop();
else
{
switch(s.top().second++)
{
case :
str+=to_string(now->val);
s.push(make_pair(now->left,));
break;
case :
s.push(make_pair(now->right,));
break;
case :
s.pop();
if(!now->left && !now->right)
result+=stoi(str);
str.resize(str.size()-);
break; }
}
}
}
};
利用后序遍历,在每次第3次遇到一个节点(case:2) 且该节点为叶子节点时,使result累加上此时过往路径对应的整数(其实这里的过往路径相当于遍历一遍当前栈内元素)。
递归版本还没理解好,先放在这。
class Solution {
public:
int sumNumbers(TreeNode *root) {
return dfs(root, );
}
private:
int dfs(TreeNode *root, int sum) {
if (root == nullptr) return ;
if (root->left == nullptr && root->right == nullptr)
return sum * + root->val;
return dfs(root->left, sum* + root->val) + dfs(root->right, sum* + root->val);
}
};
2. Path Sum
非递归版本:(和上题类似,只不过计算path sum的方式不同。)
class Solution
{
public:
bool hasPathSum(TreeNode* root,int sum)
{
return postorderTraversal(root,sum);
}
private:
int tmp=;
bool postorderTraversal(TreeNode* root,int target)
{
stack<pair<TreeNode*,int>> s;
s.push(make_pair(root,));
string str;
while(!s.empty())
{
TreeNode* now=s.top().first;
if(now==NULL)
s.pop();
else
{
switch(s.top().second++)
{
case :
tmp+=now->val;
s.push(make_pair(now->left,));
break;
case :
s.push(make_pair(now->right,));
break;
case :
s.pop();
if(!now->left && !now->right)
if(tmp==target)
return true;
tmp-=now->val;
break;
}
}
}
return false;
}
};
递归版本:(再理解一下)
class Solution {
public:
bool hasPathSum(TreeNode *root, int sum) {
if (root == NULL) return false;
if (root->left == NULL && root->right == NULL) // leaf
return sum == root->val;
return hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val);
}
};
写递归程序主要就是看递归出口以及如何缩小规模变成一个完全一样只是规模略小的子问题。
像这里就是由求root为根的一颗子树的hasPathSum转换为求分别以root->left和root->right为根的两颗子树的hasPathSum,其中target相应的减去root->val。
递归出口就是对于leaf节点(左右子树都为空)和NULL节点的处理情况。
update 8.10
bool hasPathSum(TreeNode* root, int sum) {
if (root == NULL) {
return false;
}
if (root->left == NULL && root->right == NULL && root->val == sum) {
return true;
}
return (hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val));
}
递归-模板化版本:(personal style)
class Solution
{
public:
bool hasPathSum(TreeNode* root,int sum)
{
return helper(root,sum);
}
bool helper(TreeNode* root,int target)
{
if(root==NULL) return false;
if(root->left==NULL && root->right==NULL)
return root->val==target;
return helper(root->left,target-root->val)||helper(root->right,target-root->val);
}
};
3. Path Sum II
非递归版本:(非递归用起来确实很方便,但是递归版本必须要熟练掌握)
class Solution
{
public:
vector<vector<int>> pathSum(TreeNode* root,int sum)
{
vector<vector<int>> result;
postorderTraversal(root,result,sum);
return result;
}
void postorderTraversal(TreeNode* root, vector<vector<int>> &result,int target)
{
stack<pair<TreeNode*,int>> s;
s.push(make_pair(root,));
vector<int> tmp;
int sum=;
while(!s.empty())
{
TreeNode* now=s.top().first;
if(now==NULL) s.pop();
else
{
switch(s.top().second++)
{
case :
tmp.push_back(now->val);
sum+=now->val;
s.push(make_pair(now->left,));
break;
case :
s.push(make_pair(now->right,));
break;
case :
s.pop();
if(!now->left&&!now->right)
{
if(sum==target)
result.push_back(tmp);
}
sum-=now->val;
tmp.pop_back();
break;
}
}
}
}
};
递归版本:
class Solution {
public:
vector<vector<int>> pathSum(TreeNode* root,int sum)
{
vector<vector<int>> result;
helper(result,root,sum);
return result;
}
void helper(vector<vector<int>> &result,TreeNode* root,int target)
{
if(root==NULL) return;
tmp.push_back(root->val);
if(root->left==NULL && root->right==NULL)
{
if(root->val==target)
result.push_back(tmp);
}
helper(result,root->left,target-root->val);
helper(result,root->right,target-root->val);
tmp.pop_back();
}
vector<int> tmp;
};
九、Flatten Binary Tree to Linked List
这道题目要求有说in-place, 怎么样算in-place?
非递归版本:
class Solution
{
public:
void flatten(TreeNode* root)
{
preorderTraversal(root);
}
void preorderTraversal(TreeNode* root)
{
if(root==NULL) return;
stack<TreeNode*> s;
s.push(root);
while(!s.empty())
{
TreeNode* now=s.top();
s.pop();
if(now!=NULL)
{//必须要确保压入栈的只能是非NULL的,否则会影响到now->right的指向。now->right只能指向节点,不能是NULL(除非是最后一个节点)。
if(now->right!=NULL)
s.push(now->right);
if(now->left!=NULL)
s.push(now->left);
now->left=NULL;
if(!s.empty())//检查一下是否栈非空,因为要用到s.top().(毕竟上次判断完非空后有pop操作)
now->right=s.top();
}
}
}
};
非递归版本在先序遍历的基础上有一些细节改动,主要是需要确保入栈的只能是非NULL节点。
递归版本:
一开始没有思路,后来看了code ganker的讲解和该博客的代码,原来只需要保存一个pre即可。
class Solution
{
public:
void flatten(TreeNode* root)
{
helper(root,NULL);
}
TreeNode* helper(TreeNode* root,TreeNode* pre)
{
if(root==NULL) return pre;
if(pre!=NULL)
pre->right=root;
pre=root;//每访问一个节点,pre就指向该节点。
TreeNode* left=root->left;
root->left=NULL;
TreeNode* right=root->right;
pre=helper(left,pre);
pre=helper(right,pre);
return pre;
}
};
本质上是在前序遍历的递归代码基础上改的。每遍历一个节点,pre都会更新并指向该节点。所以倒数第3行中pre=helper(left,pre); 执行完后,pre的值即为先序遍历root的左子树的最后一个点。这个需要注意一下,加深理解。
十、
1. Construct Binary Tree from Preorder and Inorder Traversal
class Solution
{
public:
TreeNode* buildTree(vector<int> &preorder,vector<int> &inorder)
{
int len=preorder.size();
return helper(preorder,,len-,inorder,,len-);
}
TreeNode* helper(vector<int> &preorder,int s1,int e1,vector<int>& inorder,int s2,int e2)
{
if(s1>e1||s2>e2) return NULL;
TreeNode* node=new TreeNode(preorder[s1]);
int index=find(inorder,node->val);
node->left=helper(preorder,s1+,index-s2+s1,inorder,s2,index-);
node->right=helper(preorder,index-s2+s1+,e1,inorder,index+,e2);
return node;
}
int find(vector<int> &inorder,int x)
{
for(int i=;i<inorder.size();i++)
{
if(inorder[i]==x)
return i;
}
return -;
}
};
需要注意Line14,15的递归下标别写错。
2. Construct Binary Tree from Inorder and Postorder Traversal
class Solution
{
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder)
{
int len=inorder.size();
return helper(inorder,,len-,postorder,,len-);
}
TreeNode* helper(vector<int> &inorder,int s1,int e1,vector<int> &postorder,int s2,int e2)
{
if(s1>e1||s2>e2) return NULL;//别忘了递归出口
TreeNode* node=new TreeNode(postorder[e2]);
int index=find(inorder,node->val);
node->left=helper(inorder,s1,index-,postorder,s2,index--s1+s2);
node->right=helper(inorder,index+,e1,postorder,index-s1+s2,e2-);
return node;
}
int find(vector<int> &inorder,int x)
{
for(int i=;i<inorder.size();i++)
if(inorder[i]==x) return i;
return -;
}
};
写递归函数不要忘了写递归出口。
十一、Binary Tree Right Side View
用BFS非递归做的。但是代码有点繁琐。
而且,需要掌握一下递归做法。
class Solution
{
public:
vector<int> rightSideView(TreeNode* root)
{
vector<int> result;
if(!root) return result;
queue<pair<TreeNode*,int>> q;
q.push(make_pair(root,));
TreeNode* pre=root;
int preLevel=;
while(!q.empty())
{
TreeNode* now=q.front().first;
if(preLevel!=q.front().second)
result.push_back(pre->val);
if(now->left==NULL&&now->right==NULL&&q.size()==)
result.push_back(now->val);
preLevel=q.front().second;
pre=now;
q.pop();
if(now->left!=NULL)
q.push(make_pair(now->left,preLevel+));
if(now->right!=NULL)
q.push(make_pair(now->right,preLevel+));
}
return result;
}
};
递归版本:
class Solution
{
public:
void recoverTree(TreeNode* root)
{
helper(root);
int tmp=p1->val;
p1->val=p2->val;
p2->val=tmp;
}
void helper(TreeNode* root)
{
if(root==NULL) return;
helper(root->left);
if(pre>root->val && flag==)
{
flag=;
p1=prePtr;
p2=root;
}
else if(pre>root->val && flag==)
p2=root;
pre=root->val;
prePtr=root;
helper(root->right);
}
int pre=INT_MIN;
TreeNode* p1=NULL,*p2=NULL;
TreeNode* prePtr=NULL;
int flag=;
};
一个重要的特性就是,在本应递增的序列里,如果有两个值x1,x2发生了互换,那么会有两种情况:
case1:两个值不相邻
那么第1次出现left>right的地方,这里的left即为x1.(因为它本应在后面,是大值,现在换到前面来必然导致它大于其右侧的数字)。应用pre指针记录下这个left对应节点。
第2次出现left>right的地方,这里的right即为x2.(同理)。
case2:两个值相邻。则只会出现1次left>right的地方。
所以,综合case1和case2,在第一次遇到left>right时就把left和right分别标记为p1,p2,这样不管会不会有第2次left>right都无所谓,当有第2次left>right时只需更新一下p2即可。
改天把非递归版本补上。
十三、Clone Graph
class Solution
{
public:
typedef UndirectedGraphNode UGNode;
UndirectedGraphNode* cloneGraph(UndirectedGraphNode* node)
{
if(node==NULL) return NULL;
return dfs(node);
}
UGNode* dfs(UGNode* node)
{
UGNode* p=new UGNode(node->label);
umap[p->label]=p;//注意,这里对umap赋值只能是p不能是node,因为在下面加入p的邻居时会用到。
for(int i=;i<node->neighbors.size();i++)
{
if(umap.find(node->neighbors[i]->label)==umap.end())
p->neighbors.push_back(dfs(node->neighbors[i]));
else
p->neighbors.push_back(umap[node->neighbors[i]->label]);
}
return p;
}
unordered_map<int,UGNode*> umap;
};
需要注意的是,在return时应该是return的新建的copy指针,而不是原来图中的指针。
简洁版:
class Solution
{
public:
int numIslands(vector<vector<char>> &grid)
{
if(grid.size()== || grid[].size()==) return ;
m=grid.size();
n=grid[].size();
for(int i=;i<m;i++)
{
for(int j=;j<n;j++)
{
if(grid[i][j]=='')
{
count++;
dfs(grid,i,j);
}
}
}
return count;
}
int count=;
int m,n;
void dfs(vector<vector<char>> &grid,int i,int j)
{
if(i< || j< || i>=m || j>=n) return;
if(grid[i][j]!='') return;
grid[i][j]='';
dfs(grid,i+,j);
dfs(grid,i-,j);
dfs(grid,i,j+);
dfs(grid,i,j-);
}
};
这里利用一个trick,就是访问过的'1'都改为’2‘,这样就不用再分配一个visited数组了,节省点空间。
啰嗦版,可无视之:
class Solution
{
public:
int numIslands(vector<vector<char>> &grid)
{
m=grid.size();
if(m==) return ;
n=grid[].size();
visited=vector<vector<int>>(m,vector<int>(n,));
sumOfOnes=calOnes(grid);
for(int i=;i<m;i++)
{
for(int j=;j<n;j++)
{
if(visited[i][j]== && grid[i][j]=='')
{
count++;
dfs(grid,i,j);
}
}
}
return count;
}
private:
int m, n;
int count=;
int sumOfOnes=;
int countOnes=;
vector<vector<int>> visited;
void dfs(vector<vector<char>> &matrix,int row,int col)
{
countOnes++;
visited[row][col]=;
if(countOnes>=sumOfOnes)
return;
if(row+<m && matrix[row+][col]=='' && !visited[row+][col])
dfs(matrix,row+,col);
if(col+<n && matrix[row][col+]=='' && !visited[row][col+])
dfs(matrix,row,col+);
if(row->= && matrix[row-][col]=='' && !visited[row-][col])
dfs(matrix,row-,col);
if(col->= && matrix[row][col-]=='' && !visited[row][col-])
dfs(matrix,row,col-);
}
int calOnes(vector<vector<char>> &matrix)
{
int count=;
for(int i=;i<m;i++)
{
for(int j=;j<n;j++)
{
if(matrix[i][j]=='')
count++;
}
}
return count;
}
};
一开始犯了很多小错误。比如,只向右和向下搜索,并以访问右下角元素作为递归出口。这是不对的,因为如果遍历的时候第一次遇到的1是第一行最后一个,那就只能遍历最后一列了。
参考别的答案后发现我的略繁琐,不简洁。
十五、Binary Tree Maximum Path Sum
code1: 分治 [new]
class Solution {
public:
int maxPathSum(TreeNode* root) {
int res = INT_MIN;
maxPath(root, res);
return res;
}
int maxPath(TreeNode* root, int &res) {
if (root == NULL) {
return ;
}
int left = max(, maxPath(root->left, res));
int right = max(, maxPath(root->right, res));
res = max(res, left + right + root->val);
return max(left, right) + root->val;
}
};
该题利用一个特点:任何一个path都是先升、后降,一旦降了就不能再升了(只升或只降可以看作特例)。因此每个path都有一个最高点。
所以,函数maxPath(node)可以定义为最高点为node的情况下所能取到的最大和。[注意,这里也能体现一个情况,就是分治函数往往返回值就是结果,dfs函数往往返回值是void]
几个需要解释的点:
这个maxPath函数在Line15返回的是以root为最高点单边儿所能取到的最大值,即不带拱的(只能从root往左或只能从root往右,不能带拱)。
而Line14里 left + right + root->val 是指的以当前点为最高点的允许带拱的情况下有可能取到的最大值。
//刚才写又忘了写递归出口了。小细节、corner case 很重要!!!
code2:
class Solution
{
public:
int maxPathSum(TreeNode* root)
{
dfs(root);
return maxNum;
}
int maxNum=INT_MIN;
int dfs(TreeNode* root)
{//每个dfs里默认是求sum一定会带上root的
if(root==NULL) return ;
int l=dfs(root->left);
int r=dfs(root->right);
int sum=root->val;
if(l>) sum+=l;
if(r>) sum+=r;
maxNum=max(maxNum,sum);
return max(l,r)>?root->val+max(l,r):root->val;//须格外注意
}
};
refer to soulMach. 代码默认是每次执行dfs函数时都必须把该节点带上来算入sum。
在最后返回时需要注意,该节点是被其父节点调用的,所以返回的时候不能有分叉,即只能返回一个让父节点遍历下来时不会发生分叉的情况,因此不能让sum把left和right都加上,最多加一个。
十六、Validate Binary Search Tree
class Solution
{
public:
bool isValidBST(TreeNode* root)
{
inorder(root);
if(flag==) return false;
else return true;
}
private:
void inorder(TreeNode* root)
{
if(root==NULL) return;
inorder(root->left);
if(!firstNode && pre>=root->val)
flag=;
firstNode=;
pre=root->val;
inorder(root->right);
}
int pre=INT_MIN;
int flag=;
int firstNode=;
};
我这代码看起来并不美观。第一次写时掉进去一个很隐蔽的坑:第一个节点的value就是INT_MIN。由于过度rely on INT_MIN,导致这个test case过不了。后来就专门加了个flag:firstNode,使得对第一个节点不置flag=1。不过很不美观。
更美观的做法是不用一个int值的pre,改为用TreeNode* pre.这样可令其初值为NULL,作为对第一个节点的判断依据。代码可参考此。
BST的充要条件是:中序遍历序列是升序序列。
--26 problems in all
============================================================================================================
树相关
backtracking相关
dfs相关
图的遍历相关
nowcoder related
cc150 related
leetcode Ch3-DFS & Backtracking I的更多相关文章
- Leetcode总结之Backtracking
本文我们就Leetcode中的一个类型的题目backtracking进行一系列的总结和归纳.backtracking这个方法本质是建立在递归的基础上,不断尝试新的路径,这里关键是每次尝试完以后需要退回 ...
- leetcode 39 dfs leetcode 40 dfs
leetcode 39 先排序,然后dfs 注意先整全局变量可以减少空间利用 class Solution { vector<vector<int>>ret; vector&l ...
- 【LeetCode】DFS 总结
DFS(深度优先搜索) 常用来解决可达性的问题. 两个要点: 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点.可以使用递归栈. 标记:和 BFS 一样同样需要对已经遍历过的节点进行 ...
- Leetcode题解 - DFS部分题目代码+思路(756、1034、1110、491、721、988)
756. 金字塔转换矩阵 """ 学到的新知识: from collections import defaultditc可以帮我们初始化字典,不至于取到某个不存在的值的时 ...
- Leetcode题解 - DFS部分简单题目代码+思路(113、114、116、117、1020、494、576、688)
这次接触到记忆化DFS,不过还需要多加练习 113. 路径总和 II - (根到叶子结点相关信息记录) """ 思路: 本题 = 根到叶子结点的路径记录 + 根到叶子结点 ...
- Leetcode 78. Subsets (backtracking) 90 subset
using prev class Solution { List<List<Integer>> res = new ArrayList<List<Integer&g ...
- DFS、BFS和Backtracking模板
区别与联系 区别 DFS多用于连通性问题因为其运行思想与人脑的思维很相似,故解决连通性问题更自然,采用递归,编写简便(但我个人不这样觉得...) DFS的常数时间开销会较少.所以对于一些能用DFS就能 ...
- leetcode@ [211] Add and Search Word - Data structure design
https://leetcode.com/problems/add-and-search-word-data-structure-design/ 本题是在Trie树进行dfs+backtracking ...
- leetcode算法总结
算法思想 二分查找 贪心思想 双指针 排序 快速选择 堆排序 桶排序 搜索 BFS DFS Backtracking 分治 动态规划 分割整数 矩阵路径 斐波那契数列 最长递增子序列 最长公共子系列 ...
- [LeetCode] 489. Robot Room Cleaner 扫地机器人
Given a robot cleaner in a room modeled as a grid. Each cell in the grid can be empty or blocked. Th ...
随机推荐
- 【转】Python格式化字符串str.format()
原文地址:http://blog.xiayf.cn/2013/01/26/python-string-format/ 每次使用Python的格式字符串(string formatter),2.7及以上 ...
- (转)Oracle与DB2在数据库高可用技术上的相同与差异探讨
原文:http://www.talkwithtrend.com/Article/178339 数据库建设过程中,高可用是每一个企业数据中心数据库建设过程中至关重要的一个关注点,直接关系到业务连续性和稳 ...
- javascript中prototype与__proto__
1.prototype:构造函数独有的属性: __proto__:每个对象都有一个名为__proto__的属性: 注意:每个构造函数(自带与自创)都有一个prototype的属性,构造函数的proto ...
- java数据结构---------插入排序的实现
插入排序分为直接插入排序和希尔排序 插入排序 实现方法 //插入排序,按从小到大的顺序 public static void insertSort(int[] array){ int j,temp = ...
- 穆里尼奥:曼联没有在今夏尝试过签下C罗
在曼联结束的本个夏季首场友谊赛中,球队5-2战胜了洛杉矶银河,在赛后穆里尼奥出席了赛后的新闻发布会,并且回答了记者的提问.其中他表示曼联在今年夏季从来没有尝试回签C罗,因为这是“不可能完成的任务”. ...
- An Introduction to Computer Thinking
1.Die Grundlage des Computers 1.1 Binärzahl in die Dezimalzahl umsetzen Bereiten nach Gewicht,dann b ...
- jdk1.8 HashMap源码讲解
1. 开篇名义 jdk1.8中hashMap发生了一些改变,在之前的版本中hsahMap的组成是数组+链表的形式体现,而在1.8中则改为数组+链表+红黑树的形式实现,通过下面两张图来对比一下二者的不同 ...
- JAVA练手--线程(Thread)
1. 查看线程是否还存活 package tet;public class kk extends Thread{ //1. 查看线程是否还存活 public void run(){ for(int i ...
- SimpleCalendar日历插件改版
先附上一张货真价实的效果图: 以上部分代码,为了适应我司项目的需求,原来插件源码大改(因为项目中下拉框用了select2,所以原来插件的下拉框就有问题了,在加上原来插件本身就有点问题,特别是农历 .节 ...
- 移动端的touchstart,touchmove,touchend事件中的获取当前touch位置
前提:touchstart,touchmove,touchend这三个事件可以通过原生和jq绑定. 原生:document.querySelector("#aa").addEven ...