LeetCode究极班系列(1-5)

1. 两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

暴力枚举

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

分析: 枚举每个元素 往后找是否有元素和当前元素组合达到要求的

哈希表

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> map; //存储值和下标
        for(int i=0;i<nums.size();i++)
        {
            int x=target-nums[i];
            if(map.count(x)) return {map[x],i};
            map[nums[i]]=i;
        }
        return {};
    }
};

分析 依旧是枚举每个元素 但是这次我们将每个元素及其下标存储到哈希表中 那么我们找和当前元素匹配的元素的时间 就优化到了O(1)不需要枚举剩余元素了

双指针

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int n=nums.size();
        vector<pair<int,int>> map;
        for(int i=0;i<n;i++)
        {
            map.push_back({nums[i],i});
        }
        sort(map.begin(),map.end());
        int i=0,j=n-1;
        while(i<j)
        {
            if(map[i].first+map[j].first==target) 
                return {map[i].second,map[j].second};
            else if(map[i].first+map[j].first<target)
                i++;
            else j--;
        }
        return {};
    }
};

分析 使用双指针可以使得一次枚举所有元素 就能得到结果 但是需要一定的单调性和记录元素和原始下标之间的映射关系

2. 两数相加

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

模拟

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        int t=0;
        ListNode* res=new ListNode();
        res->next=NULL;
        ListNode* tail=res;
        while(l1 || l2 || t)
        {
                ListNode* temp=new ListNode(t);
                if(l1!=nullptr) temp->val+=l1->val,l1=l1->next;
                if(l2!=nullptr) temp->val+=l2->val,l2=l2->next;
                t=temp->val/10;
                
                temp->val=temp->val%10;
                temp->next=tail->next;
                tail->next=temp;
                tail=temp;
        }
        return res->next;
    }
};

分析 直接模拟一下小学列竖式的操作 然后因为输出格式 所以我们采用链表的尾插法去插入新的节点

3. 无重复字符的最长子串

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。

双指针

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int n=s.size();
        unordered_map<char,int> num;
        int res=0;
        for(int i=0,j=0;i<n;i++)
        {
            num[s[i]]++;
            while(j<=i && num[s[i]]>1) num[s[j++]]--;
            res=max(res,i-j+1);
        }
        return res;
    }
};

分析:我们可以把无重复字符的子串按照 最后一个字符分成n类 求这n类子串的最大值 就是结果
对于每一类的子串 结尾位置已经确定了 我们要找到的就是最小的起始位置 因为起始位置越大 长度也就越大 然后这个起始位置的是存在单调性的 即随着结尾位置往后走 起始位置也会往后走不会像前的 这里的原因可以用反证法 如果随走结尾位置往后走 起始位置往前走的话 前一个结尾位置的最大无重复子串的长度应该更大 所以起始位置存在单调性
用哈希表来存储起始位置j到结尾位置i的中间字符出现的次数 每次结尾字符,枚举所有的结尾字符 并且将这个字符加入哈希表 因为只有这个元素加入哈希表 所以也只有他有可能破坏无重复性 那么起始位置j就应该不断往后走 使得结尾字符唯一 那么最后的i-j+1就是以当前字符结尾的无重复字符子串的最大长度

4. 寻找两个正序数组的中位数

给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。
请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。

nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0

归并排序(O(m+n))

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int length1 = nums1.size();
        int length2 = nums2.size();
        int i,j,k;
        vector<int> temp(length1+length2, 0);
        for(i=0,j=0,k=0;i<length1&&j<length2;k++)
        {
            if(nums1[i]<nums2[j])
            {
                temp[k] = nums1[i];
                i++;
            }
            else
            {
                temp[k] = nums2[j];
                j++;
            }
        }
        while(i<length1)        
            temp[k++]=nums1[i++];
        while(j<length2)
            temp[k++]=nums2[j++]; 
        if((length1+length2)%2 ==1)      
            return temp[(length1+length2)/2];    
        else
            return (temp[(length1+length2)/2]+temp[(length1+length2)/2-1])/2.0;
    }
};

分析 就是简单的归并两个区间 遍历了所有的元素 复杂度是 O(m+n)没有达到要求

找第k个数

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int tt=nums1.size()+nums2.size();
        if(tt%2==0)
        {
            int left=find(nums1,0,nums2,0,tt/2);
            int right=find(nums1,0,nums2,0,tt/2+1);
            return (left+right)/2.0;
        }else return find(nums1,0,nums2,0,tt/2+1);
    }
    int find(vector<int>& nums1,int i,vector<int>& nums2,int j,int k){
        if(nums1.size()-i>nums2.size()-j) return find(nums2,j,nums1,i,k);
        if(nums1.size()==i) return nums2[j+k-1];
        if(k==1) return min(nums1[i],nums2[j]);
        int si=min(i+k/2,int(nums1.size())),sj=j+k/2;
        if(nums1[si-1]>nums2[sj-1]) return find(nums1,i,nums2,sj,k-k/2);
        else return find(nums1,si,nums2,j,k-(si-i));
    }
};

分析 非常巧妙的做法 只要我们完成找第k个数的算法 当k=(n+m)/2的时候 就是中位数了 首先我们保证第一个区间的数肯定比第二个区间的数少 我们分别找两个区间的第k/2个数 比较这两个数
三种情况
第一个k/2大的时候 区间2的前k/2个数显然不是中位数 删除 (k-)
第一个k/2小的时候 区间1的前k/2个数显然不是中位数 删除(k-)
当两个一样大的时候 就是中位数了 直接输出
边界情况
第一个区间遍历完了 区间2的中位数就输出吧
k是1的时候 比较两个区间的最小元素 输出

5. 最长回文子串

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。

暴力枚举

class Solution {
public:
    string longestPalindrome(string s) {
        string res;
        for(int i=0;i<s.size();i++){
            int l=i-1,r=i+1;
            while(l>=0 && r<s.size() && s[l]==s[r]) l--,r++;
            if(res.size()<r-l-1) res=s.substr(l+1,r-l-1);

            l=i,r=i+1;
            while(l>=0 && r<s.size() && s[l]==s[r]) l--,r++;
            if(res.size()<r-l-1) res=s.substr(l+1,r-l-1);
        }
        return res;
    }
};

分析 回文子串只有两种形式 奇数的和偶数的 我们枚举每个元素 计算他奇偶回文串的长度 更新结果即可

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值