Leetcode Top 100 Liked Questions(序号105~139)

105. Construct Binary Tree from Preorder and Inorder Traversal105. Construct Binary Tree from Preorder and Inorder Traversal

题意:根据前序遍历和中序遍历来构造二叉树

我的思路

要用递归造树,要同时递归左子树和右子树,造树需要左边边界和右边边界

build函数有树的跟指针,前序的有左边边界和右边边界,中序的左边边界和右边边界

如果l>r return;因为是先序遍历,所以左子树是先序的第一个,先构造了;

不会做不会做

标答

上面标粗的部分是正确的,构造就在递归的时候构造,不用把树的根作为指针放到参数里面

之后就是搞清楚pl,pr和ir,il就可以了【做着做着居然做出来了!!】

例子:前序[3,0,9,4,62,5,7,8,1]

中序[4,9,6,0,2,3,5,8,7,1]

Leetcode Top 100 Liked Questions(序号105~139)_第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

代码 Runtime 30 ms Beats 34.48% Memory 25.9 MB Beats 73.83%

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);
    }
};

标答优化 map

题目中有这样一条preorder and inorder consist of unique values.

所以可以用map来减少中序查找的时间消耗

传递参数的时候可以省略前序序号的传递,或者说只需要一个pl就可以了,传递的时候函数参数为il和ir

代码 Runtime13 ms Beats 79.95% Memory26.3 MB Beats 58.43%

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);
    }
};

114. Flatten Binary Tree to Linked List

题意:Leetcode Top 100 Liked Questions(序号105~139)_第2张图片

我的思路 

先序索引,但我不会写;看了答案后至少有一点是对的,就是先递归,两个递归后在做事情

标答 递归

分为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;  这时候还需要处理原来在右子树上的结点。因为原先左子树上的都是处理好的链表,所以只要找到原先左子树上的最后一个节点,把这最后一个节点连上去就可以了

代码 递归 Runtime 5 ms Beats 59.24% Memory 12.7 MB Beats 30.44%

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来到左指正的最右边,左指针的最右边指向当前指针的右边,再把原来右指针指向左指针的位置,左指针置为空;如下图所示,总之就是一点一点挪过去 

Leetcode Top 100 Liked Questions(序号105~139)_第3张图片

代码 Runtime 3 ms Beats 88.18% Memory12.7 MB Beats 61.31%

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;
        }
    }
};

标答 优化

Leetcode Top 100 Liked Questions(序号105~139)_第4张图片

注意这里是先递归右边,相当于(我一开始想的)先到右边的尽头,之后那前一个来接上的想法【最后没写出来】

注意这里递归的顺序,学着点!!

先递归到6,这时6的右指针是prev也就是空,左指针是空,prev是6

接下来是5,5的右指针是prev也就是6,左指针是空,prev是5

接下来是4,4的右指针是prev也就是5,左指针是空,prev是4

接下来是3,3的右指针是prev也就是4,左指针是空,prev是3

……

于是就完成了

代码 Runtime 0 ms Beats 100% Memory12.7 MB Beats 30.44%

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;
    }
};

118. Pascal's Triangle

题意:Leetcode Top 100 Liked Questions(序号105~139)_第5张图片

 我的思路

用动态规划(?),第x层有x个数字,dp[x][0]=1,dp[x][x-1]=0,dp[x][i]=dp[x-1][i-1]+dp[x-1][i]

 代码 Runtime 4 ms Beats 29.4% Memory 6.9 MB Beats 18.4%

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

标答 优化

简而言之,就是用一维数组代替了二维数组,实现了相加提速

代码 Runtime 0 ms Beats 100% Memory 6.7 MB Beats 78.55%

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;
    }
};

121. Best Time to Buy and Sell Stock

题意:给出每天袜子的价格,只能选一天买袜子,只能选一天卖袜子,求利润最大

我的思路

用vector或者deque模拟单调栈?当要弹出一个大的数字的时候,就求一下栈尾和栈顶的的差,或者双指针也可以

代码 双指针 Runtime 66 ms Beats 99.76% Memory 93.4 MB Beats 11.73%

不加取消同步在66.7%左右,感觉都差不多,都是找个最小的,然后当前的减去现在最小的,最后在"当前的"里面找最大的

class Solution {
public:
    int maxProfit(vector& p) {
        int l=0,r=0,n=p.size();int profit=0;
        for(r=1;r

124. Binary Tree Maximum Path Sum

题意:树上路径和最大

我的思路

学过,好像有两种做法?一种dp一种dfs大概?

下面是用dfs做的:

那就每个结点都遍历一遍,每个节点的数字就是 sum=左边的和加上右边的和,ans=max(ans, sum);返回的时候返回两条子树和最大的那个,如果和是负的那就返回0

代码 Runtime 17 ms Beats 85.76% Memory27.7 MB Beats 52.24%

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;
    }
};

128. Longest Consecutive Sequence

题意:输出最长连续序列的长度

我的思路

初步设想快排+遍历,但是他会有相同的数字;那就用map

代码 Runtime 147 ms Beats 60.73% Memory 68.8 MB Beats 26.14%

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,好吧;其实和初步设想差不多,就是相同的数字的时候不加就可以了

首先先排序,之后

代码 Runtime 35 ms Beats 99.79% Memory43.2 MB Beats 99.65%

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

131. Palindrome Partitioning

题意:给一个字符串,返回所有是回文串的子串

我的思路

初步设想O(n^2)的遍历+回文串判断,但是这个返回的答案很dfs

那就试试用dfs做吧;不会做不会做

但是瞄了一眼标答 还是自己写了

代码 Runtime 75 ms Beats 94.39% Memory 49.3 MB Beats 82.17%

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;
    }
};

136. Single Number

题意:给一个数组,找出只出现一次的数字

我的思路

用map存一遍,在找一遍

代码 Runtime17 ms Beats 59.59% Memory20.1 MB Beats 9.40%

class Solution {
public:
    int singleNumber(vector& nums) {
        ios::sync_with_stdio(false);cin.tie(0);
        unordered_mapmp;
        for(int i=0;i

标答 位运算 

首先是异或算法的一些规律

Leetcode Top 100 Liked Questions(序号105~139)_第6张图片

从上图就可以看到,如果是两个一样的数字,那么它就会变成0;如果是不一样的数字,那么它就会留下来

代码 Runtime11 ms Beats 92.56% Memory 17 MB Beats 33.22%

class Solution {
public:
    int singleNumber(vector& nums) {
      int x = 0;
      for(int i=0; i

138. Copy List with Random Pointer

题意:复制一个一摸一样的链表

我的思路

只会链表的顺序复制,如果是随机的,那要有个map,左边是原地址,右边是现地址;

代码 Runtime 3 ms Beats 97.9% Memory11.3 MB Beats 32.3%

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;
    }
};

标答 不用map的做法

如果head是空,返回空;

copy_merge函数复制顺序的链表,其中curr是原链表的当前节点,next是原链表的下一个节点,copy是专门创造新结点的指针,curr指向新生的结点,copy指向原来下一个节点;其返回结果如图所示

Leetcode Top 100 Liked Questions(序号105~139)_第7张图片

handle_random函数如下如所示,简单来说就是复制的结点就是原节点的next,所以容易能找到;这样就可以原来的节点的random指向哪个复制的节点就指向那个节点的next

Leetcode Top 100 Liked Questions(序号105~139)_第8张图片

最后detach函数把复制的结点全部都串在一起,原来的结点也要指回原来的结点

代码 Runtime 7 ms Beats 73.26% Memory 11.2 MB Beats 87.73%

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);
    }
};

139. Word Break

题意:给你字典,问能不能用字典里的词拼接成字符串s

我的思路

用dfs吗?但是dfs先分成所有可能性,再用字典判断也太费时间了吧

不会做,看看答案

标答 set+动态规划

因为只问了能不能组成,所以只要判断bool就可以了;

首先把worddict中的词都放到set里面,之后开辟一个bool类型的dp;

假设i是字符串总长度,j是子字符串的开头,dp[j]=1表示再j之前的字符串可以分割;

之后在set里面找是否能找到以j开头[j,i)的字符串,可以的话dp[i]=1;

代码 Runtime 13 ms Beats 50.43% Memory 14.5 MB Beats 29.54%

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

标答 字典树+动态规划

Leetcode Top 100 Liked Questions(序号105~139)_第9张图片

——摘自 OI wiki

Leetcode Top 100 Liked Questions(序号105~139)_第10张图片

前置知识 Trie的模板

其中的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;
}

本题代码及解析 Runtime3 ms Beats 88.36% Memory9.7 MB Beats 60.43%

首先先把字典里的字符串造了一个字典树

简而言之就是把上一个标答里的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

你可能感兴趣的:(算法,c++,数据结构,字典树,动态规划)