leetcode精选 TOP 面试题和前200道题(2)

  1. 两数之和

暴力法或者map一遍扫描

解法1:
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        length = len(nums)
        for i in range(length-1):
            for j in range(i+1, length):
                if nums[i] + nums[j] == target:
                    return [i, j]

解法2:
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        length = len(nums)
        Dict = {}
        for i in range(len(nums)):
            if nums[i] not in Dict:
                Dict[nums[i]] = i
            if target - nums[i] in Dict and i != Dict[target-nums[i]] :#注意不能是同一个元素
                return [i, Dict[target-nums[i]]]
  1. 两数相加

用链表来模拟加法。

# 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:
        high = 0
        head = ListNode(0)
        temp = head
        while(l1 or l2 or high):
            if l1:
                a = l1.val
            else:
                a = 0
            if l2:
                b = l2.val
            else:
                b = 0
            cur = (a + b + high) % 10
            high = (a + b + high) // 10
            temp.next = ListNode(cur)
            temp = temp.next
            if l1:
                l1 = l1.next
            if l2:
                l2 = l2.next
        return head.next
  1. 无重复字符的最长子串

双指针操作,模板题目。

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if not s:
            return 0
        window = {}
        left = 0
        right = 0
        max_len = 0
        while(right < len(s)):
            c = s[right]
            right += 1
            if c not in window:
                window[c] = 1
            else:
                window[c] += 1
            while(window[c] > 1):
                d = s[left]
                left += 1
                window[d] -= 1
            max_len = max(max_len, right-left)
        return max_len
  1. Z 字形变换

基本的字符串操作,需要找到相应的规律。

class Solution:
    def convert(self, s: str, numRows: int) -> str:
        if not s or len(s) == 1 or numRows == 1:
            return s
        res = ['']*numRows
        length = numRows*2 - 2
        i = 0 
        while(i < len(s)):
            index = i % length
            if index < numRows:
                res[index] += s[i]
            else:
                res[length-index] += s[i]
            i += 1
        return ''.join(res)
  1. 整数反转

需要考虑溢出的问题。这里的x是有符号数。

class Solution:
    def reverse(self, x: int) -> int:
        minNum = -2**31
        maxNum = 2**31 -1
        ans = 0
        flag = 0
        if x < 0:
            x = -x
            flag = 1
        while(x):
            ans = 10*ans + x %10
            x = x // 10
        if flag:
            ans = -ans
        if ans < minNum or ans > maxNum:
            return 0
        else:
            return ans
  1. 回文数

这个不用考虑数字的溢出,因为如果是回文数,那么一定不会溢出。

class Solution:
    def isPalindrome(self, x: int) -> bool:
        if x < 0:
            return False
        ans = 0
        temp = x
        while(x):
            ans = ans*10 + x % 10
            x = x // 10
        if ans == temp:
            return True
        else:
            return False
  1. 最长公共前缀

字符串的基本操作。

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        def lcp(str1, str2):
            length = min(len(str1), len(str2))
            i = 0
            while(i < length):
                if str1[i] == str2[i]:
                    i += 1
                else:
                    break
            if i== 0:
                return ''
            else:
                return str1[:i]
        if not strs:
            return ''
        temp = strs[0]
        for i in range(1, len(strs)):
            temp = lcp(strs[i], temp)
            if not temp:
                return ''
        return temp
  1. 三数之和

三指针操作,这题的重点是需要去除重复状态。首先需要先排序,去除一些极端情况。然后使用i,j,k三个指针,先定住i指针,对j,k指针进行处理,记得除去重复状态。然后对i指针也需要去除重复状态。

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        if not nums:
            return []
        nums.sort()
        if nums[0] > 0 or nums[-1] < 0 or len(nums) < 3:
            return []
        res = []
        for i in range(len(nums)-2):
            if nums[i] > 0:
                continue
            if i >=1 and nums[i] == nums[i-1]: #去除i的重复状态
                continue
            j = i + 1
            k = len(nums) - 1
            while(j < k):
                if nums[i] + nums[j] + nums[k] ==0:
                    res.append([nums[i], nums[j], nums[k]])
                    while(j < k and nums[k] == nums[k-1]):
                        k -= 1
                    while(j < k and nums[j] == nums[j+1]):
                        j += 1
                    k -= 1
                    j += 1
                elif nums[i] + nums[j] + nums[k] > 0:
                    k -= 1
                else:
                    j += 1
        return res
  1. 电话号码的字母组合

简单的遍历即可。

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        Dict = {
            '2':['a', 'b', 'c'],
            '3':['d', 'e', 'f'],
            '4':['g', 'h', 'i'],
            '5':['j', 'k', 'l'],
            '6':['m', 'n', 'o'],
            '7':['p', 'q', 'r', 's'],
            '8':['t', 'u', 'v'],
            '9':['w', 'x', 'y', 'z']
        }
        if digits == '':
            return []
        self.res = []
        def dfs(s, index, route):
            if index == len(s):
                self.res.append(route)
                return
            for c in Dict[s[index]]:
                dfs(s, index+1, route+c)
        dfs(digits, 0, '')
        return self.res
  1. 有效的括号

利用栈来判断括号是否合法。当前字符是左括号的时候就入栈,否者的话先判断一下栈是否是为空,为空就返回False,不然的话就出栈判断是否是对应的字符,最后栈为空的话也返回False。

class Solution:
    def isValid(self, s: str) -> bool:
        if not s:
            return True
        stack = []
        for c in s:
            if c == '(' or c == '{' or c == '[':
                stack.append(c)
            else:
                if not stack:
                    return False
                temp = stack.pop()
                if c == ')' and temp != '(':
                    return False
                if c == '}' and temp != '{':
                    return False
                if c == ']' and temp != '[':
                    return False
        if len(stack):
            return False
        return True
  1. 合并K个排序链表

归并排序或者是堆。

解法1:
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        if not lists:
            return None
        def recursion(lists, l, r):
            if l == r:
                return lists[l]
            mid = (l + r) // 2
            left = recursion(lists, l, mid)
            right = recursion(lists, mid+1, r)
            return merge(left, right)

        def merge(l1, l2):
            new_head = ListNode(0)
            temp = new_head
            while(l1 and l2):
                if l1.val < l2.val:
                    temp.next = l1
                    l1 = l1.next
                else:
                    temp.next = l2
                    l2 = l2.next
                temp = temp.next
            if l1:
                temp.next = l1
            if l2:
                temp.next = l2
            return new_head.next
        return recursion(lists, 0, len(lists)-1)

解法2:维护一个堆即可,堆为空的时候就终止。
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
from heapq import *

class Node:
    def __init__(self, node, val):
        self.node = node
        self.val = val

    def __lt__(self, other):
        if self.val < other.val:
            return True
        else:
            return False
        
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        if not lists:
            return None
        heap = []
        for temp in lists:
            if temp:
                newnode = Node(temp, temp.val)
                heappush(heap, newnode)
        head = ListNode(0)
        temp = head
        while(heap):
            l = heappop(heap).node
            if l.next:
                heappush(heap, Node(l.next, l.next.val))
            temp.next = l
            temp = temp.next
        return head.next
  1. K 个一组翻转链表

链表反转的升级版。这个题目用递归写起来比较好理解。

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

class Solution:
    def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
        if not head or not head.next:
            return head
        tail = head
        count = 0
        while(tail):
            count += 1
            if count == k:
                break
            tail = tail.next
        if count < k:
            return head
        tail = tail.next
        newhead = self.reverse(head, tail)
        head.next = self.reverseKGroup(tail, k)
        return newhead
    
    def reverse(self, head, tail):
        pre = None
        cur = head
        while(cur != tail):
            temp = cur.next
            cur.next= pre
            pre = cur
            cur = temp
        return pre
  1. 删除排序数组中的重复项

基本的双指针操作。

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        length = len(nums)
        i = 0
        j = 0
        while(i < length):
            while(i < length-1):
                if nums[i] == nums[i+1]:
                    i += 1
                else:
                    break
            nums[j] = nums[i]
            j += 1
            i += 1
        return j
  1. 移除元素

基本的双指针操作。

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        j = 0
        for i in range(len(nums)):
            if nums[i] != val:
                nums[j] = nums[i]
                j += 1
        return j
  1. 实现 strStr()

建议使用双指针。最优的是使用KMP算法,但是我不会。

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        if not needle:
            return 0
        length1 = len(haystack)
        length2 = len(needle)
        i = 0
        while(i < length1-length2+1):
            if haystack[i] == needle[0]:
                count = 1
                j = 1
                i += 1
                while(j< length2 and haystack[i]==needle[j]):
                    count+= 1
                    i += 1
                    j += 1
                if count == length2:
                    return i - count
                else:
                    i = i - count + 1
            else:
                i += 1
        return -1
  1. 下一个排列

从后往前找第一个跳变,如果一直到头都没有找到,那么说明该数组已经是最后一个了,直接反转即可,通过这个跳变将数组分为两个部分,后面的部分一定是逆序的,只有这样才能保证两个排列是紧挨在一起的。接下来的任务就是在后面的部分中找到第一个大于该跳变元素的元素,交换即可,这个时候后面还是逆序的,还需要将后面部分给反转一下。通过这种方式来保证新生成的排列和上一个排列是挨着的。这个题要求在原数组上进行修改,不能使用额外空间。

方法1:
class Solution:
    def nextPermutation(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        length = len(nums) - 1
        i = length
        while(i >= 1):
            if nums[i] > nums[i-1]:
                break
            else:
                i -= 1
        if i == 0:
            nums.reverse()
            return
        p = length
        while(p >= i):
            if nums[p] > nums[i-1]:
                break
            else:
                p -= 1
        nums[p], nums[i-1] = nums[i-1], nums[p]
        l = i
        r = length
        while(l < r):
            nums[l], nums[r] = nums[r], nums[l]
            l += 1
            r -= 1

方法二:在后面部分查找第一个大于目标的元素,这个可以采用二分来找。这个二分查找比较特殊,不需要查找和目标相等的值,需要找到第一个大于目标元素即可,并且一定存在某个元素大于该目标值。
class Solution:
    def nextPermutation(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        length = len(nums) - 1
        i = length
        while(i >= 1):
            if nums[i] > nums[i-1]:
                break
            else:
                i -= 1
        if i == 0:
            nums.reverse()
            return
        p = self.search(nums, i, length, nums[i-1])
        nums[p], nums[i-1] = nums[i-1], nums[p]
        l = i
        r = length
        while(l < r):
            nums[l], nums[r] = nums[r], nums[l]
            l += 1
            r -= 1
        
    def search(self, nums, l, r, target):
        while(l <= r):
            mid = (l + r) //2
            if nums[mid] > target:
                l = mid + 1
            else:
                r = mid - 1
        return r
  1. 搜索旋转排序数组

这是一个局部有序的数组,我们需要确定出哪一部分是完全有序的。这里我跟之前的写法有所区别,这里采用递归的写法。通过中间值和左边值的比较来确定出有序的范围。再在里面进行二分查找,而剩下的部分又是一个局部有序的数组,这样就变成了之前的子问题,因此可以采用递归的写法。

递归写法:
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        def search(nums, l, r, target):
            while(l <= r):
                mid = (l +r) // 2
                if nums[mid] == target:
                    return mid
                elif nums[mid] > target:
                    r = mid - 1
                else:
                    l = mid + 1
            return -1

        def search_v1(nums, target):
            def recursion(nums, l, r):
                if l > r:
                    return -1
                mid = (l + r) // 2
                if nums[mid] == target:
                    return mid
                if nums[mid] >= nums[l]:
                    if nums[mid] > target >= nums[l]:
                        return search(nums, l,  mid-1, target)
                    else:
                        return recursion(nums, mid+1, r)
                else:
                    if nums[mid] < target <= nums[r]:
                        return search(nums, mid+1, r, target)
                    else:
                        return recursion(nums, l, mid-1)
            return recursion(nums, 0, len(nums)-1)
        return search_v1(nums, target)

非递归写法:
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        l = 0
        r = len(nums) - 1
        while(l <= r):
            mid = (l + r) // 2
            if nums[mid] == target:
                return mid
            elif nums[mid] >= nums[l]:
                if nums[mid] > target >= nums[l]:
                    r = mid - 1
                else:
                    l = mid + 1
            else:
                if nums[mid] < target <= nums[r]:
                    l = mid + 1
                else:
                    r = mid - 1
        return -1

这里补充一下另一道进阶的题目,现在就是这个旋转数组里会存在重复元素值。arr = [5, 6, 7, 8, 5, 5, 5, 5, 5],这个时候nums[mid] >= nums[l]的话,但这一部分却不是局部有序的,需要对右边界进行一个收缩,其他的不存在这个情况。这一题是力扣81题。

class Solution:
    def search(self, nums, target) -> int:
        def search(nums, l, r, target):
            while(l <= r):
                mid = (l +r) // 2
                if nums[mid] == target:
                    return mid
                elif nums[mid] > target:
                    r = mid - 1
                else:
                    l = mid + 1
            return -1

        def search_v1(nums, target):
            def recursion(nums, l, r):
                if l > r:
                    return -1
                mid = (l + r) // 2
                if nums[mid] == target:
                    return mid
                if nums[mid] >= nums[l]:
                    temp = mid #这5行是对右边界的特殊处理。
                    while(temp > l and nums[temp] == nums[l]):
                            temp -= 1
                    if nums[temp] >= target >= nums[l]:
                        return search(nums, l,  temp, target)
                    else:
                        return recursion(nums, mid+1, r)
                else:
                    if nums[mid] < target <= nums[r]:
                        return search(nums, mid+1, r, target)
                    else:
                        return recursion(nums, l, mid-1)
            return recursion(nums, 0, len(nums)-1)
        if search_v1(nums, target) == -1:
            return False
        else:
            return True
  1. 搜索插入位置

二分

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        l = 0
        r = len(nums) - 1
        while(l <= r):
            mid = (l + r) // 2
            if nums[mid] == target:
                return mid
            elif nums[mid] > target:
                r = mid - 1
            else:
                l = mid + 1
        return l 
  1. 组合总和

要求所有的路径,因此需要用DFS。

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        res = []
        def dfs(index, sum, route, target):
            if sum > target:
                return
            if sum == target:
                res.append(route)
                return
            for i in range(index, len(candidates)):
                dfs(i, sum+candidates[i], route+[candidates[i]], target)
        dfs(0, 0, [], target)
        return res
  1. 组合总和 II

这里是需要取组合情况,每一个元素去多取一次。去组合的方式有两种写法可以使用二叉树的生长方式也可以使用多叉树的生长方式,其中多叉树的生长方式是不完全生长。如果元素中存在重复元素就需要考虑去重的问题,按照二叉树的生长是无法去重的。如果同一层有相同元素就需要去掉,所以最好的办法就是先排序,不然得把当前元素和前面所有的元素全部比较一遍。

解法1:我这里通过使用flags数组来标记,来表明是否是同层元素。
class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        res = []
        flags = [0]*len(candidates)
        candidates.sort()
        def dfs(route, sum, index):
            if sum > target:
                return
            if sum == target:
                res.append(route)
            for i in range(index, len(candidates)):
                if flags[i] == 0:
                    if i > 0 and candidates[i] == candidates[i-1] and flags[i-1] == 0:
                        continue
                    flags[i] = 1
                    dfs(route+[candidates[i]], sum+candidates[i], i+1)
                    flags[i] = 0
        dfs([], 0, 0)
        return res 

解法2:答案给了另一种比较好的方法
class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        res = []
        candidates.sort()
        def dfs(route, sum, index):
            if sum > target:
                return
            if sum == target:
                res.append(route)
            for i in range(index, len(candidates)):
                if i > index and candidates[i] == candidates[i-1]:#注意i > index
                    continue
                dfs(route+[candidates[i]], sum+candidates[i], i+1)
        dfs([], 0, 0)
        return res 
  1. 接雨水

单调栈的应用。

class Solution:
    def trap(self, height: List[int]) -> int:
        ans = 0
        stack = []
        for i in range(len(height)):
            while(stack and height[stack[-1]] < height[i]):
                mid = stack.pop()
                if not stack:
                    break
                left = stack[-1]
                width = i - left - 1
                length = min(height[left], height[i]) - height[mid]
                ans += width*length
            stack.append(i)
        return ans
  1. 跳跃游戏 II

DP,这里的k表示当前位置之前能够走到的最远位置,这里k的计算滞后于当前位置。k表示上一个位置能够的最远距离,k一定要大于或等于i。这个题有两种写法,一种是答案的写法,k的计算滞后于当前位置,就相当于k代表的是上一个位置能够走的最远距离。还有一种写法就是先计算k再来计算当前位置能够走的步数,但是这个写法不推荐,没有第一种方便。

class Solution:
    def jump(self, nums: List[int]) -> int:
        length = len(nums)
        dp = [0]*length
        k = 0
        for i in range(length):
            for j in range(k+1, min(i+nums[i]+1, length)):
                dp[j] = dp[i] + 1
            k = max(k, i+nums[i])
        return dp[-1]

class Solution:
    def jump(self, nums: List[int]) -> int:
        length = len(nums)
        dp = [0]*length
        k = nums[0]
        for i in range(1, min(k+1, length)):
            dp[i] = dp[0] + 1
        for i in range(1, length):
            k = max(k, i-1+nums[i-1])
            for j in range(k+1, min(i+nums[i]+1, length)):
                dp[j] = dp[i] + 1
        return dp[-1]
  1. 全排列 II

当前元素如果和同层的前一个元素相同则跳出,通过标记量来判断是否为同一层元素。

解法1:
class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        res = []
        flags = [0]*len(nums)
        def dfs(nums, route, count):
            if count == len(nums):
                res.append(route)
            for i in range(len(nums)):
                if flags[i] == 0:
                    #if i > 0 and nums[i] == nums[i-1] and flags[i-1]==0:
                    #    continue
                    if i > 0:
                        p = 0
                        temp = i-1
                        while(temp>=0):
                            if nums[i] == nums[temp] and flags[temp] == 0:
                                p = 1
                                break
                            else:
                                temp -= 1
                        if p:
                            continue
                    flags[i] = 1
                    dfs(nums, route+[nums[i]], count+1)
                    flags[i] = 0

        dfs(nums, [], 0)
        return res

解法2:可以先排序,这样就只需要判断相邻的元素是否相等了。
class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        res = []
        nums.sort()
        flags = [0]*len(nums)
        def dfs(nums, route, count):
            if count == len(nums):
                res.append(route)
            for i in range(len(nums)):
                if flags[i] == 0:
                    if i > 0 and nums[i] == nums[i-1] and flags[i-1]==0:
                        continue
                    flags[i] = 1
                    dfs(nums, route+[nums[i]], count+1)
                    flags[i] = 0

        dfs(nums, [], 0)
        return res
  1. Pow(x, n)

快速幂。需要考虑负数的情况。

class Solution:
    def myPow(self, x: float, n: int) -> float:
        ans = 1
        if n < 0:
            x = 1/x
            n = -n
        while(n):
            if n % 2 == 0:
                x = x*x
                n = n // 2
            else:
                ans = ans*x
                n -= 1
        return ans

  1. 最大子序和

基本DP。

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        length = len(nums)
        dp = [0]*length
        dp[0] = nums[0]
        for i in range(1, length):
            if dp[i-1] > 0:
                dp[i] = dp[i-1] + nums[i]
            else:
                dp[i] = nums[i]
        return max(dp)

状态压缩:
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        length = len(nums)
        temp = nums[0]
        res = nums[0]
        for i in range(1, length):
            if temp > 0:
                temp = temp + nums[i]
            else:
                temp = nums[i]
            res = max(res, temp)
        return res
  1. 螺旋矩阵

主要有递归和非递归两种写法,非递归是通过设置4个边界然后一起收缩,而递归写法则是通过DFS来完成路径的模拟。

非递归:
class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        if not matrix:
            return []
        t = 0
        b = len(matrix) - 1
        l = 0
        r = len(matrix[0]) - 1
        res = []
        while(True):
            for i in range(l, r+1):
                res.append(matrix[t][i])
            t += 1
            if t > b:
                break
            for i in range(t, b+1):
                res.append(matrix[i][r])
            r -= 1
            if l > r:
                break
            for i in range(r, l - 1, -1):
                res.append(matrix[b][i])
            b -= 1
            if t > b:
                break
            for i in range(b, t-1, -1):
                res.append(matrix[i][l])
            l += 1
            if l > r:
                break
        return res


递归写法待定。
  1. 最后一个单词的长度

字符串的基本操作。

解法1:
class Solution:
    def lengthOfLastWord(self, s: str) -> int:
        words = s.split()
        if not words:
            return 0
        else:
            return len(words[-1])

解法2:
class Solution:
    def lengthOfLastWord(self, s: str) -> int:
        if not s:
            return 0
        count = 0
        for i in range(len(s)-1, -1, -1):
            if s[i] != ' ':
                count += 1
            elif count and s[i] == ' ':
                return count
        return count
  1. 螺旋矩阵 II

四个方向收缩即可。这个题如果不是方形也可以这样做。

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        t = 0
        b = n - 1
        l = 0
        r = n - 1
        num = 1
        data = [[0]*n for _ in range(n)]
        while(True):
            for i in range(l, r+1):
                data[t][i] =num
                num += 1
            t += 1
            if t > b:
                break
            for i in range(t, b+1):
                data[i][r] = num
                num += 1
            r -= 1
            if l > r:
                break
            for i in range(r, l-1, -1):
                data[b][i] = num
                num += 1
            b -= 1
            if t > b:
                break
            for i in range(b, t-1, -1):
                data[i][l] = num
                num += 1
            l += 1
            if l > r:
                break
        return data
  1. 第k个排列

简单的DFS,这个题直接用DFS会超时,剪枝后不会超时,但是python会超时。由于这个题只是让我们求第k个排列,中间的过程不需要我们求,因此可以直接用数学公式将其求出来。

解法1:
class Solution:
    def getPermutation(self, n: int, k: int) -> str:
        flags = [0]*(n+1)
        res = []
        self.end = 0
        def dfs(route, count):
            if count == n:
                self.end += 1
                if self.end == k:
                    res.append(route)
                return
            if self.end == k:
                return
            for i in range(1, n+1):
                if flags[i] == 0:
                    flags[i] = 1
                    dfs(route+str(i), count+1)
                    flags[i] = 0
        dfs('', 0)
        return res[0]

解法2:
class Solution:
    def getPermutation(self, n: int, k: int) -> str:
        frac = [i for i in range(0, n+1)]
        frac[0] = 1
        candidate = [i for i in range(1, n+1)]
        for i in range(2, n+1):
            frac[i] = frac[i]*frac[i-1]
        res = []
        k -= 1 #这个是整个代码的关键
        for i in range(n-1, -1, -1):
            index = k // frac[i]
            res.append(candidate[index])
            del candidate[index]
            k = k - index*frac[i]
        return ''.join(list(map(str, res)))

  1. 旋转链表

需要考虑k值会大于链表的长度,还需要考虑我们要反转的节点是空的情况。

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

class Solution:
    def rotateRight(self, head: ListNode, k: int) -> ListNode:
        if not head:
            return head
        l = head
        count = 0
        while(l):
            count += 1
            l = l.next
        k = k % count
        index = 0
        l = head
        temp = None
        while(l):
            index += 1
            if index == count - k:
                temp = l
                break
            l = l.next
        newhead = temp.next
        temp.next = None
        l = newhead
        if not l:
            return head
        while(l.next):
            l = l.next
        l.next = head
        return newhead
  1. 最小路径和

简单的DP问题,注意初始化。

定义一个二维的DP数组。
class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:
        length = len(grid)
        width = len(grid[0])
        dp = [[0]*width for _ in range(length)]
        dp[0][0] = grid[0][0]
        for i in range(1, length):
            dp[i][0] = dp[i-1][0] + grid[i][0]
        for j in range(1, width):
            dp[0][j] = dp[0][j-1] + grid[0][j]
        for i in range(1, length):
            for j in range(1, width):
                dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
        return dp[-1][-1]

原地修改,空间复杂度O(1)
class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:
        length = len(grid)
        width = len(grid[0])
        for i in range(1, length):
            grid[i][0] = grid[i-1][0] + grid[i][0]
        for j in range(1, width):
            grid[0][j] = grid[0][j-1] + grid[0][j]
        for i in range(1, length):
            for j in range(1, width):
                grid[i][j] = min(grid[i-1][j], grid[i][j-1]) + grid[i][j]
        return grid[-1][-1]

将二维的DP数组压缩成一维的,因为结合这个问题,中间的状态是不需要保存的,只需要最终的结果。用一个数组保存每一行的结果,然后算出新的结果后,直接覆盖即可。
class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:
        length = len(grid)
        width = len(grid[0])
        
        dp = [0]*width
        dp[0] = grid[0][0]
        for j in range(1, width):
            dp[j] = dp[j-1] + grid[0][j]
        for i in range(1, length):
            for j in range(width):
                if j == 0:
                    dp[j] = dp[j] + grid[i][j]
                else:
                    dp[j] = min(dp[j-1], dp[j]) + grid[i][j]
        return dp[-1]

  1. 加一

数组的基本操作。

class Solution:
    def plusOne(self, digits: List[int]) -> List[int]:
        high = 1
        for i in range(len(digits)-1, -1, -1):
            temp = (digits[i] + high) 
            digits[i] = temp % 10
            high = temp // 10
        if digits[0] == 0:
            digits.insert(0, 1)
        return digits
  1. 二进制求和

模拟加法。这个题与两数相加非常类似。

class Solution:
    def addBinary(self, a: str, b: str) -> str:
        res = ['']*(max(len(a), len(b)) + 1)
        i = len(a) - 1
        j = len(b) - 1
        k = len(res) - 1
        high = 0
        while(i >= 0 or j >= 0):
            if i < 0:
                temp1 = 0
            else:
                temp1 = int(a[i])
            if j < 0:
                temp2 = 0
            else:
                temp2 = int(b[j])
            temp = temp1 + temp2 + high
            res[k] = str(temp%2)
            k -= 1
            high = temp // 2
            if i >= 0:
                i -= 1
            if j >= 0:
                j -= 1
        if high:
            res[0] = '1'
        return ''.join(res)
  1. x 的平方根

简单的二分操作。

class Solution:
    def mySqrt(self, x: int) -> int:
        l = 0
        r = x
        while(l <= r):
            mid = (l+r) // 2
            if mid*mid == x:
                return mid
            elif mid*mid > x:
                r = mid - 1
            else:
                l = mid + 1
        return l-1
  1. 爬楼梯

基本的DP

解法1:
class Solution:
    def climbStairs(self, n: int) -> int:
        if n == 1:
            return 1
        dp = [0]*(n+1)
        dp[1] = 1
        dp[2] = 2
        for i in range(3, n+1):
            dp[i] = dp[i-1] + dp[i-2]
        return dp[n]

解法2:
> 状态压缩,只需要最终的结果,不需要保留中间的状态
class Solution:
    def climbStairs(self, n: int) -> int:
        if n == 1:
            return 1
        a = 1
        b = 2
        c = 2
        for i in range(3, n+1):
            c = a + b
            a = b
            b = c
        return c
  1. 编辑距离

经典DP问题。需要注意边界条件的处理。

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        length = len(word1)
        width = len(word2)

        dp = [[0]*(width+1) for _ in range(length+1)]
    
        for j in range(1, width+1):
            dp[0][j] = dp[0][j-1] + 1
        for i in range(1, length+1):
            dp[i][0] = dp[i-1][0] + 1
        for i in range(1, length+1):
            for j in range(1, width+1):
                if word1[i-1] == word2[j-1]:
                    dp[i][j] = dp[i-1][j-1]
                else:
                    dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1
        return dp[-1][-1]

状态压缩:
class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        length = len(word1) + 1
        width = len(word2) + 1
        dp = [i for i in range(width)]
        for i in range(1, length):
            pre = i-1
            dp[0] = i
            for j in range(1, width):
                tmp = dp[j]
                if word1[i-1] == word2[j-1]:
                    dp[j] = pre
                else:
                    dp[j] = min(min(dp[j-1], dp[j]), pre) + 1
                pre = tmp           
        return dp[-1]
  1. 矩阵置零

这题需要原地修改,如果限制空间复杂度就很麻烦了。需要单独对最左边的一列和最上面的一行进行处理。还需要额外定义两个变量来表示左边的一列和最上面的一行是否存在0。

class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        length = len(matrix)
        width = len(matrix[0])
        r = 0
        c = 0
        for j in range(width):
            if matrix[0][j] == 0:
                r = 1
                break
        for i in range(length):
            if matrix[i][0] == 0:
                c = 1
                break
        for i in range(1, length):
            for j in range(1, width):
                if matrix[i][j] == 0:
                    matrix[0][j] = 0
                    matrix[i][0] = 0        
        for i in range(1, length):
            if matrix[i][0] == 0:
                for j in range(1, width):
                    matrix[i][j] = 0
        for j in range(1, width):
            if matrix[0][j] == 0:
                for i in range(1, length):
                    matrix[i][j] = 0
        if (r == 1 and c == 1):
            for i in range(length):
                matrix[i][0] = 0
            for j in range(width):
                matrix[0][j] = 0
        elif r == 1:
            for j in range(width):
                matrix[0][j] = 0
        elif c == 1:
            for i in range(length):
                matrix[i][0] = 0
  1. 颜色分类

这个问题的限制条件比较多,是一个三指针问题。

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        i = 0
        pos = 0
        j = len(nums) - 1
        while(pos <= j): #一定要有等于,可以分类讨论一下
            if nums[pos] == 0:
                nums[i], nums[pos] = nums[pos], nums[i]
                i += 1
                pos += 1 #当前位置需要移动,这里也需要分类讨论一下。
            elif nums[pos] == 1:
                pos += 1
            else:
                nums[j], nums[pos] = nums[pos], nums[j]
                j -= 1
  1. 组合

按照多叉树的不完全生长来取组合。

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        res = []
        def dfs(index, route, count):
            if count == k:
                res.append(route)
            for i in range(index, n+1):
                dfs(i+1, route+[i], count+1)

        dfs(1, [], 0)
        return res
  1. 搜索旋转排序数组 II

数组中会出现重复元素。

递归写法:
class Solution:
    def search(self, nums: List[int], target: int) -> bool:
        def search(nums, l, r, target):
            while(l <= r):
                mid = (l +r) // 2
                if nums[mid] == target:
                    return mid
                elif nums[mid] > target:
                    r = mid - 1
                else:
                    l = mid + 1
            return -1

        def search_v1(nums, target):
            def recursion(nums, l, r):
                if l > r:
                    return -1
                mid = (l + r) // 2
                if nums[mid] == target:
                    return mid
                if nums[mid] >= nums[l]:
                    temp = mid #这5行是对右边界的特殊处理。
                    while(temp > l and nums[temp] == nums[l]):
                            temp -= 1
                    if nums[temp] >= target >= nums[l]:
                        return search(nums, l,  temp, target)
                    else:
                        return recursion(nums, mid+1, r)
                else:
                    if nums[mid] < target <= nums[r]:
                        return search(nums, mid+1, r, target)
                    else:
                        return recursion(nums, l, mid-1)
            return recursion(nums, 0, len(nums)-1)
        if search_v1(nums, target) == -1:
            return False
        else:
            return True

非递归写法:
class Solution:
    def search(self, nums, target) -> int:
        l = 0
        r = len(nums) - 1
        count = 0
        while(l <= r):
            mid = (l + r) // 2
            if nums[mid] == target:
                return True
            elif nums[mid] >= nums[l]:
                temp = mid
                while(temp > l):
                    if (nums[temp] == nums[l]):
                        temp -= 1
                    else:
                        break
                if nums[l] <= target <= nums[temp]:
                    r = temp
                else:
                    l = mid + 1
            else:
                if nums[r] >= target > nums[mid]:
                    l = mid + 1
                else:
                    r = mid - 1
        return False
  1. 删除排序链表中的重复元素 II

这个题用递归写法比较方便。

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

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        cur = head
        if cur.val != cur.next.val:
            temp = self.deleteDuplicates(cur.next)
            cur.next = temp
            return cur
        else:
            while(cur.next and cur.val == cur.next.val):
                cur = cur.next
            return self.deleteDuplicates(cur.next)
  1. 删除排序链表中的重复元素

基本的链表操作,删除一个节点后相当于链表往后移动一位。

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

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        l1 = head
        while(l1 and l1.next):
            a = l1.val
            b = l1.next.val
            if a == b:
                l1.next = l1.next.next
            else:
                l1 = l1.next
        return head
  1. 柱状图中最大的矩形

单调栈的一个典型应用。

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        heights.append(0)
        stack = []
        ans = 0
        for i in range(len(heights)):
            while(stack and heights[stack[-1]] > heights[i]):
                mid = stack.pop()
                if not stack:
                    width = i #一定要注意这一步
                else:
                    left = stack[-1]
                    width = i - left - 1
                ans = max(ans, width*heights[mid])
            stack.append(i)
        return ans
  1. 合并两个有序数组

跟这个类似的有合并两个有序链表,在写归并排序的时候也需要合并两个有序的数组。但是这一题给出了一定的限制条件,其中一个数组的长度足够长,并且合并的时候不能开辟额外的空间。这里需要重后往前合并,每次找出较大的一个数放在后面。其余的写法和合并链表一样。

class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
        k = m + n - 1
        m = m-1
        n = n-1
        while(m >= 0 and n >= 0):
            if nums1[m] > nums2[n]:
                nums1[k] = nums1[m]
                m -= 1
            else:
                nums1[k] = nums2[n]
                n -= 1
            k -= 1
        if n >= 0: #m有剩余的话就不用考虑了
            while(n >= 0):
                nums1[k] = nums2[n]
                n -= 1
                k -= 1
  1. 子集 II

如果按照二叉树的生长形式是无法去重的,需要按照多叉树的不完全生长方式来去重复。

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        res = []
        nums.sort()
        length = len(nums)
        def dfs(index, route):
            res.append(route)
            for i in range(index, length):
                if i > index and nums[i] == nums[i-1]:
                    continue
                dfs(i+1, route+[nums[i]])
        dfs(0, [])
        return res
  1. 复原IP地址

对于当前字符串,最多切4次,每次切的长度不超过3(需要和当前字符串的长度做一个比较0),需要额外写两个函数来保证切出来的字符串部分在0-255之间,并且不能出现00这种形式的字符串。

class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        res = []
        def string2int(s):
            sum = 0
            for c in s:
                sum = sum *10 + int(c)
            return sum

        def judge(s):
            if len(s) > 1:
                if s[0] == '0':
                    return False
            return True

        def dfs(s, route, count):
            if count == 4:
                if not s:
                    res.append('.'.join(route))
                return
            for i in range(min(3, len(s))):
                if 0 <= string2int(s[:i+1]) <= 255 and judge(s[:i+1]):
                    dfs(s[i+1:], route+[s[:i+1]], count+1) 

        dfs(s, [], 0)
        return res
  1. 二叉树的中序遍历

非递归写法。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        stack = []
        cur = root
        res = []
        while(cur or stack):
            while(cur):
                stack.append(cur)
                cur = cur.left
            temp = stack.pop()
            res.append(temp.val)
            cur = temp.right
        return res
  1. 不同的二叉搜索树

基本DP。

class Solution:
    def numTrees(self, n: int) -> int:
        dp = [0]*(n+1)
        dp[0] = 1
        for i in range(1, n+1):
            for j in range(i):
                dp[i] += dp[j]*dp[i-1-j]
        return dp[-1]
  1. 验证二叉搜索树

二叉搜索树的每一个节点都有一个范围。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        minNum = -inf
        maxNum = inf
        def judge(root, minNum, maxNum):
            if not root:
                return True
            if root.val <= minNum or root.val >= maxNum:
                return False
            return judge(root.left, minNum, root.val) and judge(root.right, root.val, maxNum)
        return judge(root, minNum, maxNum)
  1. 相同的树

简单的递归函数。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        def judge(p, q):
            if not p and not q:
                return True
            if not p:
                return False
            if not q:
                return False
            if p.val != q.val:
                return False
            return judge(p.left, q.left) and judge(p.right, q.right)
        return judge(p, q)
  1. 对称二叉树

BFS和DFS两种办法。

# BFS判断每一层是否对称
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        if not root:
            return True
        queue = [root]
        while(queue):
            length = len(queue)
            res = []
            for _ in range(length):
                temp = queue.pop(0)
                if temp:
                    res.append(temp.val)
                else:
                    res.append('null')
                if temp:
                    queue.append(temp.left)
                    queue.append(temp.right)
            res1 = res[:]
            res.reverse()
            if res1 != res:
                return False
        return True

# DFS判断每两个节点是否相同。
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        def judge(a, b):
            if not a and not b:
                return True
            if not a:
                return False
            if not b:
                return False
            if a.val != b.val:
                return False
            return judge(a.left, b.right) and judge(a.right, b.left)

        if not root:
            return True
        return judge(root.left, root.right)
  1. 二叉树的锯齿形层次遍历

简单的程序遍历,需要控制每一层的宽度和每一层在树中的深度。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
        if not root:
            return []
        res = []
        queue = [root]
        depth = 1
        while(queue):
            length = len(queue)
            ans = []
            for _ in range(length):
                temp = queue.pop(0)
                ans.append(temp.val)
                if temp.left:
                    queue.append(temp.left)
                if temp.right:
                    queue.append(temp.right)
            if depth % 2 == 0:
                ans.reverse()
            res.append(ans)
            depth += 1
        return res      
  1. 三角形最小路径和

入门DP,可以使用状态压缩。

解法1:
class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        length = len(triangle)
        dp = [[0]*length for _ in range(length)]
        dp[0][0] = triangle[0][0]
        for i in range(1, length):
            dp[i][i] = dp[i-1][i-1] + triangle[i][i]
        for i in range(1, length):
            dp[i][0] = dp[i-1][0] + triangle[i][0]
        for i in range(1, length):
            for j in range(1, i):
                dp[i][j] = min(dp[i-1][j], dp[i-1][j-1]) + triangle[i][j]
        return min(dp[-1])

解法2:状态压缩,注意是反向状态压缩。
class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        length = len(triangle)
        dp = [0]*length 
        dp[0] = triangle[0][0]
        for i in range(1, length):
            for j in range(i, -1, -1):
                if j == i:
                    dp[j] = dp[j-1] + triangle[i][j]
                elif j == 0:
                    dp[j] = dp[j] + triangle[i][j]
                else:
                    dp[j] = min(dp[j], dp[j-1]) + triangle[i][j]
        return min(dp)
  1. 买卖股票的最佳时机

入门DP问题。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if not prices:
            return 0
        length = len(prices)
        dp = [0]*length
        for i in range(1, length):
            if dp[i-1] > 0:
                dp[i] = dp[i-1] + prices[i] - prices[i-1]
            else:
                dp[i] = prices[i] - prices[i-1]
        return max(dp)
  1. 分发糖果

这个题不是很好想,正常的思路是找到分数最低的一个人,然后给一个糖果,之后在向两边扩展,但是这个操作起来比较困难,并且也不能保证最优解。正确的做法是,给第一个人一个糖果,从左往右扫描,分数比前一个人高的话,就在前一个人的基础上多加一个糖果,否则只给一个糖果;再从后往前扫描,如果前一个人的分数更高且这个人的糖果数没有高于后一个人,那么在后一个人的基础上加一。

class Solution:
    def candy(self, ratings: List[int]) -> int:
        length = len(ratings)
        nums = [0]*length
        nums[0] = 1
        for i in range(1, length):
            if ratings[i] > ratings[i-1]:
                nums[i] = nums[i-1] + 1
            else:
                nums[i] = 1
        for i in range(length-2, -1, -1):
            if ratings[i] > ratings[i+1] and nums[i] <= nums[i+1]:
                nums[i] = nums[i+1] + 1
        return sum(nums) 
  1. 复制带随机指针的链表

深拷贝操作。这个题目的正常思路先复制每一个节点,然后再来处理随机指针的赋值的问题。但现在的主要问题是节点复制之后无法知道随机指针的指向,现在有两种办法,一个用字典来存储节点复制前和复制后的关联信息,还有一个是将原始链表扩充成原来的两倍,然后再拆分成两条链表。

解法1:
"""
# Definition for a Node.
class Node:
    def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
        self.val = int(x)
        self.next = next
        self.random = random
"""

class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        if not head:
            return None
        Dict = {None:None} #处理节点为空的情况
        temp = head
        while(temp):
            node = Node(temp.val)
            Dict[temp] = node
            temp = temp.next
        temp = head
        while(temp):
            Dict[temp].next = Dict[temp.next]
            Dict[temp].random = Dict[temp.random]
            temp = temp.next
        return Dict[head]

解法2:
"""
# Definition for a Node.
class Node:
    def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
        self.val = int(x)
        self.next = next
        self.random = random
"""

class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        if not head:
            return None
        temp = head
        while(temp):
            node = Node(temp.val)
            node.next = temp.next
            temp.next = node
            temp = temp.next.next
        temp = head
        while(temp):
            if temp.random:
                temp.next.random = temp.random.next #注意random指针可能为空的情况
            temp = temp.next.next
        newhead = head.next
        l = newhead
        temp = head.next.next
        head.next = head.next.next
        while(temp):
            l.next = temp.next
            l = l.next
            temp.next = temp.next.next
            temp = temp.next
        return newhead
  1. 单词拆分

简单的DFS+剪枝

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        def dfs(s, Dict):
            if not s:
                return True
            if s in Dict:
                return Dict[s]
            for i in range(len(s)):
                if s[:i+1] in wordDict:
                    res = dfs(s[i+1:], Dict)
                    if res:
                        Dict[s] = True
                        return Dict[s]
            Dict[s] = False
            return Dict[s]
        return dfs(s, {})
  1. 单词拆分 II

这个题要比上一个麻烦一点,需要记录单词切分的路径。需要去除不可切分的状态。

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> List[str]:
        res = []
        def dfs(s, route, Dict):
            if not s:
                res.append(" ".join(route))
                return True
            if s in Dict:
                if not Dict[s]:
                    return False
            flag = 0
            for i in range(len(s)):
                if s[:i+1] in wordDict:
                    ans = dfs(s[i+1:], route+[s[:i+1]], Dict)
                    if ans:
                        flag = 1
            if flag == 0:
                Dict[s] = False
            else:
                Dict[s] = True
            return Dict[s]
        dfs(s, [], {})
        return res
  1. 环形链表

快慢指针。

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

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        if not head or not head.next:
            return False
        slow = head
        fast = head.next
        while(fast and fast.next):
            if slow == fast:
                return True
            slow = slow.next
            fast = fast.next.next
        return False
  1. 环形链表 II

快慢指针找环的入口。

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

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return None
        flag = 0
        slow = head
        fast = head.next
        while(fast and fast.next):
            if slow == fast:
                flag = 1
                break
            slow = slow.next
            fast = fast.next.next
        if not flag:
            return None
        else:
            fast = None
            while(fast != slow):
                if not fast:
                    fast = head
                else:
                    fast = fast.next
                slow = slow.next
            return slow
  1. 二叉树的前序遍历

需要使用迭代算法。二叉树的迭代遍历一共四种情况。前序、中序、后序、层序。
参考:https://leetcode-cn.com/problems/binary-tree-preorder-traversal/solution/tu-jie-er-cha-shu-de-si-chong-bian-li-by-z1m/

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        cur = root
        stack = []
        res = []
        while(cur or stack):
            while(cur):
                stack.append(cur)
                res.append(cur.val)
                cur = cur.left
            temp = stack.pop()
            cur = temp.right
        return res
  1. 二叉树的后序遍历

同样需要使用迭代写法。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def postorderTraversal(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        cur = root
        stack = []
        res = []
        while(cur or stack):
            while(cur):
                stack.append(cur)
                cur = cur.left
            temp = stack[-1]
            if not temp.right:
                res.append(stack.pop().val)
            else:
                cur = temp.right
                temp.right = None
        return res
  1. 最小栈

正常的栈是无法实现这种功能的,因此需要使用额外的空间来存储最小值。

class MinStack:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack_A = []
        self.stack_B = []

    def push(self, x: int) -> None:
        self.stack_A.append(x)
        if not self.stack_B:
            self.stack_B.append(x)
        else:
            if x <= self.stack_B[-1]:
                self.stack_B.append(x)

    def pop(self) -> None:
        temp = self.stack_A.pop()
        if temp == self.stack_B[-1]:
            self.stack_B.pop()

    def top(self) -> int:
        return self.stack_A[-1]

    def getMin(self) -> int:
        return self.stack_B[-1]



# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()
  1. 相交链表

比较笨的那种方法我就不说了,当一个链表先走到最后位置的时候直接跳转到另一个链表上,如果两个链表没有交点,那么这两个链表都会走到最后的空节点来结束程序。

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

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        l1 = headA
        l2 = headB
        while(l1 != l2):
            if l1:
                l1 = l1.next
            else:
                l1 = headB
            if l2:
                l2 = l2.next
            else:
                l2 = headA
        return l1
  1. 阶乘后的零

找5和2的个数,5的个数较少,再找5的个数,不停的除以5即可

class Solution:
    def trailingZeroes(self, n: int) -> int:
        res = 0
        while(n):
            res += n // 5
            n = n //5
        return res
  1. 最大数

由于这个题中的每一个数字存在多位数,无法通过先排序再用DFS的方式来去重,使用DFS的时间复杂度就是N!,只能通过特殊的排序方式来解决这个问题。cmp_to_key在functools这个包里。

class Solution:
    def largestNumber(self, nums: List[int]) -> str:
        nums = list(map(str, nums))
        def compare(x, y):
            if x+y < y+x:
                return 1
            elif x+y == y+x:
                return 0
            else:
                return -1
        nums.sort(key = cmp_to_key(compare))
        res =  ''.join(nums)
        if len(res) > 1 and res[0] == '0':
            return '0'
        return res
  1. 旋转数组

数组的基本操作。

解法1:每次只移动一位,从后往前移动,还需要额外定义一个变量。
class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        length = len(nums)
        k = k % length
        for _ in range(k):
            temp = nums[-1]
            for j in range(length-1, 0, -1):
                nums[j] = nums[j-1]
            nums[0] = temp

解法2:用双指针进行反转
class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        k = k % len(nums)
        def rota(nums, i , j):
            while(i < j):
                nums[i], nums[j] = nums[j], nums[i]
                i += 1
                j -= 1
            
        rota(nums, 0, len(nums)-1)
        rota(nums, 0, k - 1)
        rota(nums, k, len(nums)-1)

  1. 打家劫舍

基本DP。

class Solution:
    def rob(self, nums: List[int]) -> int:
        length = len(nums)
        if length < 1:
            return 0
        if length < 2:
            return nums[0]
        dp = [0]*length
        dp[0] = nums[0]
        dp[1] = max(nums[1], nums[0])#要注意这一个操作。
        for i in range(2, length):
            dp[i] = max(dp[i-1], nums[i]+dp[i-2])
        return max(dp)
  1. 计数质数

素数筛。

class Solution:
    def countPrimes(self, n: int) -> int:
        flags = [0]*n
        for i in range(2, n):
            if flags[i] == 0:
                for j in range(2, n):
                    if i*j > n-1:
                        break
                    flags[i*j] = 1
        count = 0
        for i in range(2, n):
            if flags[i] == 0:
                count += 1
        return count
  1. 存在重复元素

排序,字典都可以。

class Solution:
    def containsDuplicate(self, nums: List[int]) -> bool:
        return len(set(nums)) < len(nums)
  1. 用队列实现栈

与这个类似的有用两个栈实现一个队列,用两个队列实现一个栈。用一个队列实现栈。用两个队列实现栈的时候需要取队列的长度,python中len()操作的时间复杂度为O(1)。

用两个队列:
class MyStack:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.queue_A = []
        self.queue_B = []


    def push(self, x: int) -> None:
        """
        Push element x onto stack.
        """
        self.queue_A.append(x)


    def pop(self) -> int:
        """
        Removes the element on top of the stack and returns that element.
        """
        while(len(self.queue_A) > 1):
            self.queue_B.append(self.queue_A.pop(0))
        res = self.queue_A.pop(0)
        while(self.queue_B):
            self.queue_A.append(self.queue_B.pop(0))
        return res


    def top(self) -> int:
        """
        Get the top element.
        """
        while(len(self.queue_A) > 1):
            self.queue_B.append(self.queue_A.pop(0))
        res = self.queue_A.pop(0)
        self.queue_B.append(res)
        while(self.queue_B):
            self.queue_A.append(self.queue_B.pop(0))
        return res


    def empty(self) -> bool:
        """
        Returns whether the stack is empty.
        """
        if self.queue_A:
            return False
        else:
            return True



# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()

用一个队列:将前面的n-1个数全部反转。
class MyStack:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.queue_A = []


    def push(self, x: int) -> None:
        """
        Push element x onto stack.
        """
        self.queue_A.append(x)
        length = len(self.queue_A)
        i = 0
        while(i < length -1):
            self.queue_A.append(self.queue_A.pop(0))
            i += 1


    def pop(self) -> int:
        """
        Removes the element on top of the stack and returns that element.
        """
        return self.queue_A.pop(0)


    def top(self) -> int:
        """
        Get the top element.
        """
        return self.queue_A[0]


    def empty(self) -> bool:
        """
        Returns whether the stack is empty.
        """
        if self.queue_A:
            return False
        else:
            return True



# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()
  1. 滑动窗口最大值

需要维护一个单调不增双端队列,需要双端的原因是因为滑动窗口在移动的时候两端都会有信息更新,单调不增的原因是因为要防止较小的数字丢失,由于这里是要取最大值,当遇到较大的元素,比这个小的元素都可以出队列。
这个题的核心代码就7行。

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        queue = []
        res = []
        for i in range(k):
            if not queue or queue[-1] >= nums[i]:
                queue.append(nums[i])
            else:
                while(queue and queue[-1]< nums[i]):
                    queue.pop()
                queue.append(nums[i])
        res.append(queue[0])
        for i in range(k, len(nums)):
            if not queue or queue[-1] >= nums[i]:
                queue.append(nums[i])
            else:
                while(queue and queue[-1]< nums[i]):
                    queue.pop()
                queue.append(nums[i])
            if queue[0] == nums[i-k]:
                queue.pop(0)
            res.append(queue[0])
        return res
            
  1. 最长上升子序列

经典DP问题,如果需要找到所有的最优解只能用DP,但是DP的时间复杂度为O(N2),但是如果说只需要找到最长上升子序列的长度的话,就只需要利用贪心和二分维护一个递增列表,最终返回这个列表的长度即可。

解法1:
class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if not nums:
            return 0
        length = len(nums)
        dp = [1]*length
        for i in range(1, length):
            for j in range(i):
                if nums[i] > nums[j]:
                    dp[i] = max(dp[i], dp[j]+1) #每一种情况都需要考虑所以要取最大值。
        return max(dp)

解法2:这里的二分需要说明一下,这里需要维护一个递增的列表,但是原数组会出现重复元素的情况,因此会有当前元素等于递增列表中的某一个元素的情况,这个时候返回该元素的下标,就相当于没有进行修改。但是递增的列表中是没有重复元素的,并且递增的列表中一定存在元素大于或等于当前元素。
class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if not nums:
            return 0
        temp = []
        
        def search(nums, target):
            l = 0
            r = len(nums) - 1
            while(l <= r):
                mid = (l +r) // 2
                if nums[mid] < target:
                    l = mid + 1
                elif nums[mid] > target:
                    r = mid - 1
                else:
                    return mid
            return l 
        
        for num in nums:
            if not temp or temp[-1] < num:
                temp.append(num)
            else:
                cur = search(temp, num)
                temp[cur] = num
        return len(temp)

这里补充一下最长上升子序列的一个进阶题,现在是二维的,我们先要先对第一维度按照升序排列,这个时候我们就只需要对第二维度求一个最长上升子序列即可。有一个需要注意的是,如果第一维度相同,那么第二维度按照降序来,这个非常重要,不然会出错。其他的就没啥了,力扣354就是这样的一个题。

class Solution:
    def maxEnvelopes(self, envelopes: List[List[int]]) -> int:
        if not envelopes:
            return 0
        
        def compare(x, y):
            if x[0] > y[0] or (x[0] == y[0] and x[1] < y[1]):
                return 1
            elif x[0] == y[0] and x[1] == y[1]:
                return 0
            else:
                return -1
        
        def search(nums, target):
            l = 0
            r = len(nums)-1
            while(l <= r):
                mid = (l+r) // 2
                if nums[mid] == target:
                    return mid
                elif nums[mid] > target:
                    r = mid - 1
                else:
                    l = mid + 1
            return l
    
        envelopes.sort(key = cmp_to_key(compare))
        print(envelopes)
        temp = []
        for env in envelopes:
            print(env)
            if not temp or env[1] > temp[-1]:
                temp.append(env[1])
            else:
                cur = search(temp, env[1])
                temp[cur] = env[1]
        return len(temp)

第二种写法
class Solution:
    def maxEnvelopes(self, envelopes: List[List[int]]) -> int:
        if not envelopes:
            return 0
        
        def search(nums, target):
            l = 0
            r = len(nums)-1
            while(l <= r):
                mid = (l+r) // 2
                if nums[mid] == target:
                    return mid
                elif nums[mid] > target:
                    r = mid - 1
                else:
                    l = mid + 1
            return l
    
        envelopes.sort(key = lambda x: (x[0], -x[1]))
        temp = []
        for env in envelopes:
            print(env)
            if not temp or env[1] > temp[-1]:
                temp.append(env[1])
            else:
                cur = search(temp, env[1])
                temp[cur] = env[1]
        return len(temp)
  1. 戳气球

经典DP。

class Solution:
    def maxCoins(self, nums: List[int]) -> int:
        nums.insert(0, 1)
        nums.append(1)
        length = len(nums)
        dp = [[0]*length for _ in range(length)]
        for r in range(2, length):
            for i in range(length-r):
                j =  i + r
                for k in range(i+1, j):
                    dp[i][j] = max(dp[i][j], dp[i][k] + dp[k][j] + nums[i]*nums[k]*nums[j])
        return dp[0][-1]
  1. 零钱兑换

完全背包问题。有两种写法,但是为了和01背包统一一般按照这种写法。

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [inf]*(amount+1)
        dp[0] = 0
        for coin in coins:
            for i in range(coin, amount+1):
                dp[i] = min(dp[i], dp[i-coin]+1)
        if dp[-1] == inf:
            return -1
        else:
            return dp[-1]
  1. 有效的完全平方数

二分为最优解法。

class Solution:
    def isPerfectSquare(self, num: int) -> bool:
        l = 1
        r = num
        while(l <= r):
            mid = (l + r)// 2
            if mid*mid == num:
                return True
            elif mid*mid < num:
                l = mid + 1
            else:
                r = mid - 1
        return False
  1. 移掉K位数字

这个题目也是一个单调栈的应用。要求剩下的序列最小,那么希望是升序的,又要求在O(N)的时间复杂度以内就只能使用单调栈了。

class Solution:
    def removeKdigits(self, num: str, k: int) -> str:
        stack = []
        remain = len(num) - k
        for c in num:
            while(k and stack and stack[-1] > c):
                stack.pop()
                k -= 1
            stack.append(c)
        res = stack[0:remain]
        temp = ''.join(res).lstrip('0')
        if not temp:
            return '0'
        else:
            return temp
  1. 用 Rand7() 实现 Rand10()

做这个题之前,我们需要知道从random10生成random7是非常简单的,现在我们需要保证数值能够覆盖1-10,并且概率也需要相等。

# 剔除的数字越少,算法效率越高。
# The rand7() API is already defined for you.
# def rand7():
# @return a random integer in the range 1 to 7

class Solution:
    def rand10(self):
        """
        :rtype: int
        """
        while(True):
            x = rand7()*7 + rand7()
            if x >= 11 and x<=50:
                break
        return x % 10 + 1
  1. 零钱兑换 II

完全背包问题。

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        dp = [0]*(amount+1)
        dp[0] = 1
        for coin in coins:
            for i in range(coin, amount+1):
                dp[i] = dp[i] + dp[i-coin]
        return dp[-1]
  1. 24 点游戏

这一题本质就是暴力,因为24的状态是可以枚举的,一共就4个数字,但是这一题的括号不是很好处理,并且减法和除法操作需要考虑数字交换的问题。我们可以这样考虑,对于一个数组,我们每次取出两个元素,进行加减乘除运算,也就是6种可能。这样得到了新的结果,除去已经使用的元素这样就得到了一个新的数组,再次来进行递归操作即可。

class Solution:
    def judgePoint24(self, nums: List[int]) -> bool:
        def dfs(nums):
            if len(nums) == 1:
                if abs(nums[0] - 24) < 1e-6:
                    return True
                return False
            for i in range(len(nums)-1):
                for j in range(i+1, len(nums)):
                    new_nums = []
                    for k in range(len(nums)):
                        if k != i and k != j:
                            new_nums.append(nums[k])
                    if dfs(new_nums + [nums[i]+nums[j]]):
                        return True
                    if dfs(new_nums + [nums[i]*nums[j]]):
                        return True
                    if dfs(new_nums + [nums[i]-nums[j]]):
                        return True
                    if dfs(new_nums + [nums[j]-nums[i]]):
                        return True
                    if nums[i]:
                        if dfs(new_nums + [nums[j] / nums[i]]):
                            return True
                    if nums[j]:
                        if dfs(new_nums + [nums[i] / nums[j]]):
                            return True
            
        return dfs(nums)
  1. 每日温度

单调栈。

class Solution:
    def dailyTemperatures(self, T: List[int]) -> List[int]:
        res = [0]*len(T)
        stack = []
        for i in range(len(T)):
            while(stack and T[stack[-1]] < T[i]):
                temp = stack.pop()
                res[temp] = i - temp
            stack.append(i)
        return res
  1. 最长公共子序列

经典DP问题。状态压缩不是很好写。

解法1:
class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        length1 = len(text1) + 1
        length2 = len(text2) + 1
        dp = [[0]*length2 for _ in range(length1)]
        
        for i in range(1, length1):
            for j in range(1, length2):
                if text1[i-1] == text2[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1])
        return dp[-1][-1]

解法2:状态压缩
class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        length1 = len(text1) + 1
        length2 = len(text2) + 1
        dp = [0]*length2
        
        for i in range(1, length1):
            pre = 0 #存斜对角的状态,每行需要初始化为0。
            for j in range(1, length2):
                tmp = dp[j]
                if text1[i-1] == text2[j-1]:
                    dp[j] = pre + 1
                else:
                    dp[j] = max(dp[j-1], dp[j])
                pre = tmp #更新之前的状态。
        return dp[-1]

解法3:补充
找到最优的子串(找路径)
这个只能够二维DP。
def findpath(dp, text1, text2):
    i = len(text1)
    j = len(text2)
    res = []
    while(i > 0 and j > 0):
        if dp[i][j] == dp[i-1][j-1] + 1:
            res.append(tex1[i-1])
        elif dp[i][j] == dp[i-1][j]:
            i -= 1
        else:
            j -= 1
    res.reverse()
    return res       
  1. 通过投票对团队排名

这个题不要转换成字符串来比较大小,比如10 > 9但是'10' < '9',python可以直接比较列表的大小。

class Solution:
    def rankTeams(self, votes: List[str]) -> str:
        Dict = {}
        k = len(votes[0])
        for vote in votes:
            for index, c in enumerate(vote):
                if c not in Dict:
                    Dict[c] = [0]*k
                Dict[c][index] += 1
        # for c in Dict:
        #     Dict[c] = ''.join(list(map(str, Dict[c])))
        temp = list(Dict.items())
        temp.sort(key = lambda x:(x[1], -ord(x[0])), reverse=True)
        return ''.join(dict(temp).keys())

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270