题意:根据前序遍历和中序遍历来构造二叉树
要用递归造树,要同时递归左子树和右子树,造树需要左边边界和右边边界
build函数有树的跟指针,前序的有左边边界和右边边界,中序的左边边界和右边边界
如果l>r return;因为是先序遍历,所以左子树是先序的第一个,先构造了;
不会做不会做
上面标粗的部分是正确的,构造就在递归的时候构造,不用把树的根作为指针放到参数里面
之后就是搞清楚pl,pr和ir,il就可以了【做着做着居然做出来了!!】
例子:前序[3,0,9,4,6,2,5,7,8,1]
中序[4,9,6,0,2,3,5,8,7,1]
显而易见,左子树的pl是pl+1;左子树的il是il;左子树的ir是pos-1;
显而易见,右子树的pr=pr;右子树的il是pos+1;右子树的ir是ir
左子树是[ il,pos-1]的长度,所以pr=pl+1+pos-1-li=pl+pos-li;
右子树是[pos+1,ir]的长度,所以pl=左子树的pr+1=pl+pos-li+1
=pr-(ir-pos-1)=pr-ir+pos+1
class Solution {
public:
TreeNode*build(vector& preorder, vector& inorder,int pl,int pr,int il,int ir){
if(il>ir)return NULL;
TreeNode *root=new TreeNode(preorder[pl]);
int pos;//为了找到il和ir
for(int i=il;i<=ir;i++)
if(inorder[i]==preorder[pl]){pos=i;break;}
root->left=build(preorder,inorder,pl+1,pl+pos-il,il,pos-1);
root->right=build(preorder,inorder,pr-ir+pos+1,pr,pos+1,ir);
return root;
}
TreeNode* buildTree(vector& preorder, vector& inorder) {
int n=preorder.size()-1;
TreeNode *root=NULL;
return build(preorder,inorder,0,n,0,n);
}
};
题目中有这样一条preorder
and inorder
consist of unique values.
所以可以用map来减少中序查找的时间消耗
传递参数的时候可以省略前序序号的传递,或者说只需要一个pl就可以了,传递的时候函数参数为il和ir
class Solution {
public:
unordered_mapmp;
int pre=0;
TreeNode* build(vector& preorder,int l,int r){
if (l>r) return NULL;
int mid=preorder[pre++];
TreeNode * root=new TreeNode(mid);
int pos=mp[mid];
root->left=build(preorder,l,pos-1);
root->right=build(preorder,pos+1,r);
return root;
}
TreeNode* buildTree(vector& preorder, vector& inorder) {
int n=inorder.size()-1;
for(int i=0;i<=n;i++) mp[inorder[i]]=i;//中序序列的这个数字排在第几个序号
return build(preorder,0,n);
}
};
先序索引,但我不会写;看了答案后至少有一点是对的,就是先递归,两个递归后在做事情
分为4种情况:1只有左边的,2只有右边的,3都没有的,4都有的
首先是第一种情况,假设现在root是1这个结点【56当它不存在】,234按照道理都已经建好了;只需要root->right=root->left; root->left=NULL;就可以了
第二种情况,假设节点是5,6按道理都建好了,其实5放着不动就好了
第三种情况就先不管
第四种情况,假设节点是2,34按道理都建好了,首先要把右子树空出来,right=root->right; root->right=root->left;root->left=NULL; 这时候还需要处理原来在右子树上的结点。因为原先左子树上的都是处理好的链表,所以只要找到原先左子树上的最后一个节点,把这最后一个节点连上去就可以了
class Solution {
public:
void flatten(TreeNode* root) {
if(root==NULL)return;
flatten(root->left);
flatten(root->right);
if(root->left){
TreeNode* right=root->right;
root->right=root->left;
root->left=NULL;
while(root->right)root=root->right;
root->right=right;
}
}
};
首先如果root是空的话就返回;如果当前指针有左指针,先将pre指向左指针指向的位置;pre来到左指正的最右边,左指针的最右边指向当前指针的右边,再把原来右指针指向左指针的位置,左指针置为空;如下图所示,总之就是一点一点挪过去
class Solution {
public:
void flatten(TreeNode* root) {
if(root == NULL) return;
TreeNode *curr = root;
while(curr){
if(curr->left){
TreeNode *pre = curr->left;
while(pre->right) pre = pre->right;
pre->right = curr->right;
curr->right = curr->left;
curr->left = NULL;
}
curr = curr->right;
}
}
};
注意这里是先递归右边,相当于(我一开始想的)先到右边的尽头,之后那前一个来接上的想法【最后没写出来】
注意这里递归的顺序,学着点!!
先递归到6,这时6的右指针是prev也就是空,左指针是空,prev是6
接下来是5,5的右指针是prev也就是6,左指针是空,prev是5
接下来是4,4的右指针是prev也就是5,左指针是空,prev是4
接下来是3,3的右指针是prev也就是4,左指针是空,prev是3
……
于是就完成了
class Solution {
public:
TreeNode* prev = NULL;
void flatten(TreeNode* root) {
if(!root) return;
flatten(root->right);
flatten(root->left);
root->right = prev;
root->left = NULL;
prev = root;
}
};
用动态规划(?),第x层有x个数字,dp[x][0]=1,dp[x][x-1]=0,dp[x][i]=dp[x-1][i-1]+dp[x-1][i]
class Solution {
public:
vector> generate(int num) {
if(num==1)return {{1}};
if(num==2)return {{1},{1,1}};
vector> ans={{1},{1,1}};int temp=0;
for(int x=3;x<=num;x++){//一共num层
vector sol;sol.push_back(1);
for(int i=1;i
简而言之,就是用一维数组代替了二维数组,实现了相加提速
class Solution {
public:
vector> generate(int numRows) {
vector>ans;
ans.push_back({1});
for(int i=0;i tem=ans.back();//{1}
vector v(tem.size()+1);//开空间
for(int i=0;i<=tem.size();i++){
if(i==0){
v[i]=tem[0];continue;//1
}
if(i==tem.size())//1
v[i]=tem[tem.size()-1];
else
v[i]=tem[i-1]+tem[i];
}
ans.push_back(v);
}
return ans;
}
};
题意:给出每天袜子的价格,只能选一天买袜子,只能选一天卖袜子,求利润最大
用vector或者deque模拟单调栈?当要弹出一个大的数字的时候,就求一下栈尾和栈顶的的差,或者双指针也可以
不加取消同步在66.7%左右,感觉都差不多,都是找个最小的,然后当前的减去现在最小的,最后在"当前的"里面找最大的
class Solution {
public:
int maxProfit(vector& p) {
int l=0,r=0,n=p.size();int profit=0;
for(r=1;r
题意:树上路径和最大
学过,好像有两种做法?一种dp一种dfs大概?
下面是用dfs做的:
那就每个结点都遍历一遍,每个节点的数字就是 sum=左边的和加上右边的和,ans=max(ans, sum);返回的时候返回两条子树和最大的那个,如果和是负的那就返回0
class Solution {
public:
int ans=-9999;
int maxP(TreeNode* root) {
int l=0;int r=0;int sum=0;
if(root->left!=NULL) l=maxP(root->left);
if(root->right!=NULL) r=maxP(root->right);
sum=l+r+(root->val);
ans=max(ans,sum);
return max(max(l,r)+(root->val),0);
}
int maxPathSum(TreeNode* root) {
maxP(root);
return ans;
}
};
题意:输出最长连续序列的长度
初步设想快排+遍历,但是他会有相同的数字;那就用map
class Solution {
public:
int longestConsecutive(vector& nums) {
ios::sync_with_stdio(false);cin.tie(0);
if(nums.size()==0)return 0;
mapmp;int maxx=1;
long long pre=-9999999999;int cnt=0;
for(int i=0;i
我的2n比不上nlogn,好吧;其实和初步设想差不多,就是相同的数字的时候不加就可以了
首先先排序,之后
class Solution {
public:
int longestConsecutive(vector& nums){
ios_base::sync_with_stdio(false);cin.tie(NULL);
int n=nums.size();
if(n==0)return 0;
int maxx=0;int cnt=1;
sort(nums.begin(),nums.end());
for(int i=1;i
题意:给一个字符串,返回所有是回文串的子串
初步设想的遍历+回文串判断,但是这个返回的答案很dfs
那就试试用dfs做吧;不会做不会做
但是瞄了一眼标答 还是自己写了
class Solution {
public:
bool jud(string s){
int n=s.size();
for(int i=0;i> &result,vector &sol,int id,string &s){
if(id==s.size()){
result.push_back(sol);return;
}
for(int len=1;len+id<=s.size();len++){
string temp=s.substr(id,len);
if(jud(temp)){
sol.push_back(temp);
dfs(result,sol,id+len,s);
sol.pop_back();
}
}
}
vector> partition(string s) {
if(s.size()==0)return {{}};
vector> result;
vector sol;
dfs(result,sol,0,s);
return result;
}
};
题意:给一个数组,找出只出现一次的数字
用map存一遍,在找一遍
class Solution {
public:
int singleNumber(vector& nums) {
ios::sync_with_stdio(false);cin.tie(0);
unordered_mapmp;
for(int i=0;i
首先是异或算法的一些规律
从上图就可以看到,如果是两个一样的数字,那么它就会变成0;如果是不一样的数字,那么它就会留下来
class Solution {
public:
int singleNumber(vector& nums) {
int x = 0;
for(int i=0; i
题意:复制一个一摸一样的链表
只会链表的顺序复制,如果是随机的,那要有个map,左边是原地址,右边是现地址;
class Solution {
public:
Node* copyRandomList(Node* head) {
unordered_mapmp;
if(head==NULL)return NULL;
Node* root=new Node(head->val);
Node* q=root;Node* cr=NULL;mp[head]=root;
for(Node* p=head->next;p!=NULL;p=p->next){
cr=new Node(p->val);
q->next=cr;q=q->next;
mp[p]=cr;
}
q=root;
for(Node* p=head;p!=NULL;p=p->next){
Node* qv=p->random;
q->random=mp[qv];
q=q->next;
}
return root;
}
};
如果head是空,返回空;
copy_merge函数复制顺序的链表,其中curr是原链表的当前节点,next是原链表的下一个节点,copy是专门创造新结点的指针,curr指向新生的结点,copy指向原来下一个节点;其返回结果如图所示
handle_random函数如下如所示,简单来说就是复制的结点就是原节点的next,所以容易能找到;这样就可以原来的节点的random指向哪个,复制的节点就指向那个节点的next
最后detach函数把复制的结点全部都串在一起,原来的结点也要指回原来的结点
class Solution {
public:
void copy_merge(Node* head){
Node* curr=head; Node* next=head->next;
while(curr!=NULL){
Node* copy=new Node(curr->val);
curr->next=copy; copy->next=next;
curr=next;
if(next!=NULL) next=next->next;
}
}
void handle_random(Node* head){
Node* curr=head;
while(curr!=NULL){
if(curr->random!=NULL) curr->next->random=curr->random->next;
curr=curr->next->next;
}
}
Node* detach(Node* head){
Node* curr=head; Node* dummy=new Node(-1); Node* tail=dummy;
while(curr!=NULL){
tail->next=curr->next; tail=tail->next;
curr->next=tail->next; curr=curr->next;
}
return dummy->next;
}
Node* copyRandomList(Node* head) {
if(head==NULL) return head;
copy_merge(head); handle_random(head);
return detach(head);
}
};
题意:给你字典,问能不能用字典里的词拼接成字符串s
用dfs吗?但是dfs先分成所有可能性,再用字典判断也太费时间了吧
不会做,看看答案
因为只问了能不能组成,所以只要判断bool就可以了;
首先把worddict中的词都放到set里面,之后开辟一个bool类型的dp;
假设i是字符串总长度,j是子字符串的开头,dp[j]=1表示再j之前的字符串可以分割;
之后在set里面找是否能找到以j开头[j,i)的字符串,可以的话dp[i]=1;
class Solution {
public:
bool wordBreak(string s, vector& D) {
unordered_set st;
for(auto q:D) st.insert(q);
vectordp(s.size()+1,0);dp[0]=1;//如果字符串为空就可以
for(int len=1;len<=s.size();len++){
for(int i=0;i
——摘自 OI wiki
其中的nex只要看着上图就可以理解,26代表26个字母,100000代表字符串长度,nex[u][c]的值代表指向哪一个结点;
插入函数中,l表示字符串的长度,因为要把整个字符串做成一条树链,所以用for循环遍历整个字符串,一边遍历一边插入。如果之前有节点就不用增加节点,没有节点就++cnt来增加节点;
查找函数中,就一个字符串向下走,无结点就是没有,走到最后一个字符for循环结束了,那就看exist,exist为0就是没有
struct trie {
int nex[100000][26], cnt;
bool exist[100000]; // 该结点结尾的字符串是否存在
void insert(char *s, int l) { // 插入字符串
int p = 0;
for (int i = 0; i < l; i++) {
int c = s[i] - 'a';
if (!nex[p][c]) nex[p][c] = ++cnt; // 如果没有,就添加结点
p = nex[p][c];
}
exist[p] = 1;
}
bool find(char *s, int l) { // 查找字符串
int p = 0;
for (int i = 0; i < l; i++) {
int c = s[i] - 'a';
if (!nex[p][c]) return 0;
p = nex[p][c];
}
return exist[p];
}
};
注意:数组的大小,int p的位置,exist的作用
#include
using namespace std;
int nxt[500004][26];
int exist[500004];
int main()
{
ios::sync_with_stdio(false);
int n;cin>>n;
string tmp;int cnt=0;
for(int i=0;i>tmp;
for(int j=0;tmp[j];j++){
int c=tmp[j]-'a';
if(!nxt[p][c])nxt[p][c]=++cnt;
p=nxt[p][c];
}
exist[p]=1;
}
int m;cin>>m;
while(m--){
string q;cin>>q;int p=0;
bool flag=0;
for(int i=0;q[i];i++){
int c=q[i]-'a';
if(!nxt[p][c]){flag=1;break;}
p=nxt[p][c];
}
if(flag||exist[p]==0) cout<<"WRONG\n";
else if(exist[p]==1){exist[p]++;cout<<"OK\n";}
else cout<<"REPEAT\n";
}
return 0;
}
首先先把字典里的字符串造了一个字典树
简而言之就是把上一个标答里的set换成了trie而已
注意这是倒过来遍历的,因为字典树是前缀建树,所以 只有substring的第一个字符不断变化,字典树才可以单纯地用exist来判断这个子串在不在
class Solution {
public:
int nxt[20004][26];bool exist[20004]={0};
bool wordBreak(string s, vector& D) {
int cnt=0;
for(int i=0;idp(s.size()+1,0);//如果字符串为空就可以
int n=s.size();dp[n]=1;
for(int i = n-1;i >= 0;i--) {//i是倒过来的字符串
int p=0;
for(int j=i;j