LeetCode Top100 Liked 题单(序号1~17)

01Two Sum - LeetCode

我自己写的代码【193ms】

因为不知道怎么加cmp函数,就只能pair的first设为值了,但其实这也是瞎做,应该也是O(n²)吧

class Solution {
public:
    vector twoSum(vector& nums, int target) {
        pairpa[10004];int n=0;
        for(auto i:nums){
            pa[n].first=i;pa[n].second=n;n=n+1;
        }
        sort(pa,pa+n);
        int j=0,i=1;
        while(jtarget){
                j++;i=j+1;
            }
            else return {pa[i].second,pa[j].second};
        }  
        return {-1,-1};
    }
};

O(n²)代码【319ms】

class Solution {
public:
    
    vector twoSum(vector& nums, int target) {
        int n = nums.size();
        
        for(int i = 0; i < n; i++)
            for(int j = i + 1; j < n; j++)
                if(nums[i] + nums[j] == target) return {i,j};

        return {-1,-1};
    }
};

O(nlogn)代码【16ms】

我想到了排序,也想到了应该双指针,但没想到应该左指针在最左,右指针在最优

如果总和大于target,大的变小,如果总和小于target,小的变大。

class Solution {
public:
    
    vector twoSum(vector& nums, int target) {
        pairpa[10004];int n=0;
        for(auto i:nums){
            pa[n].first=i;pa[n].second=n;n=n+1;
        }
        sort(pa,pa+n);
        int j=0, i=n-1;
        while(jtarget) i--;
            else j++;
        }
        return {-1,-1};
    }
};

O(n)代码【4ms】

用了Hash。原理是加入一个值,如果map里面有对应的值就输出各自的序号,没有就保存序号。蓝桥杯有一道题好像就是这样做的

class Solution {
public:
    
    vector twoSum(vector& nums, int target) {
        int n = nums.size();
        unordered_map mp;
        for(int i=0;i

02Add Two Numbers - LeetCode

一开始看错了,以为最右边是个位,看了一些答案才发现是最左边是个位

首先先创建一个头节点,之后有一个移动指针p->next=new ListNode(sum%10);

一直到链表遍历完,进位(carry)结束

最后返回链表head->next

我的代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* head=new ListNode();
        ListNode* p=head;
        int carry=0;
        while(l1!=NULL||l2!=NULL||carry){
            int sum=0;
            if(l1!=NULL){
                sum+=l1->val;
                l1=l1->next;
            }
            if(l2!=NULL){
                sum+=l2->val;
                l2=l2->next;
            }
            sum+=carry;
            carry=sum/10;
            p->next=new ListNode(sum%10);
            p=p->next;      //p指向下一个结点
        }
        return head->next;
    }
};

递归做法

class Solution {
public:
    ListNode *helper(ListNode *l1,ListNode *l2,int carry){
        if(!l1 && !l2){
            if(carry) return new ListNode(carry);
            else return NULL;
        }
        int val=(l1?l1->val:0)+(l2?l2->val:0)+carry;
        ListNode *res=new ListNode(val%10);
        res->next=helper((l1?l1->next:NULL),(l2?l2->next:NULL),val/10);
        return res;
    }
    ListNode *addTwoNumbers(ListNode *l1,ListNode *l2){
        return helper(l1,l2,0);
    }
};

03 Longest Substring Without Repeating Characters

有思路但不会写

我的思路

时间复杂度为n,就是直接扫一遍,每次扫到一个字符

        如果vis为0,vis[i]设为1

        如果扫到的vis已经为1,从头pop_front(),直到vis[i]为0,比较ans和max的大小,选择保留

之后,将扫到的字符加入ans中

困难

我发现string容器没有pop_front,而且每一次比较ans和max的大小,选择保留时都要重新遍历一遍,时间复杂度其实挺大的

答案思路

答案也用了vis,不过直接先把string存起来,记录pair下表

时隔几个星期后

之前的思路已经忘光光了,重新整理了思路,用vis记录string的下标,st的子串的左端点,string从1开始计数

若vis[s[i]]为真,那么将st到vis[s[i]]之间的字符的vis清零,st更新为vis[s[i]]+1,将vis[s[i]]更新为i;若vis[s[i]]为假,代表没遇到过这个字符,直接记录位置;

最后字符串循环一次后,记得再更新一次max的大小!!

另外初始化的时候,直接空字符串输出0!!

代码【11ms】

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if(s=="")return 0;
        if(s=="")return 0;
        int vis[300]={0};pairmaxx;int st=1;maxx.first=1; maxx.second=1;
        s="!"+s;
        vis[s[st]]=st;
        for(int i=2;i(maxx.second-maxx.first)){
                    maxx.first=st; maxx.second=i;
                }
                while(st<=vis[s[i]]){vis[s[st]]=0;st++;}//将st更新到重复出现的位置的后面
            }
            vis[s[i]]=i;//如果没有,记录位置;有就更新位置
        }
        if((s.size()-st)>(maxx.second-maxx.first)){
            maxx.first=st; maxx.second=s.size();
       }
        return maxx.second-maxx.first;
    }  
};

4. Median of Two Sorted Arrays

两个已排好序的数组 找中位数 数组从0开始计数

算法课上学过O(log(mn))的算法,但是忘记了,只会O(m+n)的合并后直接中位数了

代码【43ms Beats 62.01%】O(m+n)

class Solution {
public:
    double findMedianSortedArrays(vector& nums1, vector& nums2) {
        vectoruse;
        int len1=nums1.size(),len2=nums2.size();
        int i=0,j=0;
        for(;i

看了解析后:

简述:用二分查找的思想做这道题。如果要更小的,把最大的那部分给删了;如果要更大的,把最小的那部分给删了

设A数组和B数组的中位数为AM和AM,其位置是AI和BI,为了找位置为K的数:

如果AI+BI小于K,说明AI和BI的位置小了,要大一点

       若AV小于BV,那么把【Astart,AI】的部分删掉,确保不会删掉第K位数;反之亦然。

如果AI+BI大于等于K,说明 Astart到AI 和 Bstart到BI (共AI+BI+2个数)中有位置为K的数;

        若AV大于BV,那么把【AI,Aend】的部分删掉,确保不会删掉第K位数;反之亦然。

最后A或者B的Start>End,结束;若BStart>BEnd,最后返回A【k-BStart】,反之亦然

LeetCode Top100 Liked 题单(序号1~17)_第1张图片

代码【35ms Beats 86.96%】 O(log(m*n))

注意数组的范围是【0, n.size()-1】!!!

class Solution {
public:
    double findMedianSortedArrays(vector& n, vector& m) {
        int num=int(n.size())+int(m.size());
        if(num%2)
            return q(n,m,num/2,0,n.size()-1,0,m.size()-1);
        return (q(n,m,num/2,0,n.size()-1,0,m.size()-1)+q(n,m,num/2-1,0,n.size()-1,0,m.size()-1))/2.0;
    }
     double q(vector& a,vector& b,int k,int Astart,int Aend,int Bstart,int Bend){
          if(Astart>Aend)return b[k-Astart];
          if(Bstart>Bend)return a[k-Bstart];
          int AIndex=(Astart+Aend)/2,BIndex=(Bstart+Bend)/2;
          int aV=a[AIndex],bV=b[BIndex];
          if(AIndex+BIndexbV)
                    return q(a,b,k,Astart,Aend,BIndex+1,Bend);
               else 
                    return q(a,b,k,AIndex+1,Aend,Bstart,Bend);
          }
          else{//删除大的部分
               if(aV>bV)
                    return q(a,b,k,Astart,AIndex-1,Bstart,Bend);
               else 
                    return q(a,b,k,Astart,Aend,Bstart,BIndex-1);
          }
     return -1;
     }
};

还有一个做法是O(log⁡(min⁡(m,n))),但是我没有看懂,在实际运行的时候,是45ms,就很怪

!没看懂的代码

class Solution {
public:
    double findMedianSortedArrays(vector& nums1, vector& nums2) {
        if (nums1.size() > nums2.size()) {
            return findMedianSortedArrays(nums2, nums1);
        }
        
        int m = nums1.size(), n = nums2.size();
        int left = 0, right = m;
        
        while (left <= right) {
            int partitionA = (left + right) / 2;
            int partitionB = (m + n + 1) / 2 - partitionA;
            
            int maxLeftA = (partitionA == 0) ? INT_MIN : nums1[partitionA - 1];
            int minRightA = (partitionA == m) ? INT_MAX : nums1[partitionA];
            int maxLeftB = (partitionB == 0) ? INT_MIN : nums2[partitionB - 1];
            int minRightB = (partitionB == n) ? INT_MAX : nums2[partitionB];
            
            if (maxLeftA <= minRightB && maxLeftB <= minRightA) {
                if ((m + n) % 2 == 0) {
                    return (max(maxLeftA, maxLeftB) + min(minRightA, minRightB)) / 2.0;
                } else {
                    return max(maxLeftA, maxLeftB);
                }
            } else if (maxLeftA > minRightB) {
                right = partitionA - 1;
            } else {
                left = partitionA + 1;
            }
        }
        
        return 0.0;
    }
};

5. Longest Palindromic Substring

题意:给一个字符串,找回文串

我知道这是马拉车问题,但是我不记得马拉车的代码怎么写了;好像哈希也可以做这种题目,但是我能想到的哈希也是O(n^2)

我的思路

首先设回文串是单数,遍历一遍字符串,接着设回文串是双数,遍历一遍字符串,时间复杂度是O(n^2)

class Solution {
public:
    string longestPalindrome(string s) {
        string st="";
        for(int i=0;i

查解析的时候看到又说区间动态规划做这道题的,动态规划已经完全不会了qwq

不过动态规划和我这个方法的时间复杂度居然差不多 哈哈

我看懂了!

首先每个字母是一个回文串,所以所有dp[0][i]=True,之后一个个排,差不多就是这意思,看代码肯定能看懂

代码【180ms】区间dp O(n^2)

class Solution {
public:
    string longestPalindrome(string s) {
        int n=s.size();
        string ans="";ans+=s[0];
        bool dp[1005][1005]={0};//字母最后一个的序号 里回文串长度(不包含最后一个字母的长度)
        for(int i=0;i<1005;i++)dp[i][0]=1;
        for(int i=1;i=0;j--){
                if(s[i]==s[j]){
                    if(i-j==1||dp[i-1][i-j-2]){
                        dp[i][i-j]=1;
                        if((int)ans.size()

然后看到同样是O(n^2)的双指针法,但是不理解为什么能快那么多

同样是看了代码就知道怎么做的了

代码 【9ms Beats 95.99%】双指针法 

直接黏贴了别人的代码

class Solution {
public:
    string longestPalindrome(string s) {
        int lenLP=1;
        int startLP=0;
        int n=s.length();
        for(int i=0;i=0 && rightlenLP){
                lenLP=len;
                startLP=left+1;
            }
        }
        return s.substr(startLP,lenLP);
    }
};

Manacher算法

算法思想

首先在首尾和每个数字之间添加”#“,例如”aofs“改成”“#a#o#f#s#”,这样解决了奇偶不同的问题,即字符串永远都是奇数,同时第0位,随便加上一个符号

p表示包括当前字符的 加入#后的 回文串半径,例如

# a # a # c # a # b # d # k # a # c # a # a #

1 2 3 2 1 4 1 2 1 2 1 2 1 2 1 2 1 4 1 2 3 2 1

pos和maxlen表示答案的中心和长度

id和mx表示最右边的回文串的中心和端点,注意:mx不包含在回文串中

遍历字符串 t 时,如果此时在最右回文串的外面(或者i=1时,mx=0),即 i>mx,那么p[i]=1

若 i 在最右边的已知回文串区域的右端点里面,说明这个回文串左边镜像字符 2*id-i 已经求过p[i]了
如果 p[2*id-i]+i 大于mx,即p[2*id-i]大于mx-i,就暂定p[i]=mx-i,因为超出回文串了
如果p[2*id-i]+i 小于mx,即p[2*id-i]小于mx-i,因为还在回文串里面,所以p[i]=p[2*id-i]

在 t[i+p[i]] == t[i-p[i]] 的基础上,向外扩展、更新p[i]

如果出现了新的最右字符串,那么更新id和mx

代码【10ms Beats 95.49%】Manacher

居然和上面的双指针时间差不多 哈哈

class Solution {
public:
string longestPalindrome(string s) {
        string t = "$#";
        for(auto i : s){
            t += i;
            t += "#";
        }
        vectorp(t.size(),1);//包括当前字符的 加入#后的 回文串半径
        int pos = 0;
        int maxlen = 0;
        int id = 0;
        int mx = 0;
        for(int i=1;imx){//mx是最右边的已知回文串区域的右端点,不在回文串里面的第一个数字
                mx = i+p[i];
                id = i;//最右边的已知回文串中心
            }
            //记录最终的答案
            if(p[i]>maxlen){
                maxlen = p[i];
                pos = i;
            }
        }
        //(pos - maxlen)/2 长度
        return s.substr( (pos - maxlen)/2,maxlen-1 );
    }

};

11. Container With Most Water

题意:求min(h[l] , h[r])*(r - l)最大

我的思路

双循环,O(n^2),但n=1e5 会超时,不会做不会做

双指针解析

参考:6.container-with-most-water(装最多水的容器

以序列最外面两条边形成的乘积为起始面积,找出两条边中较小的一条,索引加一(i++),找出一条更大的边来代替较小的边,以使得整个容器最大(可以看参考链接的图,更易于理解)

代码 自己写的 111ms Beats 35.18%

class Solution {
public:
    int maxArea(vector& h) {
        int l=0,r=h.size()-1;
        int maxx=0;
        while(lh[r])r--;
            else l++;
        }
        return maxx;
    }
};

 上述代码中还有可以优化的地方,因为当 l 或 r 更新时,一定要找到更大的h[ l ]或者h[ r ],否则对答案无影响

代码修改后 92ms Beats 92.74%

class Solution {
public:
    int maxArea(vector& h) {
        int l=0,r=h.size()-1;
        int maxx=0,minh=9999999;
        while(l

为什么可以连续的h[r]<=minh和h[l]<=minh?例如【8,88,8】答案为16,就算右边动了,变成了【8,16】,最小的高还是8,底宽还变小了,所以在底宽变小的前提下,一定要将minh提高才可以

13. Roman to Integer

题意:给一串字符串,写出代表的罗马数字

我的思路

普通的模拟

改成if…else……可以从7ms进化成3ms

代码 3ms Beats 97.3% 模拟

class Solution {
public:
    int romanToInt(string s) {
        int ans=0;
        for(int i=0;i

14. Longest Common Prefix

题意:给出前缀组Str,找到最长的字符串前缀

我的思路

用map把所有Str中的string的所有前缀都收集了,在收集过程中记录下最长的前缀

代码 232ms Beats 5.31%

class Solution {
public:
    string longestCommonPrefix(vector& strs) {
        map  mp;
        int mx=0;string a="";string tmp="";
        for(int i=0;imx){
                    mx=(int)tmp.size();a=tmp;
                }
            }
        }
        return a;
    }
};

标答思路

看公共前缀就把字符串组排序,看最后和最前的公共字符前缀即可

代码  3ms Beats 89.36% 逻辑构造题

class Solution {
public:
    string longestCommonPrefix(vector& strs) {
       string tmp="";
       sort(strs.begin(),strs.end());
       int first=0,last=(int)strs.size()-1;
       for(int i=0;i

15. 3Sum

题意:给定一串数组,输出三个数,使得三个数的和为0

我的思路

O(n^2)组成pair组,存入pair的和,线性组,以后一一对照 总之全部用map实现

最后居然过了没想到的哈哈哈哈哈

代码 2999ms Beats 5.00%

class Solution {
public:
    vector> threeSum(vector& nums) {
        vector > pr[200005];int n=nums.size();map a;
        set< vector> ans;vector> aa;
        for(int i=0;i200000)continue;
                pr[as+100000].push_back({i.first,j.first});
            }
        }//把所有的和都放好了
        for(auto i:a){
            int id=100000-i.first;
            if(!pr[id].empty()){//如果有对应的
                for(int j=0;j1
                    ||i.first!=pr[id][j].first&&i.first==pr[id][j].second&&i.second>1
                    ||i.first==pr[id][j].first&&i.first==pr[id][j].second&&i.second>2
                    ){//如果三个数的序号不一样
                        vector a;a.push_back(i.first);
                        a.push_back(pr[id][j].first);a.push_back(pr[id][j].second);
                        sort(a.begin(),a.end());
                        ans.insert(a);
                    }
                }
            }
        }
        for (auto q:ans){aa.push_back(q);}
        return aa;
    }
};

标答 双指针

首先将nums按大小排序,这样方便之后查找目标答案

顺序遍历nums,target就是-nums[i],设left是i+1,right是nums.size()-1,sum=nums[left] + nums[right]

如果sumtarget,说明sum太大了,left向左移来减小sum;

如果sum=target,那么刚好{nums[i], nums[left], nums[right]}加入vector中【还是排好序的】,因为适配的target的组合不止一个,所以移动left和right来跳过相同的数,之后接着查找,直到left>right停止

代码 113ms Beats 97.35% 双指针+排序

class Solution {
public:
    vector> threeSum(vector& nums) {
    vector> result;
    int n = nums.size();// Sort the array to use two-pointer approach
    sort(nums.begin(), nums.end());
    for (int i=0; i0 && nums[i] == nums[i - 1]) {
            continue; // Skip duplicates for the first element
        }
        int target = -nums[i];
        int left = i + 1;
        int right = n - 1;
        while (left < right) {
            int sum = nums[left] + nums[right];
            if (sum == target) {
                result.push_back({nums[i], nums[left], nums[right]});
                while (left < right && nums[left] == nums[left + 1]) 
                    left++; // Skip duplicates for the second element
                left++;
                while (left < right && nums[right] == nums[right - 1])
                    right--;
                right--;    // Skip duplicates for the third element
            }
            else if (sum < target) left++;
            else right--;
        }
    }
    return result;
}
};

17. Letter Combinations of a Phone Number

我的思路

直接死算(?),用map一个个放,最后输出符合个数的

代码 5ms Beats 9.32%

class Solution {
public:
    vector letterCombinations(string digits) {
        vector result; 
        vector  mp;
        vector as={"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
        int start=0,end=0;
        for(int i=0;i

标答 

普通的循环:用两个vector交替,另外上面代码的-2好像不太行

代码 0ms Beats 100% 循环

class Solution {
public:
    vector letterCombinations(string digits) {
        if(digits.empty()) return{};
        vector as={"", "", "abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
        vector output={""};
        for (auto st:digits){//输入
            vector temp;
            for(auto c: as[(st-'0')] ){//字母
                for(auto t:output){
                    temp.push_back(t+c);
                }
            }
            output.clear();
            output=temp;
        }
        return output;
    }
};

代码 0ms 6.27mb Beats 97.65% 递归

在主函数中,首先如果digit的长度为0,那就返回空;规定好mapping后进入递归函数

在递归函数中,跳出的条件是index大于digit的长度,ans把一次的outpu加入,退出递归;在递归中,首先是循环(表示这个按键可以是什么字母)进入递归(index+1),出递归后把这个字母弹出,进入下次循环装上下个可能的字母

class Solution {
public:
    void solve(string &digits,vector &ans,string &output,int index,vector &mapping)
    {
        if(index>=(int)digits.size()){
            ans.push_back(output);
            return;
        }
        int id=digits[index]-'0';
        for(int i=0;i<(int)mapping[id].size();i++){
            output.push_back(mapping[id][i]);
            solve(digits,ans,output,index+1,mapping);
            output.pop_back();
        }
    }

    vector letterCombinations(string digits) {
        if(!(int)digits.size())return {};
        int index=0;vector ans;
        string output="";
        vector mapping={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
        solve(digits,ans,output,index,mapping);
        return ans;
    }

};

你可能感兴趣的:(leetcode,算法,c++)