week3

day1

1. 合并两个有序链表(中等)

1.1 题目描述

	将两个升序链表合并为一个新的 **升序** 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

1.2 算法思路

  • Input: list1,list2
  • output list

在这里插入图片描述

  • 要求:list 为 list1 和 list2 合并 后的有序数组

  • 思路:

    • 存在空链表,返回另一个非空链表

    • 不存在空链表,执行下列操作

      • 创建节点指针 h1,h2 分别指向链表list1,list2的表头,以及空节点prehead和指向该空节点的指针pre,prehead 为合并链表的头节点。
        在这里插入图片描述

      • 比较 节点h1 和 h2 的大小,pre与值较小的节点相连,然后被选中的节点指针后移

      • 继续上一步,直至 某一链表的元素全部被加入到新链表中,即被合并。
        在这里插入图片描述

      • 返回prehead.next

1.3 代码

#Definition for singly-linked list.
#class ListNode:
#def __init__(self, val=0, next=None):
#self.val = val
#self.next = next
class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        #保证链表都不为空
        if not list1:
            return list2
        
        if not list2:
            return list1

        #
        h1 = list1
        h2 = list2
        
        prehead = ListNode()
        pre = prehead

        while h1 and h2:
            if h1.val<=h2.val:
                pre.next = h1
                pre = pre.next
                h1=h1.next
            else:
                pre.next = h2
                pre = pre.next
                h2 = h2.next
        
        if not h1:
            pre.next = h2
        else:
            pre.next = h1
        
        return prehead.next

2.括号生成

2.1 题目描述

	数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且** 有效的 **括号组合。

2.2 算法思路

  • Input : n:int
  • Output: list[]
  • 该问题可抽象为深度优先遍历一颗以‘(’为根,且‘(’与‘)’的数目一致其等于n的树
    在这里插入图片描述

2.3 代码

from typing import List


class Solution:
  def generateParenthesis(self, n: int) -> List[str]:


      res = []
      cur_str = ''
      def dfs(cur_str, left, right):
          """
          :param cur_str: 从根结点到叶子结点的路径字符串
          :param left: 左括号还可以使用的个数
          :param right: 右括号还可以使用的个数
          :return:
          """
          if left == 0 and right == 0:
              res.append(cur_str)
              return
          if right < left: # 防止")("错误
              return
          if left > 0:
              dfs(cur_str + '(', left - 1, right)
          if right > 0:
              dfs(cur_str + ')', left, right - 1)

      dfs(cur_str, n, n)
      return res

3.合并k个升序链表

3.1 题目描述

	给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。)

3.2 算法思路

  • 运用了归并的思想
    在这里插入图片描述

3.3 代码

class Solution:

  def mergeKLists(self, lists: List[ListNode]) -> ListNode:
      if not lists: return None
      n = len(lists) #记录子链表数量
      return self.mergeSort(lists, 0, n - 1) #调用归并排序函数

  def mergeSort(self, lists: List[ListNode], l: int, r: int) -> ListNode:
      if l == r:
          return lists[l]
      m = (l + r) // 2
      L = self.mergeSort(lists, l, m) #循环的递归部分
      R = self.mergeSort(lists, m + 1, r)
      return self.mergeTwoLists(L, R) #调用两链表合并函数

  def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
      if not l1:return l2
      if not l2:return l1
      dummy = ListNode(0) #构造虚节点
      move = dummy #设置移动节点等于虚节点
      while l1 and l2: #都不空时
          if l1.val < l2.val:
              move.next = l1 #移动节点指向数小的链表
              l1 = l1.next
          else:
              move.next = l2
              l2 = l2.next
          move = move.next
      move.next = l1 if l1 else l2 #连接后续非空链表
      return dummy.next #虚节点仍在开头

4.两两交换链表中的节点

4.1 题目描述

	给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
	![在这里插入图片描述](https://img-blog.csdnimg.cn/a1dcadaab0cd4f97bf2f60a70c0fa6f4.png#pic_center)

4.2 算法思路

  • Input:list
  • Output:list
    • 链表元素数量小于2,放回该链表的头节点head
    • 链表元素数量大于2
      - 创建两个分别指向第一个和第二个元素的指针p1,p2和一个连接头结点的空节点p。
      - 置换p1 ,p2所指向的相邻节点
      - 更新p1,p2的位置
      - 若p1,p2不为空则继续执行置换和更新
      - 循环结束,表示链表已经操作完成,放回链表的非空头节点
      - 如图所示:

在这里插入图片描述

4.3 代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        p = ListNode(0,head)
        if head and head.next:
            p1 = head
            p2 = p1.next
            head = head.next
            while p1 and p2:
                # 置换
                p.next = p2
                p1.next = p2.next
                p2.next = p1

                # 更新位置
                p = p1
                p1 = p1.next
                if p1:
                    p2 = p1.next

        return head

day2

1.K个一组翻转链表

1.1 题目描述

​ 给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表
​ k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将
​ 最后剩余的节点保持原有顺序。
​ 你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

在这里插入图片描述

1.2算法思路

  • 链表分区为已翻转部分+待翻转部分+未翻转部分
  • 每次翻转前,要确定翻转链表的范围,这个必须通过 k 此循环来确定
  • 需记录翻转链表前驱和后继,方便翻转完成后把已翻转部分和未翻转部分连接起来
  • 初始需要两个变量 pre 和 end,pre 代表待翻转链表的前驱,end 代表待翻转链表的末尾
  • 经过k此循环,end 到达末尾,记录待翻转链表的后继 next = end.next
  • 翻转链表,然后将三部分链表连接起来,然后重置 pre 和 end 指针,然后进入下一次循环
  • 特殊情况,当翻转部分长度不足 k 时,在定位 end 完成后,end==null,已经到达末尾,说明题目已完成,直接返回即可
  • 时间复杂度为 O(n∗K) 最好的情况为 O(n) 最差的情况为 O(n**2)
  • 空间复杂度为 O(1) 除了几个必须的节点指针外,我们并没有占用其他空间

1.3 代码

class Solution:
  # 翻转一个子链表,并且返回新的头与尾
  def reverse(self, head: ListNode, tail: ListNode):
      prev = tail.next
      p = head
      while prev != tail:
          nex = p.next
          p.next = prev
          prev = p
          p = nex
      return tail, head

  def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
      hair = ListNode(0)
      hair.next = head
      pre = hair

      while head:
          tail = pre
          # 查看剩余部分长度是否大于等于 k
          for i in range(k):
              tail = tail.next
              if not tail:
                  return hair.next
          nex = tail.next
          head, tail = self.reverse(head, tail)
          # 把子链表重新接回原链表
          pre.next = head
          tail.next = nex
          pre = tail
          head = tail.next
      
      return hair.next

2.删除有序数组中的重复项

2.1 题目描述

	给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,
	使每个元素 只出现一次 ,返回删除后数组的新长度。
	元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

2.2 算法思路

  • 题目中所给的数组有序,故若有复数个相同元素,则它们必相邻。
  • 从左至右扫描数组
  • 每次扫描都判断当前元素是否扫描过
    • 若没有扫描过,将其赋值到数组相应的位置
    • 否则,跳过

2.3 代码

class Solution:
  def removeDuplicates(self, nums: List[int]) -> int:
      dic = {}
      i = 0
      for num in nums:
          if num not in dic:
              dic.setdefault(num)
              nums[i]=num
              i+=1
      return i

3.移除元素

3.1 题目详情

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

3.2 算法思路

  • 将所有等于val的元素移动到首位

    :{1,3,5,7,9,7},taget = 5 ->{1,3,7,9,7,5},return 5

步骤:

1.从左到右扫描列表nums

2.发现与目标值相同的元素时,找到该元素后面首个不等于val的元素,与其交换位置

3.循环执行步骤2,直至列表遍历完或者发现某位置之后的元素的值全都等于val时,停止循环

4.放回最后一个非val元素的位置

在这里插入图片描述

3.3 代码

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        n = len(nums)
        k = 0
        for i in range(n):
            if nums[i]==val:
                for j in range(i,n):
                    if nums[j]!=val:
                        break
                if nums[j] == val:
                    return i
                else:
                    t = nums[j]
                    nums[j] = nums[i]
                    nums[i] = t
        return n



4.找出字符串中第一个匹配项的下标

4.1 题目详情

给你两个字符串 haystack 和 needle ,
请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。
如果 needle 不是 haystack 的一部分,则返回  -1 。

4.2 算法思路

  • 设n1,n2 为 haystack 和 needle 的大小
  • 易知,n1<n2 时,needle 不可能 在 haystack中
  • 当n1>=n2时:
    • 找出 haystack 中与needle首字符相同的字符的位置(即,找出以needle[0]开头的子串)
    • 逐个匹配子串和needle
      • 若出现匹配,返回该索引
      • 否则,返回 -1

4.3 代码

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        n1,n2 = len(haystack),len(needle)
        if n1<n2:
            return -1
        
        ss = []
        c1 = needle[0]#首字符
        # 找到haystack中所有的以c1为开头的,且长度为n2的字符串
        for i in range(n1-n2+1):
            if haystack[i]==c1:
                ss.append(i)
        
        for j in ss:
            if haystack[j:j+n2]==needle:
                return j
        
        return -1

day3

1.两数相除

1.1 题目详情

给你两个整数,被除数 dividend 和除数 divisor。将两数相除,要求 不使用 乘法、除法和取余运算。

整数除法应该向零截断,也就是截去(truncate)其小数部分。例如,8.345 将被截断为 8 ,-2.7335 将被截断至 -2 。

返回被除数 dividend 除以除数 divisor 得到的 商 。

注意:假设我们的环境只能存储 32 位 有符号整数,其数值范围是 [−231,  231 − 1] 。本题中,如果商 严格大于 231 − 1 ,则返回 231 − 1 ;如果商 严格小于 -231 ,则返回 -231 。

1.2 算法思路

  • 被除数为最小值时
    • 除数为1,return 最小值
    • 除数为-1,return 最大值
  • 除数为最小值时
    • 若被除数为最小值,return 1
    • 否则,return 0
  • 除数为0,return 0
  • 设立一个flag,正负数转换
    • 正数:取反 ,flag = True
    • 负数:flag = False

1.3 代码

class Solution:
    def divide(self, dividend: int, divisor: int) -> int:
        INT_MIN, INT_MAX = -2**31, 2**31 - 1

        # 考虑被除数为最小值的情况
        if dividend == INT_MIN:
            if divisor == 1:
                return INT_MIN
            if divisor == -1:
                return INT_MAX
        
        # 考虑除数为最小值的情况
        if divisor == INT_MIN:
            return 1 if dividend == INT_MIN else 0
        # 考虑被除数为 0 的情况
        if dividend == 0:
            return 0
        
        # 一般情况,使用类二分查找
        # 将所有的正数取相反数,这样就只需要考虑一种情况
        rev = False
        if dividend > 0:
            dividend = -dividend
            rev = not rev
        if divisor > 0:
            divisor = -divisor
            rev = not rev
        
        candidates = [divisor]
        # 注意溢出
        while candidates[-1] >= dividend - candidates[-1]:
            candidates.append(candidates[-1] + candidates[-1])
        
        ans = 0
        for i in range(len(candidates) - 1, -1, -1):
            if candidates[i] >= dividend:
                ans += (1 << i)
                dividend -= candidates[i]

        return -ans if rev else ans

2.下一个排列

2.1 题目详情

​ 整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。

例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。

必须 原地 修改,只允许使用额外常数空间。

2.2 算法思路

具体地,我们这样描述该算法,对于长度为 n 的排列 a:

1.	首先从后向前查找第一个顺序对 (i,i+1),满足 a[i]<a[i+1]。这样「较小数」即为 
	a[i]。此时 [i+1,n) 必然是下降序列。
	
2.	如果找到了顺序对,那么在区间 [i+1,n) 中从后向前查找第一个元素 j 
	满足 a[i]<a[j]。这样「较大数」即为 a[j]。
	
3.	交换 a[i] 与 a[j],此时可以证明区间 [i+1,n) 必为降序。
	我们可以直接使用双指针反转区间 [i+1,n) 使其变为升序,而无需对该区间进行排序。

2.3 代码

class Solution:
    def nextPermutation(self, nums: List[int]) -> None:
        i = len(nums) - 2
        while i >= 0 and nums[i] >= nums[i + 1]:
            i -= 1
        if i >= 0:
            j = len(nums) - 1
            while j >= 0 and nums[i] >= nums[j]:
                j -= 1
            nums[i], nums[j] = nums[j], nums[i]
        
        left, right = i + 1, len(nums) - 1
        while left < right:
            nums[left], nums[right] = nums[right], nums[left]
            left += 1
            right -= 1

3.搜索旋转排序数组

3.1 题目详情

整数数组 nums 按升序排列,数组中的值 互不相同 。

在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

3.2 算法思路

二分查找

在这里插入图片描述

3.3 代码

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

        n=len(nums)
        if n==1 and nums[0]!=target:
            return -1
        elif n==1 and nums[0]==target:
            return 0

        left,right = 0,1
        while right<n:
            if nums[left]<nums[right]:
                left+=1
                right+=1
            else:
                break
            
            
        
        if right==n:
            return Bin_search(nums,0,n-1,target)
        else:
            if target== nums[0]:
                return 0
            elif target>nums[0]:
                return Bin_search(nums,0,left,target)
            elif target<nums[0]:
                return Bin_search(nums,right,len(nums)-1,target)

4.在排序数组中查找元素的第一个和最后一个位置

4.1 题目详情

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

4.2 算法思路

二分查找

4.3 代码

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        if len(nums)==0 or target not in nums:
            return [-1,-1]
        def fistposition():
            left = 0
            right = len(nums)-1
            while(left<right):
                mid = (left+right)//2
                if target>nums[mid]:
                    left = mid+1
                if target==nums[mid]:
                    right = mid #注意这里,是right = mid ,因为找初始位置,后面的不管了,用二分法继续找前面的,直到left==right
                if target<nums[mid]:
                    right = mid-1
            return left
        
        def lastposition():
            left = 0
            right = len(nums)-1
            while(left<right):
                mid = (left+right+1)//2
                if target>nums[mid]:
                    left = mid+1
                if target==nums[mid]:
                    left = mid #这里则相反,只找到后面的,不找前面的,但mid = (left+right+1)//2,这里要+1,因为要往后推1位,如果不加1,实际上就没法推了
                if target<nums[mid]:
                    right = mid-1
            return left

        leftt = fistposition()
        lastt = lastposition()
        return [leftt,lastt]

day4

1.搜索插入位置

1.1 题目详情

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

1.2 算法思路

二分查找该元素

1.3 代码

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

2.有效的数独

2.1 题目详情

请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

2.2 算法思路

(1)行判断

  • 逐行扫描

(2)列判断

  • 逐列扫描

(3)九宫格判断

  • 对每个九宫格做判断

2.3 代码

class Solution:
    def isValidSudoku(self, board: List[List[str]]) -> bool:
        for i in range(9):                        #对每一行进行判断
            storage = []
            for j in range(9):
                if board[i][j] == '.':
                    continue
                if board[i][j] in storage:
                    return False
                else:
                    storage.append(board[i][j])
        for i in range(9):                         #对每一列进行判断
            storage = []
            for j in range(9):
                if board[j][i] == '.':
                    continue
                if board[j][i] in storage:
                    return False
                else:
                    storage.append(board[j][i])
        for i in range(0, 9, 3):                   #对九宫格是否重复进行判断
            for j in range(0, 9, 3):
                storage = []
                for x in range(0, 3):
                    for y in range(0, 3):
                        if board[i + x][j + y] == '.':
                            continue
                        if board[i + x][j + y] in storage:
                            return False
                        else:
                            storage.append(board[i + x][j + y])
        return True

3.组合总和

3.1 题目详情

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

3.2 算法思路

在这里插入图片描述

3.3 代码

from typing import List


class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:

        def dfs(candidates, begin, size, path, res, target):
            if target < 0:
                return
            if target == 0:
                res.append(path)
                return

            for index in range(begin, size):
                dfs(candidates, index, size, path + [candidates[index]], res, target - candidates[index])

        size = len(candidates)
        if size == 0:
            return []
        path = []
        res = []
        dfs(candidates, 0, size, path, res, target)
        return res

4.组合总和II

4.1 题目详情

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次 。

注意:解集不能包含重复的组合。 

4.2 算法思路

”回溯法“

由于我们需要求出所有和为 target的组合,并且每个数只能使用一次,因此我们可以使用递归 + 回溯的方法来解决这个问题:

  • 我们用dfs(pos,rest) 表示递归的函数,其中 pos 表示我们当前递归到了数组candidates 中的第 pos 个数,而 rest 表示我们还需要选择和为 rest 的数放入列表作为一个组合;
  • 对于当前的第 pos 个数,我们有两种方法:选或者不选。如果我们选了这个数,那么我们调用 dfs(pos+1,rest−candidates[pos]) 进行递归,注意这里必须满足 rest≥candidates[pos]。如果我们不选这个数,那么我们调用 dfs(pos+1,rest) 进行递归;
  • 在某次递归开始前,如果 rest 的值为 0,说明我们找到了一个和为 target 的组合,将其放入答案中。每次调用递归函数前,如果我们选了那个数,就需要将其放入列表的末尾,该列表中存储了我们选的所有数。在回溯时,如果我们选了那个数,就要将其从列表的末尾删除。

4.3 代码

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        def dfs(pos: int, rest: int):
            nonlocal sequence
            if rest == 0:
                ans.append(sequence[:])
                return
            if pos == len(freq) or rest < freq[pos][0]:
                return
            
            dfs(pos + 1, rest)

            most = min(rest // freq[pos][0], freq[pos][1])
            for i in range(1, most + 1):
                sequence.append(freq[pos][0])
                dfs(pos + 1, rest - i * freq[pos][0])
            sequence = sequence[:-most]
        
        freq = sorted(collections.Counter(candidates).items())
        ans = list()
        sequence = list()
        dfs(0, target)
        return ans

day5

1.字符串相乘

1.1 题目详情

给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。

1.2 算法思路

根据字符的ascall值,计算他的整数形式大小

1.3 代码

class Solution:
    def multiply(self, num1: str, num2: str) -> str:
        n1 = 0
        n2 = 0
        for i,c1 in enumerate(num1):
            n1+=(ord(c1)-ord('0'))*10**(len(num1)-i-1)
        for j,c2 in enumerate(num2):
            n2+=(ord(c2)-ord('0'))*10**(len(num2)-j-1)
        
        return "{}".format(n1*n2)
    

3.跳跃游戏II

3.1 题目详情

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。

每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

0 <= j <= nums[i] 
i + j < n
返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。

3.2 算法思路

  • 如果我们「贪心」地进行正向查找,每次找到可到达的最远位置,就可以在线性时间内得到最少的跳跃次数。
  • 例如,对于数组 [2,3,1,2,4,2,3],初始位置是下标 0,从下标 0 出发,最远可到达下标 2。下标 0 可到达的位置中,下标 1 的值是 3,从下标 1 出发可以达到更远的位置,因此第一步到达下标 1。
  • 从下标 1 出发,最远可到达下标 4。下标 1 可到达的位置中,下标 4 的值是 4 ,从下标 4 出发可以达到更远的位置,因此第二步到达下标 4。
  • 在具体的实现中,我们维护当前能够到达的最大下标位置,记为边界。我们从左到右遍历数组,到达边界时,更新边界并将跳跃次数增加 1。
  • 在遍历数组时,我们不访问最后一个元素,这是因为在访问最后一个元素之前,我们的边界一定大于等于最后一个位置,否则就无法跳到最后一个位置了。如果访问最后一个元素,在边界正好为最后一个位置的情况下,我们会增加一次「不必要的跳跃次数」,因此我们不必访问最后一个元素。

3.3 代码

class Solution:
    def jump(self, nums: List[int]) -> int:
        n = len(nums)
        maxPos, end, step = 0, 0, 0
        for i in range(n - 1):
            if maxPos >= i:
                maxPos = max(maxPos, i + nums[i])
                if i == end:
                    end = maxPos
                    step += 1
        return step

4.全排列

4.1 题目详情

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

4.2 算法思路

“回溯”
这个问题可以看作有 n 个排列成一行的空格,我们需要从左往右依此填入题目给定的 
n 个数,每个数只能使用一次。那么很直接的可以想到一种穷举的算法,即从左往右每一个位置都依此尝试填入一个数,看能不能填完这 
n 个空格,在程序中我们可以用「回溯法」来模拟这个过程。
我们定义递归函数 backtrack(first,output) 表示从左往右填到第 first 个位置,当前排列为 output。 那么整个递归函数分为两个情况:
(1)如果 first=n,说明我们已经填完了 n 个位置(注意下标从 0 开始),找到了一个可行的解,我们将 output 放入答案数组中,递归结束。
(2)如果 first<n,我们要考虑这第 first 个位置我们要填哪个数。根据题目要求我们肯定不能填已经填过的数,因此很容易想到的一个处理手段是我们定义一个标记数组 vis 来标记已经填过的数,那么在填第 first 个数的时候我们遍
n 个数,如果这个数没有被标记过,我们就尝试填入,并将其标记,继续尝试填下一个位置,即调用函数 backtrack(first+1,output)。回溯的时候要撤销这一个位置填的数以及标记,并继续尝试其他没被标记过的数。

4.3 代码

class Solution:
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        def backtrack(first = 0):
            # 所有数都填完了
            if first == n:  
                res.append(nums[:])
            for i in range(first, n):
                # 动态维护数组
                nums[first], nums[i] = nums[i], nums[first]
                # 继续递归填下一个数
                backtrack(first + 1)
                # 撤销操作
                nums[first], nums[i] = nums[i], nums[first]
        
        n = len(nums)
        res = []
        backtrack()
        return res

day6

2.旋转图像

2.1 题目详情

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

2.2 算法思路

在这里插入图片描述

在这里插入图片描述

由上图中的变化可以看出,旋转前后,坐标之间的映射关系。

为 (i,j) ->(j,n-1-i)

2.3 代码

class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        n = len(matrix)
        res = [[0]*n for _ in range(n)]
        for i in range(n):
            for j in range(n):
                res[j][n-1-i] = matrix[i][j]
                
        matrix[:] = res

3. 字母异位词分组

3.1 题目详情

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

3.2 算法思路

编码:将一个字符串转换为一个二十四位的元组(每位的数值表示,该字母在字符串中的数量)

比较编码,判断是否是字母异位词

3.3 代码

class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        mp = collections.defaultdict(list)

        for st in strs:
            counts = [0] * 26#"add"->"(1002 0000 0000 0000 0000 000000)"
            for ch in st:
                counts[ord(ch) - ord("a")] += 1
            # 需要将 list 转换成 tuple 才能进行哈希
            mp[tuple(counts)].append(st)
        
        return list(mp.values())

4.pow(x,n)

4.1 题目详情

实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,x**n )。	

4.2 算法思路

思路同归并

4.3 代码

class Solution:
    def myPow(self, x: float, n: int) -> float:
        # 采用归并的方法,两两相乘,逐层如此
        if x==0:
            return 0
        flag = 1    
        if n<0 :
            flag*=-1
            n*=-1
        if flag==-1:
            return 1/self.mergeSort(n,x)
        return self.mergeSort(n,x)

        
    def mergeSort(self,n:int ,x:float) -> float:

        #递归出口
        if n==0:
            return 1
        t = self.mergeSort(n//2,x)
        if (n%2==1):
            return t*t*x
        return t*t

day7

1.最后一个单词的长度

1.1 题目详情

给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。

单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。

1.2 算法思路

eg: “hello world”

一个句子的最后一个单词有以下特征:

  • 位于句子的最后
  • 被空格分割

解决:

1.去句子左右空格,设n为最后一个单词的长度,初始化为0

2.从句子末尾向开头遍历,每次循环n+=1,碰到空格,或者越界就停止。

3.return n

1.3 代码

class Solution:
    def lengthOfLastWord(self, s: str) -> int:
        s = s.lstrip()
        s = s.rstrip()

        n = 0
        for i in range(1,len(s)+1):
            if s[-i] != ' ':
                n+=1
            else:
                break
        
        return n

2.加一

2.1 题目详情

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

2.2 算法思路

数组->数字->数组

2.3 代码

class Solution:
    def plusOne(self, digits: List[int]) -> List[int]:
        res = 0
        for i,n in enumerate(digits):
            res = res*10+n
        res+=1
        a = []
        while res:
            digit = res-res//10*10
            res//=10
            a.append(digit)

        return a[::-1]

3.二进制求和I

3.1 题目详情

给你两个二进制字符串 a 和 b ,以二进制字符串的形式返回它们的和。

3.2 算法思路

二进制字符串->十进制数字->求和->和(十进制)->和(二进制)

3.3 代码

class Solution:
    def addBinary(self, a: str, b: str) -> str:
        num1 =0
        num2 = 0
        for i in a:
            num1=num1*2+int(i)
        for i in b:
            num2=num2*2+int(i)
        
        res = num1+num2
        if res>0:
            ans = ''
            while res:
                t = res-res//2*2
                res//=2
                ans+='{}'.format(t)
            
            return ans[::-1]
        elif res==0:
            return '0'

4.对称二叉树

4.1 题目详情

给你一个二叉树的根节点 root , 检查它是否轴对称。

4.2 算法思路

递归思想:

一颗二叉数是不是镜像

  • 两颗子树都是镜像,且左右孩子的值相等 ,True
  • 两颗子树不都是镜像 或 左右孩子的值不相等,False

递归出口

  • 左右孩子都为空,即为叶节点,True

4.3 代码

# 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 check(self,p:TreeNode,q:TreeNode)->bool:
        if not p and not q:
            return True
        if not p or not q:
            return False
        return (p.val == q.val) and self.check(p.left,q.right) and self.check(p.right,q.left)

    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        return self.check(root,root)

5.k间物品的最大和

5.1 题目详情

袋子中装有一些物品,每个物品上都标记着数字 1 、0 或 -1 。

给你四个非负整数 numOnes 、numZeros 、numNegOnes 和 k 。

袋子最初包含:

numOnes 件标记为 1 的物品。
numZeroes 件标记为 0 的物品。
numNegOnes 件标记为 -1 的物品。
现计划从这些物品中恰好选出 k 件物品。返回所有可行方案中,物品上所标记数字之和的最大值。

5.2 算法思路

按价值高低选择物品。

5.3 代码

class Solution:
    def kItemsWithMaximumSum(self, numOnes: int, numZeros: int, numNegOnes: int, k: int) -> int:

        sum = 0

        for i in range(k):
            if numOnes>=1:
                sum+=1
                numOnes = numOnes-1
                continue

            if numZeros>=1:
                sum+=0
                numZeros = numZeros-1
                continue

            if numNegOnes>=1:
                sum-=1
                numNegOnes = numNegOnes-1
                continue
            
            
        return sum
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值