1

LeetCode刷题大全

 3 years ago
source link: https://www.guofei.site/2020/09/19/lc.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

LeetCode刷题大全

2020年09月19日

Author: Guofei

文章归类: 8-数据结构与算法 ,文章编号: 590


版权声明:本文作者是郭飞。转载随意,但需要标明原文链接,并通知本人
原文链接:https://www.guofei.site/2020/09/19/lc.html

Edit

1. Two Sum

  1. 限制加和等于n。不要每一步都判断x+y=n,而是判断n-x=y
  2. 两次遍历。考虑不写2个for,而是1次遍历,把元素放到hashtable中
def twoSum(self, nums: List[int], target: int) -> List[int]:
    tmp_dict = dict()
    for i, j in enumerate(nums):
        if j in tmp_dict:
            return [tmp_dict[j], i]
        tmp_dict[target - j] = i

2. Add Two Numbers

  1. 对于链表,curr.next 指针向下走,这个不多说
  2. curr1=curr1.next if curr1 else curr1 这个操作,让遍历到尾部后,值保持curr1=None,好处是少些几行代码。作为配合,在其它行,可以用 if curr1 来判断是否到尾部
  3. 链表还有个好处,如本例,l3 是含一个空的头的,返回 l3.next 即可
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
    carry=0
    l3=ListNode(None)
    curr1,curr2,curr3=l1,l2,l3
    while curr1 or curr2 or carry:
        value=(curr1.val if curr1 else 0)+(curr2.val if curr2 else 0)+carry
        carry,val=divmod(value,10)
        curr3.next=ListNode(val)
        curr1=curr1.next if curr1 else curr1
        curr2=curr2.next if curr2 else curr2
        curr3=curr3.next
    return l3.next

3. Longest Substring Without Repeating Characters

  1. 2 sum 那道题里面写过,这种貌似需要两次遍历的题目。都可以想一下能不能用 hashTable 存下历史遍历,从而变成一次遍历。
def lengthOfLongestSubstring(self, s: str) -> int:
    used = dict()
    start = 0
    res = 0
    for i, c in enumerate(s):
        if c in used and start <= used[c]:
            start = used[c] + 1
        else:
            res = max(res, i - start + 1)
        used[c] = i
    return res

4. Median of Two Sorted Arrays

这个题目其实可以 cheat,俩列表加一起,然后用Python自代的sort,代码就不写了。

套路详解:https://leetcode.com/problems/median-of-two-sorted-arrays/discuss/2481/Share-my-O(log(min(mn)))-solution-with-explanation

  1. 中位数的定义:中位数两边的数量相等。
  2. 因此,num1 的分割和num2的分割存在一个恒等关系,len(left1)+len(left2)=len(right1)+len(right2)
  3. 这也就意味着,给定num1的分割,就立即能计算出num2的分割
  4. 本质上还是把两次遍历变成一次
  5. 如果if情况过多,考虑换一下两个变量,减少代码量。

(代码还是好难写,奇偶之类的)

def median(A, B):
    m, n = len(A), len(B)
    if m > n:
        A, B, m, n = B, A, n, m

    imin, imax, half_len = 0, m, (m + n + 1) / 2
    while imin <= imax:
        i = (imin + imax) / 2
        j = half_len - i
        if i < m and B[j-1] > A[i]:
            # i is too small, must increase it
            imin = i + 1
        elif i > 0 and A[i-1] > B[j]:
            # i is too big, must decrease it
            imax = i - 1
        else:
            # i is perfect

            if i == 0: max_of_left = B[j-1]
            elif j == 0: max_of_left = A[i-1]
            else: max_of_left = max(A[i-1], B[j-1])

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

            if i == m: min_of_right = B[j]
            elif j == n: min_of_right = A[i]
            else: min_of_right = min(A[i], B[j])

            return (max_of_left + min_of_right) / 2.0

5. Longest Palindromic Substring

没有套路。中间往外拓,平方级复杂度。

6. ZigZag Conversion

没有套路。按照ZigZag的字面意思,在s上做遍历。
(其实还可以先算出每个值所在的位置,不过有点儿费草稿纸)

class Solution:
    def convert(self, s: str, numRows: int) -> str:
        if numRows==1 or (len(s)<numRows):
            return s
        res_list = [''] * numRows
        location, direction = 0, -1 # 假想开头前面也迭代过程中,所以初始是负号
        for i in s:
            res_list[location] = res_list[location] + i
            if location == 0 or location == numRows - 1:
                direction = -direction
            location += direction
        return ''.join(res_list)

7. Reverse Integer

没有套路。
按位数颠倒一个整数。考虑负号和零。Python用字符串反转就行了。

class Solution:
    def reverse(self, x: int) -> int:
        sign = 1 if x >= 0 else -1
        y=sign*int(str(sign*x)[::-1])
        if y>2**31-1 or y<-2**31:
            return 0
        return y

简洁的写法

class Solution:
    def reverse(self, x: int) -> int:
        sign = (x > 0) - (x < 0)
        r = int(str(sign*x)[::-1])
        return sign*r * (r < 2**31)

8. String to Integer (atoi)

实在无聊的题,就是各种边界条件。不用看了

9.

10. Regular Expression Matching

好题!
套路:

  1. 递归(不是很容易想出)
  2. 递归的某些节点被反复调用。考虑存下来。(下面代码里面没有体现)

https://leetcode.com/problems/regular-expression-matching/solution/


def isMatch(text, pattern):
    if not pattern:
        return not text

    first_match= text and pattern[0] in {text[0],'.'}
    if len(pattern)>=2 and pattern[1]=='*':
        return isMatch(text,pattern[2:]) or first_match and isMatch(text[1:],pattern)
    else:
        return first_match and isMatch(text[1:],pattern[1:])

需要再过一遍:DP也能很好的解这个题。

11. Container With Most Water

  1. 像 2-sum 那个题目,看起来需要2次遍历,立即想到暂存历史计算,减少计算量。
  2. 先看看哪些情况是“不可能”的,把它们排除出去。左挡板从左向右遍历,如果后一个比前一个还短,那么这个不可能是 candidate,右挡板同样的道理。
  3. (到上面这一步已经可以写代码了,最坏平方级复杂度)还可以继续优化(这个看答案才想出来)。我们上一步把不可能的情况删掉后得到了递增的左挡板,和递增(从右往左看)的右挡板。如果左挡板低于右挡板,那么右挡板向左移动就不可能是 candidate,于是又排除了很多情况,变成 O(n) 复杂度。
a=[1,8,6,2,5,4,8,3,7]
i,j=0,len(a)-1
weight=0
while i<j:
    weight=max(weight,(j-i)*min(a[i],a[j]))
    if a[i]<a[j]:
        i+=1
    else:
        j-=1

weight

12. Integer to Roman

all_symbols = [
    ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX']
    ,['', 'X', 'XX', 'XXX','XL', 'L', 'LX', 'LXX', 'LXXX', 'XC']
    , ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM']
    ,['', 'M', 'MM', 'MMM']
]

''.join([all_symbols[i][num//(10**i)%10] for i in range(3,-1,-1)])

13~15 无聊的题目

16. 3Sum Closest

n-Sum 两种思路:

  1. 复用2sum,加一层遍历。2sum复杂度是n,3sum复杂度是O(n^2)
  2. 2sum还有另一种解法,先排序,然后使用 Container With Most Water 的思路,找解复杂度是 O(n),但排序的复杂度是 O(nlgn),所以2sum复杂度是 O(nlgn),3sum复杂度 O(n^2+nlgn)=O(n^2)
  3. 所以2sum和3sum都可以用这个思路,复杂度一样
  4. 但3Sum Closest显然不能用1了,但可以用2

nums=[0,-4,1,-5]
target=0

nums.sort()


def sum2_cloeset(nums,n):
    i,j=0,len(nums)-1
    res=nums[i]+nums[j]
    diff=abs(res-n)
    while i<j:
        sum2=nums[i]+nums[j]
        if sum2==n:
            return sum2
        diff_tmp=abs(sum2-n)
        if diff_tmp<diff:
            diff=diff_tmp
            res=sum2
        if sum2>n:
            j-=1
        else:
            i+=1
    return res


# nums=[1,2,3,4,5]
# sum2_cloeset(nums,5)


diff=abs(sum(nums[:3])-target)
res=sum(nums[:3])
for i in range(len(nums)-2):
    tmp=sum2_cloeset(nums[i+1:],target-nums[i])
    if abs(tmp+nums[i]-target)<diff:
        print(tmp,nums[i])
        res=tmp+nums[i]
        diff=abs(tmp+nums[i]-target)

res

17. Letter Combinations of a Phone Number

基础题,可以以此题为例子,把几种解法练熟了,甚至背下来。

  1. 利用 itertools
    import itertools
    def letterCombinations(self, digits: str) -> List[str]:
     if len(digits)==0:return []
     all_char=['','','abc','edf','ghi','jkl','mno','pqrs','tuv','wxyz']
     candidate=[list(all_char[int(button)]) for button in digits]
     return [''.join(i) for i in  itertools.product(*candidate)]
    
  2. 递归。这个递归方法要求返回所有遍历到叶节点的路径。而不是成功访问1个叶节点就返回。这两种递归都要非常熟悉。
    all_char=['','','abc','edf','ghi','jkl','mno','pqrs','tuv','wxyz']
    def letter_comb(digits):
     if len(digits)==0:
         return []
     if len(digits)==1:
         return list(all_char[int(digits[0])])
     prev=letter_comb(digits[:-1])
     lst=all_char[int(digits[-1])]
     return [s+l for s in prev for l in lst]
    letter_comb(digits)
    
  3. 递归(从前往后),另外,如果不考虑空的特殊情况,代码还可以再省一些
    all_char=['','','abc','edf','ghi','jkl','mno','pqrs','tuv','wxyz']
    def letter_comb(digits):
     if len(digits)==0:
         return ['']
     prev=all_char[int(digits[0])]
     lst=letter_comb(digits[1:])
     return [p+l for p in prev for l in lst]
    
  4. 遇到笛卡尔积问题,优先使用这个套路。递归太容易写错,还是for循环好一些。
    all_char=['','','abc','edf','ghi','jkl','mno','pqrs','tuv','wxyz']
    def letter_comb(digits):
     digits=[int(i) for i in digits]
     res=['']
     for digit in digits:
         res=[i+j for i in res for j in all_char[digit]]
     return res
    
  5. 事实证明,太多骚操作,复用就不方便。简简单单的for循环。(22题复用此代码) ```python all_char=[’’,’’,’abc’,’edf’,’ghi’,’jkl’,’mno’,’pqrs’,’tuv’,’wxyz’]

digits=[int(i) for i in digits] def letter_comb(digits): res=[’’] for digit in digits: tmp=list() for i in res: for j in all_char[digit]: tmp.append(i+j) res=tmp return res




### 18. 4Sum

用递归,nSUM都能做出来
```python

def find_2sum(nums,target):
    i,j=0,len(nums)-1
    res=[]
    while i<j:
        sum2=nums[i]+nums[j]
        if sum2==target:
            res.append([nums[i],nums[j]])
            i+=1
        elif sum2>target:
            j-=1
        else:
            i+=1
    return res


def find_Nsum(nums,target,N):
    if N==2:
        return find_2sum(nums,target)
    res=[]
    for i in range(len(nums)-N+1):
        tmp=find_Nsum(nums[i+1:],target-nums[i],N-1)
        if len(tmp)>0:
            res.extend([[nums[i]]+t for t in tmp])
    return res


nums=[1,0,-1,0,-2,2]
target=0
nums.sort()
find_Nsum(nums,target,N=4)

19. Remove Nth Node From End of List

  1. 凡是链表上的遍历问题,一律先想想能否使用单指针/双指针/多指针
  2. 如果处理的链表是不含空的头节点那种,一律加上空的头节点。这样往往可以减少很多处理边界条件的代码量。
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
    dummy=ListNode('')
    dummy.next=head
    curr1=dummy
    for i in range(n):
        curr1=curr1.next
    curr2=dummy
    while curr1.next:
        curr1=curr1.next
        curr2=curr2.next
    curr2.next=curr2.next.next
    return dummy.next

20. Valid Parentheses

好题目,用堆栈做,没什么难度

def isValid(self, s: str) -> bool:
    stack=list()
    pairs={'(':')','[':']','{':'}'}
    pairs2={')':'(',']':'[','}':'{'}
    for i in s:
        if i in pairs:
            stack.append(i)
        elif i in pairs2:
            if len(stack)==0:
                return False
            elif stack[-1]==pairs2[i]:
                stack.pop()
            else:
                return False
    return len(stack)==0

21. Merge Two Sorted Lists

  1. 遇到链表,第一想到指针
  2. 处理链表,一律先转化成带空头节点的链表。可以减少一些边界条件的代码量,减少错误。
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
    if l1 is None :
        return l2
    if l2 is None:
        return l1
    dummy1,dummy2=ListNode(),ListNode()
    dummy1.next,dummy2.next=l1,l2
    curr1,curr2=dummy1,dummy2.next
    while curr2 and curr1.next:
        if curr1.next.val>curr2.val:
            tmp1,tmp2=curr1.next,curr2.next
            curr1.next=curr2
            curr2.next=tmp1
            curr2=tmp2
            curr1=curr1.next
        else:
            curr1=curr1.next
    while curr1.next:
        curr1=curr1.next
    curr1.next=curr2
    return dummy1.next

22. Generate Parentheses

先把左括号全写出来,然后插入右括号。想到套路:

  1. 抽屉插空问题
  2. 解决抽屉插空问题,需要罗列笛卡尔积。用17题的方法,最土的for循环。
class Solution:
    def generateParenthesis(self, n: int) -> List[str]:

        cases=[[[0],0]] # seq&cumsum
        for i in range(1,n):
            tmp=[]
            for case in cases:
                seq,cumsum=case
                for k in range(i-cumsum+1):
                    tmp.append([seq+[k],cumsum+k])
            cases=tmp

        res=[]
        for case in cases:
            seq,cumsum=case[0],case[1]
            one_case=''
            for i in seq:
                one_case+=')'*i
                one_case+='('
            one_case+=(')'*(n-cumsum))
            res.append(one_case)

        return set(res)

23. Merge k Sorted Lists

复用merge2

class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        if len(lists)==0:return None
        if len(lists)==1:return lists[0]
        def merge2(l1,l2):
            if l1 is None :
                return l2
            if l2 is None:
                return l1
            dummy1,dummy2=ListNode(),ListNode()
            dummy1.next,dummy2.next=l1,l2
            curr1,curr2=dummy1,dummy2.next
            while curr2 and curr1.next:
                if curr1.next.val>curr2.val:
                    tmp1,tmp2=curr1.next,curr2.next
                    curr1.next=curr2
                    curr2.next=tmp1
                    curr2=tmp2
                    curr1=curr1.next
                else:
                    curr1=curr1.next
            while curr1.next:
                curr1=curr1.next
            curr1.next=curr2
            return dummy1.next

        l1=lists[0]
        for i in range(1,len(lists)):
            l1=merge2(l1,lists[i])

        return l1     

24. Swap Nodes in Pairs

考点都是在前面出现过的,必须都玩熟了:

  1. 遇到链表,无脑转成带空头节点的链表
  2. curr.next and curr.next.next 来规避 curr.nextNone 的情况
class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        if head is None:
            return head
        node=ListNode()
        node.next=head
        curr=node
        while curr.next and curr.next.next:
            tmp1,tmp2=curr.next,curr.next.next
            tmp1.next=tmp2.next
            tmp2.next=tmp1
            curr.next=tmp2
            curr=tmp1
        return node.next

25. Reverse Nodes in k-Group

这个题就是两个逻辑的合并:取接下来的n个node,链表反转。

  1. 链表反转是一个基础考点。可以每次把最前面的节点放到后面。也可以每次把最后面的节点放到前面。
  2. 链表上的遍历,立即想到指针。
  3. 链表无脑转为带空头节点的链表
  4. 实际上可以一边向下遍历,一边反转,但这种解法与“不玩儿骚操作”的理念不符。作为替代,把“遍历找到下面k个”和“链表反转”作为两个部分分开写(尽管运行速度会慢一些)

您的支持将鼓励我继续创作!

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK