LeetCode-Day1

LeetCode50题(17天)-Day1

2 两数之和

题号:002
难度:中等
https://leetcode-cn.com/problems/add-two-numbers/
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1: c 输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 输出:7 -> 0 -> 8 原因:342 + 465 = 807
示例 2: c 输入:(3 -> 7) + (9 -> 2) 输出:2 -> 0 -> 1 原因:73 + 29 = 102

思路:
模仿我们小学时代学的加法运算。个位相加超过十进一,十位相加有进位则加上进位,依次类推。
C++实现:

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode *head = nullptr, *tail = nullptr;
        int carry = 0;
        while (l1 || l2) {
            int n1 = l1 ? l1->val: 0;
            int n2 = l2 ? l2->val: 0;
            int sum = n1 + n2 + carry;
            if (!head) {
                head = tail = new ListNode(sum % 10);
            } else {
                tail->next = new ListNode(sum % 10);
                tail = tail->next;
            }
            carry = sum / 10;
            if (l1) {
                l1 = l1->next;
            }
            if (l2) {
                l2 = l2->next;
            }
        }
        if (carry > 0) {
            tail->next = new ListNode(carry);
        }
        return head;
    }
};

python实现:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        result = ListNode(-1)
        l3 = result
        flag = 0
        while l1 is not None and l2 is not None:
            a = l1.val
            b = l2.val
            c = a + b + flag
            l3.next = ListNode(c % 10)
            flag = 1 if c >= 10 else 0
            l1 = l1.next
            l2 = l2.next
            l3 = l3.next

        while l1 is not None:
            a = l1.val + flag
            l3.next = ListNode(a % 10)
            flag = 1 if a >= 10 else 0
            l1 = l1.next
            l3 = l3.next

        while l2 is not None:
            b = l2.val + flag
            l3.next = ListNode(b % 10)
            flag = 1 if b >= 10 else 0
            l2 = l2.next
            l3 = l3.next

        if flag == 1:
            l3.next = ListNode(flag)

        return result.next

4 两数之和

题号:4
难度:困难
https://leetcode-cn.com/problems/median-of-two-sorted-arrays/
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例 1: c nums1 = [1, 3] nums2 = [2]则中位数是 2.0
示例 2:c nums1 = [1, 2] nums2 = [3, 4]则中位数是 (2 + 3)/2 = 2.5

思路:
**第一种:利用二分策略
中位数:用来将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。
由于题目要求时间复杂度为 O(log(m + n)),所以不能从两个有序数组的首尾挨个遍历来找到中位数(复杂度 O(m + n));而是要通过二分策略,通过每次比较,能够直接按比例的刷掉一组数字,最后找到中位数(复杂度 O(log(m + n)))。

nums1: [a1,a2,a3,...am] 
nums2: [b1,b2,b3,...bn]
[nums1[:i],nums2[:j] | nums1[i:], nums2[j:]]
nums1 取 i 个数的左半边 
nums2 取 j = (m+n+1)/2 - i 的左半边

只要保证左右两边 个数 相同,中位数就在 | 这个边界旁边产生,从而可以利用二分法找到合适的 i。

C++实现

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        if (nums1.size() > nums2.size()) {
            return findMedianSortedArrays(nums2, nums1);
        }
        
        int m = nums1.size();
        int n = nums2.size();
        int left = 0, right = m;
        // median1:前一部分的最大值
        // median2:后一部分的最小值
        int median1 = 0, median2 = 0;

        while (left <= right) {
            // 前一部分包含 nums1[0 .. i-1] 和 nums2[0 .. j-1]
            // 后一部分包含 nums1[i .. m-1] 和 nums2[j .. n-1]
            int i = (left + right) / 2;
            int j = (m + n + 1) / 2 - i;

            // nums_im1, nums_i, nums_jm1, nums_j 分别表示 nums1[i-1], nums1[i], nums2[j-1], nums2[j]
            int nums_im1 = (i == 0 ? INT_MIN : nums1[i - 1]);
            int nums_i = (i == m ? INT_MAX : nums1[i]);
            int nums_jm1 = (j == 0 ? INT_MIN : nums2[j - 1]);
            int nums_j = (j == n ? INT_MAX : nums2[j]);

            if (nums_im1 <= nums_j) {
                median1 = max(nums_im1, nums_jm1);
                median2 = min(nums_i, nums_j);
                left = i + 1;
            } else {
                right = i - 1;
            }
        }

        return (m + n) % 2 == 0 ? (median1 + median2) / 2.0 : median1;
    }
};

第二种:通过合并为一个有序数组的方式
思路:首先把两个有序数组合并为一个有序数组,然后根据数组长度来确定中位数,如果数组长度为偶数,那么返回两个中位数的平均值,如果数组长度为奇数,那么返回中位数。
python实现

class Solution:
    def findMedianSortedArrays(self, nums1, nums2):
        m = len(nums1)
        n = len(nums2)
        nums1.extend(nums2)# 合并
        nums1.sort()# 排序		
        if (m + n) & 1:# 如果是奇数 返回中位数
            return nums1[(m + n - 1) >> 1]
        else:# 返回两个中位数的平均值
            return (nums1[(m + n - 1) >> 1] + nums1[(m + n) >> 1]) / 2

5 最长回文子串

题号:5
难度:中等
https://leetcode-cn.com/problems/longest-palindromic-substring/
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1: c 输入: “babad” 输出: “bab” 注意: “aba” 也是一个有效答案。
示例 2: c 输入: “cbbd” 输出: “bb”
示例 3: c 输入: “a” 输出: “a”

实现
回文是一个正读和反读都相同的字符串,例如,“aba”是回文,而“abc”不是。
第一种:暴力法,列举所有的子串,判断该子串是否为回文。
执行结果:超出时间限制
c++实现

public class Solution
{
    public string LongestPalindrome(string s)
    {
        if (string.IsNullOrEmpty(s))
            return string.Empty;
        if (s.Length == 1)
            return s;

        int start = 0;
        int end = 0;
        int len = int.MinValue;
        for (int i = 0; i < s.Length; i++)
        {
            for (int j = i + 1; j < s.Length; j++)
            {
                string str = s.Substring(i, j - i + 1);
                if (isPalindrome(str) && str.Length > len)
                {
                    len = str.Length;
                    start = i;
                    end = j;
                }
            }
        }
        return s.Substring(start, end - start + 1);
    }

    public bool isPalindrome(string s)
    {
        for (int i = 0, len = s.Length / 2; i < len; i++)
        {
            if (s[i] != s[s.Length - 1 - i])
                return false;
        }
        return true;
    }
}

第二种:动态规划
动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。

我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。
具体的动态规划算法多种多样,但它们具有相同的填表格式。
使用记号 s[l, r] 表示原始字符串的一个子串,l、r 分别是区间的左右边界的索引值,使用左闭、右闭区间表示左右边界可以取到。
dp[l, r] 表示子串 s[l, r](包括区间左右端点)是否构成回文串,是一个二维布尔型数组。
当子串只包含 1 个字符,它一定是回文子串;
当子串包含 2 个以上字符的时候:s[l, r] 是一个回文串,那么这个回文串两边各往里面收缩一个字符(如果可以的话)的子串 s[l + 1, r - 1] 也一定是回文串。
故,当 s[l] == s[r] 成立的时候,dp[l, r]的值由 dp[l + 1, r - l] 决定,这里还需要再多考虑一点点:“原字符串去掉左右边界”的子串的边界情况。
当原字符串的元素个数为 3 的时候,如果左右边界相等,那么去掉它们以后,只剩下 1 个字符,它一定是回文串,故原字符串也一定是回文串;
当原字符串的元素个数为 2 的时候,如果左右边界相等,那么去掉它们以后,只剩下 0 个字符,显然原字符串也一定是回文串。
综上,如果一个字符串的左右边界相等,判断为回文只需以下二者之一成立即可: - 去掉左右边界以后的字符串不构成区间,即s[l + 1, r - 1]包含元素少于2个,即:r - l <= 2。 - 去掉左右边界以后的字符串是回文串,即dp[l + 1, r - 1] == true。

python实现

class Solution:
    def longestPalindrome(self, s: str) -> str:
        count = len(s)
        if count == 0 or count == 1:
            return s
        longestPalindromelen = 1
        longestPalindromeStr = s[0:1]
        dp = [[False] * count for i in range(count)]
        for r in range(1, count):
            for l in range(0, r):
                if s[r] == s[l] and (r - l <= 2 or dp[l + 1][r - 1] == True):
                    dp[l][r] = True
                    if longestPalindromelen < r - l + 1:
                        longestPalindromelen = r - l + 1
                        longestPalindromeStr = s[l:l + longestPalindromelen]
        return longestPalindromeStr
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值