攻陷LeetCode算法区(C++) -------- LeetCode 1~5

目录

LeetCode 1. 两数之和

算法 1:暴力枚举

算法 2:排序+双指针

算法 3:哈希表

LeetCode 2. 两数相加

算法  模拟(类似高精度运算)

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

算法:典型双指针算法

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

算法1:排序

算法 2:递归

LeetCode 5. 最长回文子串

算法:暴力搜索


LeetCode 1. 两数之和

算法 1:暴力枚举

很水的暴力做法,时间复杂度O(n^2),直接上代码吧(ーー゛)

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        for(int i = 0; i < nums.size() - 1; i ++)   // 直接二重循环不解释
        {
            for(int j = i + 1; j < nums.size(); j ++)
                if(nums[i] + nums[j] == target)
                    return {i, j};
        }
        return {};
    }
};

算法 2:排序+双指针

推荐做法* 时间复杂度O(nlogn),上代码♪(^∇^*),具体步骤和思路见注释

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> cpy;
        for(int i = 0; i < nums.size(); i ++)   //作一备份,便于sort打乱顺序后的查找
            cpy.push_back(nums[i]);
        sort(nums.begin(), nums.end());     // 排序
        int a1, a2;     //代表答案的数字(不是下标)
        bool flag = 0;
        for(int i = 0, j = nums.size() - 1; i < j; )    // 定义头指针i,尾指针j
        {
            if(nums[i] + nums[j] == target)
            {
                a1 = nums[i], a2 = nums[j];
                flag = 1;
                break;
            } 
            if(nums[i] + nums[j] > target) j --;
            else if(nums[i] + nums[j] < target) i ++;
        }
        if(!flag) return {};
        int r1, r2;     //代表答案(下标)
        for(int i = 0; i < cpy.size(); i ++)    // 找到对应的下标
        {
            if(cpy[i] == a1)
            {
                r1 = i;
                break;
            }
        }
        for(int i = cpy.size() - 1; i >= 0; i --)
        {
            if(cpy[i] == a2)
            {
                r2 = i;
                break;
            }
        }
        return {r1, r2};
    }
};

算法 3:哈希表

推荐做法* 时间复杂度O(n)

这里用到了哈希表unordered_map<T t, U u>,上代码︿( ̄︶ ̄)︿

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> heap;   //建立一个哈希表
        for(int i = 0; i < nums.size(); i ++)
        {
            int r = target - nums[i];   // 如果两个数中的一个数是nums[i],另一个就是target - nums[i],设为r
            if(heap.count(r))   // 哈希表里查询到了r
                return {heap[r], i};    // 返回答案
            heap[nums[i]] = i;   // 没找到就把当前元素放进哈希表
        }
        return {};
    }
};

第一题完~~


LeetCode 2. 两数相加

算法  模拟(类似高精度运算)

模拟竖式即可,时间复杂度O(n),上代码 (ーー゛)

/**
 * 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(-1);     // 做有关链表的题目,有个常用技巧:添加一个虚拟头结点:ListNode *head = new ListNode(-1);,可以简化边界情况的判断
        ListNode *cur = head;
        int t = 0;
        while(l1 || l2 || t)
        {
            if(l1) t += l1->val, l1 = l1->next;
            if(l2) t += l2->val, l2 = l2->next;
            cur->next = new ListNode(t % 10);   // 将t的个位数插入新的虚拟链表(通过构造函数初始化)
            cur = cur->next;
            t /= 10;
        }
        return head->next;  //跳过虚拟的头结点
    }
};

第二题完~~



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

算法:典型双指针算法

可以用一个哈希表来维护字段,时间复杂度O(n),上代码~

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int res = 0;    // 最大值(即答案)
        unordered_map<char, int> heap;  // 定义哈希表维护两指针(如下)之间的字符串
        for(int i = 0, j = 0; i < s.length(); i ++) // 双指针i, j分别为头指针,尾指针,每次仅移动头指针
        {
            heap[s[i]] ++;     // 哈希表中放入指针i所指的字符
            while(heap[s[i]] > 1) heap[s[j ++]] --; //指针j往后移动一格,哈希表中去掉指针j所指的字符
            res = max(res, i - j + 1);
        }
        return res;
    }
};

第三题完~~


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

这题有些难度,这里放两个做法(第一个是水的( ̄ε(# ̄))

算法1:排序

ummm纯暴力做法,直接合并排序,时间复杂度O((n + m)log(n + m))^{},上代码

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        vector<int> merged;
        int s1 = nums1.size(), s2 = nums2.size();
        int s = s1 + s2;
        for(int i = 0; i < s1; i ++)
            merged.push_back(nums1[i]);
        for(int i = 0; i < s2; i ++)
            merged.push_back(nums2[i]);
        sort(merged.begin(), merged.end());
        if(s % 2) return  merged[s / 2];
        else return 1.0 * (merged[s / 2] + merged[s / 2 - 1]) / 2; //这里注意数据类型的转换
    }
};

算法 2:递归

乍一看看不出递归算法可行o( ̄ヘ ̄o#),不妨先将原问题简化为:

 在两个有序数组中,找出第k小的数

 原问题即找出第(n + m)/2小的数(暂时不分奇偶讨论)
m,n \geq k / 2,从 nums1 和 nums2 中各取前 k/2 个元素:

如果 nums1[k/2-1] > nums2[k/2-1],则说明 nums1 中取的元素过多,nums2 中取的元素过少;因此nums2 中的前  k / 2 个元素一定都小于等于第 k 小的数,所以我们可以不考虑这些数,将问题简化为在剩下的数中找第 k-\left \lfloor k/2 \right \rfloor 小的数。


如果 nums1[k/2-1] \leqslant nums2[k/2-1],同理可说明 nums1 中的前 k/2 个元素一定都小于等于第 k 小数,

类似可将问题的规模减少一半。

再考虑边界情况,如果 m < k/2,则我们从 nums1 中取 m 个元素,从 nums2 中取 k / 2 个元素(由于 k = (n+m) / 2,因此 m, n 不可能同时小于 k / 2):

 如果 nums1[m-1] > nums2[k/2-1],则 nums2 中的前 k / 2 个元素一定都小于等于第 k 小的数,我们可以将问题归约成在剩下的数中找第k-\left \lfloor k/2 \right \rfloor 小数。


如果 nums1[m-1] \leqslant nums2[k/2-1],则 nums1 中的所有元素一定都小于等于第 k 小数,

因此第 k 小的数是 nums2[k-m-1]

对于每种情况,都能把原问题简化成几个子问题,当 k 为1或某数组为空时,可退出递归。符合递归的条件,代码如下:

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int s1 = nums1.size(), s2 = nums2.size();
        int s = s1 + s2;    // s为总长度
        if(s % 2 == 0)
        {
            int left = find(nums1, 0, nums2, 0, s / 2); // find即为递归函数
            int right = find(nums1, 0, nums2, 0, s / 2 + 1);
            return (left + right) / 2.0;
        }
        else 
            return find(nums1, 0, nums2, 0, s / 2 + 1);
    }
    int find(vector<int> &nums1, int i, vector<int> &nums2, int j, int k) //i, j表示两数组从第i, j位开始计算, k表示找第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]);  // k是1,则返回两数组的头一个元素(无需讨论第一个数组是否为空,上面已经讨论过了)
        int si = min((int)nums1.size(), i + k / 2), sj = j + k / 2; // 按照分类讨论的标准模拟
        if(nums1[si - 1] > nums2[sj - 1])
            return find(nums1, i, nums2, sj, k - k / 2);  //si位置上的元素大,则nums2从sj处开始计算,nums1不变,递归寻找第 (k - k / 2) 个元素
        else    
            return find(nums1, si, nums2, j, k - (si - i)); //同理,相反(写si - i 是为了避免讨论si 等于s1.size()的情况)
    }
};

此做法时间复杂度为O(log(n+m))

第四题完~~



LeetCode 5. 最长回文子串

算法:暴力搜索

水题,直接上代码 ︿( ̄︶ ̄)︿

class Solution {
public:
    string longestPalindrome(string s) {
        string res; 
        for(int i = 0; i < s.length(); i ++)
        {
            int l = i - 1, r = i + 1;   // 对应回文串长度为奇数情况
            while(l >= 0 && r < s.length() && s[l] == s[r]) l --, r ++;     // 根据回文串的特性,向两边扩展
            if(res.length() < r - l - 1) res = s.substr(l + 1, r - l - 1);  // 这里注意边界

            l = i, r = i + 1;   // 对应回文串长度为奇数情况
            while(l >= 0 && r < s.length() && s[l] == s[r]) l --, r ++;     // 同上
            if(res.length() < r - l - 1) res = s.substr(l + 1, r - l - 1);
        }
        return res;
    }
};

时间复杂度为O(n^2),但实际无法达到

第五题完~~


更多题解在主页哦~~(๑•̀ㅂ•́)و✧

如果觉得不错,可以⭐Star 和 Fork (u‿ฺu✿ฺ)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值