LeetCode 算法系列:第 1 ~ 5 题

本文深入解析LeetCode上的五道经典算法题,包括两数之和、两数相加、无重复字符的最长子串、寻找两个正序数组的中位数及最长回文子串,提供C++及Python实现代码,旨在帮助读者掌握高效算法解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

两数之和
两数相加
无重复字符的最长子串
寻找两个正序数组的中位数
最长回文子串

1.两数之和

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

示例

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

思路

第一种做法:循环俩次,依次遍历查找,时间复杂度为O(n^2)
第二种方法:使用哈希表,将出现过的数全部存到哈希表中,然后依次遍历哈希表中的值,判断目标值减去x的值target - x是否存在即可,时间复杂度为O(n)

c++版本

 vector<int> twoSum(vector<int>& nums, int target) {
       unordered_map<int, int> mp;
       for(int i =0;i<nums.size();i++)
       {
           int r = target - nums[i];
           if(mp.count(r))
                return {mp[r],i};
            mp[nums[i]]=i;
       } 
       return {};
    }

python版本

class Solution(object):
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        mp = {}
        for i, x in enumerate(nums):
            r = target - x
            if r in mp:
                return [mp[r], i]
            mp[x] = i
        return [];

2 两数相加

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

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

思路

(模拟人工加法) O(n)
这是道模拟题,模拟我们小时候列竖式做加法的过程:
从最低位至最高位,逐位相加,如果和大于等于10,则保留个位数字,同时向前一位进1.
如果最高位有进位,则需在最前面补1.
做有关链表的题目,有个常用技巧:添加一个虚拟头结点:ListNode *head = new ListNode(-1),可以简化边界情况的判断。
时间复杂度:由于总共扫描一遍,所以时间复杂度是 O(n).

c++版

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode * p = new ListNode(-1);
        ListNode * cur = p;
        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);
            cur = cur->next;
            t /= 10;
        }
        return p->next;
    }
};

python版本

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution(object):
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        p = ListNode(-1)
        cur = p
        t = 0
        while(l1 or l2 or t):
            if l1:
                t += l1.val
                l1 = l1.next
            if l2:
                t += l2.val
                l2 = l2.next
            cur.next = ListNode(t % 10)
            cur = cur.next
            t /= 10
        return p.next
}; 	

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

题目描述

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

示例1

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

示例2

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

示例3

输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

思路

(双指针扫描) O(n)
定义两个指针 i,j(i<=j),表示当前扫描到的子串是 [i,j] (闭区间)。
扫描过程中维护一个哈希表unordered_map<char,int> hash,表示 [i,j]中每个字符出现的次数。
线性扫描时,每次循环的流程如下:
指针 j 向后移一位, 同时将哈希表中 s[j] 的计数加一: hash[s[j]]++;
假设 j 移动前的区间 [i,j]中没有重复字符,则 j 移动后,只有 s[j] 可能出现2次。
因此我们不断向后移动 i,直至区间 [i,j]中 s[j] 的个数等于1为止;
复杂度分析:由于 i,j 均最多增加n次,且哈希表的插入和更新操作的复杂度都是 O(1),
因此,总时间复杂度 O(n).

c++代码

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

python版本

class Solution(object):
    def lengthOfLongestSubstring(self, s):
        mp={}
        ans=0
        i=0
        for j in range(len(s)):
            if s[j] in mp:
                mp[s[j]] +=1
            else:
                mp[s[j]]=1
            while(mp[s[j]]>1):
                mp[s[i]] -= 1
                i += 1
            ans = max(ans,j-i+1)
        return ans
        """
        :type s: str
        :rtype: int
        """

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

题目描述

给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。

请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1 和 nums2 不会同时为空。

示例1

nums1 = [1, 3]
nums2 = [2]

则中位数是 2.0

示例2

nums1 = [1, 2]
nums2 = [3, 4]

则中位数是 (2 + 3)/2 = 2.5

思路

使用二分的做法求解
交换顺序

     为了减少思考,我们先假定一个序列的长度总不大于第二个。如果大于了,那么就交换一下。

怎么二分查找呢

     假设两个序列按顺序合并了,那么中间点的位置就在(len1 + len2 + 1) // 2

假定这个理想中位数为x

     考虑一般情况下,第一个序列存在一个数,其左边都是小于x,右边都大于;对第二个序列也是一样。
我们对这两个数在各自序列的位置分别称作mid1和mid2。
     所以我们首先先对第一个序列二分查找。
记录左边界,右边界为第一个序列的左右边界。
而查找的中间就是左右边界的中间点。
对于mid2,便是(len1 + len2 + 1) // 2减去mid1

更新二分查找的条件

     mid1左侧和mid2左侧的数都应该比mid1和mid2对应的数小。

    所以可以肯定,如果mid2左侧的数比mid1对应的数都大,那么第一行的中间太靠左了。

可以这么想,如果mid2左侧的数比mid1对应的都大,那不如第二行的数选小一点而第一行的数选大一点,这样两个数会更接近。

要把第一行的中间往右,即二分查找的更新left。

反之更新right。套用模板。

记得mid1不要越过上限!

c++版本

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int total = nums1.size() + nums2.size();
        if (total % 2 == 0)
        {
            int left = findKthNumber(nums1, 0, nums2, 0, total / 2);
            int right = findKthNumber(nums1, 0, nums2, 0, total / 2 + 1);
            return (left + right) / 2.0;
        }
        else
        {
            return findKthNumber(nums1, 0, nums2, 0, total / 2 + 1);
        }
    }

    int findKthNumber(vector<int> &nums1, int i, vector<int> &nums2, int j, int k)
    {
        if (nums1.size() - i > nums2.size() - j) return findKthNumber(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 findKthNumber(nums1, i, nums2, j + k / 2, k - k / 2);
        }
        else
        {
            return findKthNumber(nums1, si, nums2, j, k - (si - i));
        }
    }
};

python版本

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        n1 = len(nums1)
        n2 = len(nums2)
        if n1 > n2:
            return self.findMedianSortedArrays(nums2,nums1)
        k = (n1 + n2 + 1)//2
        left = 0
        right = n1
        while left < right :
            m1 = left +(right - left)//2
            m2 = k - m1
            if nums1[m1] < nums2[m2-1]:
                left = m1 + 1
            else:
                right = m1
        m1 = left
        m2 = k - m1 
        c1 = max(nums1[m1-1] if m1 > 0 else float("-inf"), nums2[m2-1] if m2 > 0 else float("-inf") )
        if (n1 + n2) % 2 == 1:
            return c1
        c2 = min(nums1[m1] if m1 < n1 else float("inf"), nums2[m2] if m2 <n2 else float("inf"))
        return (c1 + c2) / 2

5 最长回文子串

题目描述

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

示例1

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

示例 2

输入: “cbbd” 输出: “bb”

思路

(暴力枚举) O(n2)

由于字符串长度小于1000,因此我们可以用 O(n2) 的算法枚举所有可能的情况。

首先枚举回文串的中心 i,然后分两种情况向两边扩展边界,直到遇到不同字符为止:

回文串长度是奇数,则依次判断 s[i−k]==s[i+k],k=1,2,3,…

回文串长度是偶数,则依次判断 s[i−k]==s[i+k−1],k=1,2,3,…

如果遇到不同字符,则我们就找到了以 ii 为中心的回文串边界。

时间复杂度分析:一共两重循环,所以时间复杂度是 O(n2)。

c++版本

class Solution {
public:
 string longestPalindrome(string s) {
        int n = s.size();
        string ans ;
        int l = 0;
        for(int i =0;i<n;i++)
        {
                for(int k =0;i-k>=0&&i+k<n;k++)
                {
                   if(s[i-k]!=s[i+k])
                    break;
                    if(2*k+1>l)
                    {
                        ans = s.substr(i-k,2*k+1);
                        l = 2*k+1;
                    } 
                }


              for(int k =0;i-k>=0&&i+k+1<n;k++)
                {
                   if(s[i-k]!=s[i+k+1])
                    break;
                    if(2*(k+1)>l)
                    {
                        ans = s.substr(i-k,2*(k+1));
                        l = 2*(k+1);
                    } 
                }       
            }
        return ans;
    }
};

python版本

class Solution(object):
    def longestPalindrome(self, s):
    ans =""
        n = len(s)
        for i in range(n):
            for k in range(n):
                if i-k<0 or i+k>=n or s[i-k]!=s[i+k]:
                    break
                if(2*k+1>len(ans)):
                    ans = s[i-k:i+k+1]
            for k in range(n):
                if i-k<0 or i+k+1>=n or s[i-k]!=s[i+k+1]:
                    break
                if(2*(k+1)>len(ans)):
                    ans = s[i-k:i+k+2]
        return ans
         """
        :type s: str
        :rtype: str
        """
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值