leetcode刷题:1-20

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/xiao_lxl/article/details/91548387


leetcode刷题:1-20

1 Two Sum

在列表中找到两个数,使得它们的和等于某一给定值,返回这两个数的位置.时间复杂度:O(n),python中的字典其实就是哈希表的应用,所以我们通过字典用哈希表来降低查找的时间复杂度

def twoSum(self, nums, target):
    """
    :type nums: List[int]
    :type target: int
    :rtype: List[int]
    """
    d = {}
    
    for i, n in enumerate(nums):
        m = target - n
        if m in d:
            return [d[m], i]
        else:
            d[n] = i
        

2 Add Two Numbers

将两个倒序存放在单链表里的数相加,将结果倒序存储在单链表里返回.思路非常简单,先将两个单链表中的数字分别提取出来求和,然后将=求得的和存入一个单链表,实际上相加这一步也可以直接在原链表中完成,只需要添加判断条件while(l1 or l2 or carry)即可.

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

class Solution:
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        
        node1 = l1
        node2 = l2
        l3 = ListNode(0)
        l3.next = ListNode(0)
        node3 = l3
        
        sum1 = 0
        coe = 1
        while not node1 is None:
            sum1 += node1.val * coe
            coe *= 10
            node1 = node1.next
            
        sum2 = 0
        coe = 1
        while not node2 is None:
            sum2 += node2.val * coe
            coe *= 10
            node2 = node2.next
            
        sum = sum1+sum2
        
        while sum > 0:
            node3.next = ListNode(sum % 10)
            node3 = node3.next
            sum //= 10
            
        return l3.next

3 Longest Substring Without Repeating Characters

找到字符串中没有重复字符的最大子串.一开始没有想到用字典,而是直接用str来存储字串,时间复杂度是O(n^2),后来用字典将时间复杂度降到了O(n).注意到仅当字典中出现重复值且该重复值在strat区段里时才移动start.另外用了Sliding Window的思想,每次将strat移动到重复值的下一位置

class Solution:
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        start = 0
        max_length = 0
        substring = {}
        
        for i, c in enumerate(s):
            if c in substring and start <= sunstring[c]:
                start = substring[c] + 1
            else:
                max_length = max(max_length, i-start+1)
            substring[c] = i
            
        return max_length

4 寻找两个有序数组的中位数

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。

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

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

def findMedianSortedArrays( nums1, nums2):
    """
    :type nums1: List[int]
    :type nums2: List[int]
    :rtype: float
    """

    m = len(nums1)
    n = len(nums2)

    if m > n:
        nums1,nums2,m,n = nums2, nums1,n,m
    if n== 0:
        raise ValueError

    imin = 0
    imax = m
    half_len = (m+n+1) // 2

    while imin <= imax:
        i = (imin + imax) // 2
        j = half_len - i
        
        print(i,j)

        if i < m and nums2[j-1] > nums1[i]:
            imin = i + 1
            print("(imin)")
            print( imin)
            
        elif i > 0 and nums1[i-1] > nums2[j]:
            imax = i-1
            print("(imax)")
            print(imax)
        else:
            print("(else)")

            if i == 0:
                max_of_left = nums2[j-1]
            elif j == 0:
                max_of_left = nums1[i-1]
            else:
                max_of_left = max(nums1[i-1], nums2[j-1])
                
            print('max_of_left:')
            print(max_of_left)

            if (m+n) % 2 == 1:
                return max_of_left

            if i == m:
                min_of_right = nums2[j]
            elif j == n:
                min_of_right = nums1[i]
            else:
                min_of_right = min(nums1[i], nums2[j])
                
            print('max_of_left,min_of_right:')
            print(max_of_left,min_of_right)

            return (max_of_left + min_of_right) / 2.0
        

    
                

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

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

print(findMedianSortedArrays(nums1,nums2))

5 Longest Palindromic Substring

最长回文子串问题,一开始我的思路如下:回文子串的特点是首尾字母相同,所以我对每一个字母都找到位于它后面的相同字母,利用切片判断这一段是否为回文子串(str[i:j]==str[i:j][::-1]).虽然AC了但是时间复杂度很高,主要是因为str.find操作非常耗时.

后来看了Solution发现这是一道可以用动态规划解决的问题,思路是若s是回文字串,令s’=s加上s左右两侧的两个字母,如果这两个字母相同则s’也是回文字串.重写代码如下:

class Solution:
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        
        max = 0
        palindromic = '' if len(s) == 0 else s[0]
        for i in range(len(s)):
            length = 1
            while i - length >= 0 and i+length < len(s) and s[i-length] == s[i+length]:
                
                tmp = s[i-length : i+length+1]
                print(i, tmp)
                if len(tmp) > max:
                    max = len(tmp)
                    palindromic = tmp
                length += 1
            length = 1
            
            while i-length+1 >=0 and i+length <len(s) and s[i-length+1] == s[i+length]:
                tmp = s[i-length+1:i+length+1]
                print(i, tmp)
                if len(tmp) >max:
                    max = len(tmp)
                    palindromic = tmp
                length += 1
        return palindromic
      
solu = Solution()
str1 = "babad"
print(solu.longestPalindrome(str1))

6 ZigZag Conversion

一道将字符串做之字形排列的题目.我们用n表示行数,将排列后得到的字符串分为完整竖列和折线两部分.每个完整竖列有n个数,每两个完整竖列之间的折线有n-2列,每列一个数,因此每两个完整竖列中同一行的数的间隔是n+n-2=2n-2.同时我们发现,除了第一行和最后一行之外的第i行都有折线,第i行的第一个折线是第2n-i个数.于是可以遍历输出每一行,判定条件是这一行我们要输出的数字是否超出了字符串的长度

class Solution:
    def convert(self, s, numRows):
        """
        :type s: str
        :type numRows: int
        :rtype: str
        """
        zigzag = ''
        
        if numRows == 1 or numRows == 0 or numRows >=len(s):
            return s
        
        space = 2*numRows-2
        for i in range(1, numRows+1):
            n=0
            if i==1 or i==numRows:
                while i+n*space <= len(s):
                    zigzag += s[i+n*space-1]
                    n+=1
            else:
                while i+n*space <= len(s):
                    zigzag += s[i+n*space-1]
                    if (2*numRows-i) + (n*space) <= len(s):
                        zigzag += s[(2*numRows-i)+(n*space)-1]
                    n+=1
                    
        return  zigzag

7 Reverse Integer 将给定的数字倒序输出.

class Solution(object):
    def reverse(self, x):
        """
        :type x: int
        :rtype: int
        """
        tmp = abs(x)
        
        sum = 0
        while tmp > 0:
            sum = sum*10 + tmp%10
            tmp = tmp // 10
            
        sum = sum if x >=0 else -sum
        
        return sum if sum<2**31 and sum>-2**31 else 0

8 String to Integer(atoi)

将给定字符串中符合条件的一串数字字符转化为int类型返回.我的思路是设定标志位start=0和符号位sign,遍历字符串,当start=0时遇到空格则continue,遇到+则记录sign=1,遇到-则记录sign=-1,遇到数字则记录数字;当strat=1时代表已经找到了第一个数字或符号位,此时遇到除数字之外的字符都break,遇到数字则继续记录数字.注意我们得到的整数值不能超过INT_MAX和INT_MIN.后来发现其实用str.strip()函数来去除字符串头尾的空格会更方便

class Solution(object):
    def myAtoi(self, str):
        """
        :type str: str
        :rtype: int
        """
        
        ans = 0
        start = 0
        sign = 0
        if str.isspace() is True:
            print(0)
        for i in str:
            if start == 0:
                if i.isspace() is True:
                    continue
                if i == '+':
                    sign = 1
                elif i == '-':
                    sign = -1
                elif i.isdigit() is True:
                    sign = 1
                    ans = ans*10 + int(i)
                else:
                    break
                start = 1
            else:
                if i.isdigit() is True:
                    ans = ans*10+int(i)
                else:
                    break
                    
        ans = sign*ans
        if ans >= 2147483647:
            return 2147483647
        elif ans <= -2147483648:
            return -2147483648
        
        return ans
                    

9 Palindrome Number

判断一个数字是否是回文数.题目要求不能用额外的空间,否则可以利用python的字符串切片轻松解决.我的思路是求出该整数的位数,判断第一位数和最后一位数是否相同,如果相同则将位数/100,然后将原数字的首尾两个数删除,最后如果位数<1说明是回文数.

def isPalindrome( x):
    """
    :type x: int
    :rtype: bool
    """

    if x<0:
        return False

    high = 1
    while x/high >= 10:
        high *= 10
    print(high)    
    
    while x // high == x%10:
        x = x%high//10
        print(x)
        high /= 100
        print(high)
        if high < 1:
            return True

    return False

10 正则表达式匹配

给定一个字符串 (s) 和一个字符模式 §。实现支持 ‘.’ 和 ‘*’ 的正则表达式匹配。

‘.’ 匹配任意单个字符。 ‘*’ 匹配零个或多个前面的元素。 匹配应该覆盖整个字符串 (s) ,而不是部分字符串。

说明:

s 可能为空,且只包含从 a-z 的小写字母。 p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。 示例 1:

输入: s = “aa” p = “a” 输出: false 解释: “a” 无法匹配 “aa” 整个字符串。 示例 2:

输入: s = “aa” p = “a” 输出: true 解释: ‘’ 代表可匹配零个或多个前面的元素, 即可以匹配 ‘a’ 。因此, 重复 ‘a’ 一次, 字符串可变为 “aa”。

if len(p) == 0: return len(s) == 0
        
        if len(p) == 1:
            return len(s) == 1 and (s[0] == p[0] or p[0] == '.')
        
        if p[1] != '*':
            if len(s) == 0: return False
            return (s[0] == p[0] or p[0] == '.') and self.isMatch(s[1:], p[1:])
        
        while (len(s) != 0 and (s[0] == p[0] or p[0] == '.')):
            if self.isMatch(s,p[2:]): 
                return True
            s=s[1:]
            
        return self.isMatch(s,p[2:])

执行用时 :2056 ms, 在所有Python提交中击败了7.75%的用户
内存消耗 :11.6 MB, 在所有Python提交中击败了41.60%的用户

11.求其中两条直线与x轴围成的容器的最大容量.

Container With Most Water 给定许多条与y轴平行的直线,求其中两条直线与x轴围成的容器的最大容量.

这道题用到了双指针的思想.我们在数轴的两端分别放置一个left指针和right指针,因为容器容量=较短边*两边位置之差,所以如果移动较大的那个指针,那么容量必定在减小.因此我们不断往中间移动较小的指针才有可能使容量变大,直到两指针相遇为止.

对于算法合理性的逻辑推理:我们假设在best_left和best_right位置取到最大容量,那么left指针到达best_left位置或right指针到达best_right位置至少有一种会发生.不妨令left指针到达best_left位置,此时right指针的位置有三种可能:

位于best_right位置左侧.这说明best_right位置已经被计算过,成立. 位于best_right位置,同上. 位于best_right位置右侧.因为left指针移动的条件是right指针所在边大于left指针所在边,如果符合此条件,且right指针在best_right右侧,那么当前容量一定大于假设中的最大容量,与假设矛盾.所以left指针必定会一路移动至best_right位置.

class Solution(object):
    def maxArea(self, height):
        """
            :type height: List[int]
            :rtype: int
            """
        left = 0
        right = len(height) - 1

        maxArea = 0
        while left != right:
            h = min(height[left], height[right])
            maxArea = max(maxArea, h * (right-left))

            if height[left] < height[right]:
                left += 1
            else:
                right -= 1

        return maxArea

执行用时 :144 ms, 在所有Python提交中击败了55.27%的用户
内存消耗 :13.2 MB, 在所有Python提交中击败了8.60%的用户

12.Integer to Roman

将十进制数字转化为罗马数字.比较简单的一道题.我的思路是判断当前位数,改变代表1/5/10的字符然后逐位输出.也可以直接将每位上的各种字符表示存在列表里,然后直接取出.

def intToRoman(num):
    """
    :type num: int
    :rtype: str
    """
    
    carry = 1
    roman = ''
    
    while num!= 0:
        n = num % 10
        num //= 10
        if carry == 1:
            numeral_1 = 'I'
            numeral_5 = 'V'
            numeral_10 = 'X'
        elif carry == 10:
            numeral_1 = 'X'
            numeral_5 = 'L'
            numeral_10 = 'C'
        elif carry == 100:
            numeral_1 = 'C'
            numeral_5 = 'D'
            numeral_10 = 'M'
        else:
            numeral_1 = 'M'
            numeral_5 = ''
            numeral_10 = ''
            
        if 1<= n <=3:
            roman = numeral_1*n + roman
        elif n==4:
            roman = numeral_1+numeral_5 + roman
        elif 5<=n<=8:
            roman = numeral_5 + numeral_1*(n-5)+roman
        elif n==9:
            roman = numeral_1+numeral_10+roman
            
        carry *= 10
        
    return roman
            
intToRoman(48)        

执行用时 :32 ms, 在所有Python提交中击败了97.90%的用户
内存消耗 :11.7 MB, 在所有Python提交中击败了33.80%的用户

13.Roman to Integer

将罗马数字转化为十进制数字.非常无聊的一道题.比较简单的方法是写非常多的if语句来判断,或者将罗马数字与对应的十进制数字存入字典来转换.下面是我在discuss里看到的一个方案,巧妙利用了罗马数字"大数前面的小数用来减,大数后面的小数用来加"这个特点.

def romanToInt( s):
    """
    :type s: str
    :rtype: int
    """
    roman_map = {
        "I": 1,
        "V": 5,
        "X": 10,
        "L": 50,
        "C": 100,
        "D": 500,
        "M": 1000,
    }
    
    
    result = 0
    last_num = None
    for char in s:
        current_num = roman_map[char]
        if last_num is None or last_num >= current_num:
            result += current_num
        elif last_num < current_num:
            result += current_num - 2 * last_num
        last_num = current_num
    return result


romanToInt('XLVIII')

执行用时 :52 ms, 在所有Python提交中击败了85.13%的用户
内存消耗 :11.6 MB, 在所有Python提交中击败了32.99%的用户

14.Longest Common Prefix

找最长公共前缀字符串.我的思路是找出列表中最短的字符串,然后对最短字符串的每个字符都在列表中遍历,直到出现不同或者遍历结束为止.在discuss里看到很多方法利用了python中的sort(),min(),max()这些内置方法对字符串排序,会使时间快很多.

def longestCommonPrefix( strs):
    """
    :type strs: List[str]
    :rtype: str
    """
    
    prefix = ''
    
    if strs == []:
        return prefix
    
    mininum = float("inf")
    for s in strs:
        mininum = min(len(s), mininum)
        
    print(mininum)
        
    i=0
    for j in range(mininum):
        for i in range(len(strs)):
            while strs[i][j] != strs[0][j]:
                print(prefix)
                return prefix
        prefix = prefix + strs[0][j]
        
    return prefix

T = ['abc','abcd','abfg','abopfge']
longestCommonPrefix( T)

执行用时 :40 ms, 在所有Python提交中击败了25.81%的用户
内存消耗 :11.9 MB, 在所有Python提交中击败了23.86%的用户

15.3Sum

给定一个数组,找到其中三个数的和为零的所有可能,以列表形式返回.这道题的基本思路是先将数组排序,从左往右遍历一次.在遍历每个数的过程中设立两个指针,如果三个数的和大于零则左移右指针,如果三个数的和小于零则右移左指针,直到两个指针相遇.注意我们用的是set()来存储找到的结果,可以避免list中出现重复.在此基础上,我增加了一个对排序过的数组的操作,即当最左边两个数与最右边一个数的和大于零时删去最右边的数,当最左边一个数与最右边两个数的和小于零时删去最左边的数.这个操作大大提升了运行速度.

def threeSum(self, nums):
    """
    :type nums: List[int]
    :rtype: List[List[int]]
    """
    
    zeros = set()
    nums.sort()
    
    if len(nums) < 3:
        return []
    
    if nums.count(0) > len(nums) -2:
        return [[0,0,0]]
    
    while len(nums) > 3 and (nums[0] + nums[1] + nums[-1] > 0 or nums[-1]+nums[-2]+nums[0] < 0):
        if nums[0] + nums[1] + nums[-1] > 0 :
            nums.remove(nums[-1])
        else:
            nums.remove(nums[0])
            
    for i in range(len(nums)-2):
        if nums[i] > 0:
            break
        j = i+1
        k = len(nums) -1
        while j < k:
            sum = nums[i] + nums[j] + nums[k]
            if sum == 0:
                zeros.add((nums[i], nums[j], nums[k]))
                j += 1
                continue
            elif sum < 0:
                j += 1
            else:
                k -= 1
                
    return list(map(list,zeros))

执行用时 :1044 ms, 在所有Python提交中击败了21.21%的用户
内存消耗 :16.9 MB, 在所有Python提交中击败了5.55%的用户

16.3Sum Closest

给定一个数组和一个目标值,找到数组中的三个数,使得这三个数之和与目标值之间的差距最小,返回它们的和.

这题的思路与15题类似,也是利用双指针,只不过判定条件从三个数之和是否为零改成了三个数之和是

否比目前已有的closest值更接近目标

def threeSum(self, nums, target):
    """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
    
    closest = nums[0] + nums[1] + nums[2]
    nums.sort()
    length = len(nums)
    for i in range(length):
        l = i+1
        r = length - 1
        while l < r:
            tmp = nums[i] + nums[l] + nums[r]
            if tmp == target:
                closest = target
                break
            elif tmp < target:
                if target - tmp < abs(target - closest):
                    closest = tmp
                l += 1
            elif tmp > target:
                if tmp - target < abs(target - closest):
                    closest = tmp;
                r -= 1
    return closest

17 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

[外链图片转存失败(img-SdYneUnS-1566218841880)(attachment:image.png)]

示例:

输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].

class Solution(object):
    def letterCombinations(self, digits):
        """
        :type digits: str
        :rtype: List[str]
        """
        
        if not digits:
            return []
        
        digit2char = {
            '2':'abc',
            '3':'def',
            '4':'ghi',
            '5':'jkl',
            '6':'mno',
            '7':'pqrs',
            '8':'tuv',
            '9':'wxyz',
        }
        
        res = [ i for i in digit2char[digits[0]] ]
        
        result = []
        for i in digits[1:]:
            for m in res:
                for n in digit2char[i]:
                    res = m+n
                    result.append(res)
                    #print(res)
                    
        return result
        
        
solu =  Solution()
tmp = '23'
print(solu.letterCombinations(tmp))
        

18.4Sum 找出list中所有相加等于target的4个数的list.

一开始我的思路是令new_target=target-num1,然后转换为一个3Sum问题,但这种做法的时间复杂度太高了.查看Solution后发现这道题要使用hash的思想,在python中对应的实现就是使用先dict存储list中的两数之和和它们在list中的位置,然后对于这个dict中的value,寻找一个key=target-value,然后将他们对应的数字存入list即可.需要注意的是python中的list,set,dict是不可哈希的,int,float,str,tuple是可哈希的.

def fourSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        d = dict()
        
        for i in range(len(nums)):
            for j in range(i+1, len(nums)):
                sum2 = nums[i] + nums[j]
                if sum2 in d:
                    d[sum2].append((i,j))
                else:
                    d[sum2] = [(i,j)]
                    
        result = []
        for key in d:
            value = target - key
            if value in d:
                list1 = d[key]
                list2 = d[value]
                for(i,j) in list1:
                    for(m,n) in list2:
                        if i != m and i != n and j != m and j != n:
                            flist = [nums[i], nums[j],nums[m],nums[n]]
                            flist.sort()
                            if flist not in result:
                                result.append(flist)
        return result
            

19 删除链表的倒数第N个节点

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5. 说明:

给定的 n 保证是有效的。

进阶:

你能尝试使用一趟扫描实现吗?

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

class Solution(object):
    def removeNthFromEnd(self, head, n):
        """
        :type head: ListNode
        :type n: int
        :rtype: ListNode
        """
        res = ListNode(0)
        res.next = head
        list1 = res
        list2 = res
        for i in range(n):
            list1 = list1.next
        while list1.next:
            list1 = list1.next
            list2 = list2.next
        list2.next = list2.next.next
        return res.next

20 有效的括号

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 注意空字符串可被认为是有效字符串。

示例 1:

输入: “()” 输出: true 示例 2:

输入: “()[]{}” 输出: true

class Solution(object):
    def isValid(self, s):
        """
        :type s: str
        :rtype: bool
        """
        result = []
        
        samples = ['[]','{}','()']
        
        for i in range(len(s)):
            result.append(s[i])
            
            if len(result) >= 2 and result[-2] + result[-1] in samples:
                result.pop()
                result.pop()
                
                
        return len(result) == 0
        
solu = Solution()
ss = "{{[}]}"

print(solu.isValid(ss))

猜你喜欢

转载自blog.csdn.net/xiao_lxl/article/details/91548387