2021-06-23

刷题笔记

6.23
力扣62:不同路径
自己:使用了回溯法,超时,因为会有很多重复调用,可以加一个缓存,代码如下:(递归从下到上)

class Solution(object):
    def uniquePaths(self, m, n):
        d = {}
        def dfs(i, j):
            # 如果(i,j)在缓存中则直接返回
            if (i, j) in d:
                return d[i,j]
            # 到达边界时,返回 1    
            if i == m - 1 or j == n - 1:
                return 1
            # 继续递归调用,往下i+1,往右j+1    
            d[i,j] = dfs(i + 1, j) + dfs(i, j + 1)
            return d[i,j]
        return dfs(0,0)

解法二:动态规划(从上到下)
状态转移方程:d[i][j]=d[i-1][j]+d[i][j-1]
首先处理边界问题,第一行第一列赋值1

class Solution(object):
    def uniquePaths(self, m, n):
       #先列再行
        dp = [[0 for _ in range(n)] for _ in range(m)]
        # 第一行都赋予 1
        for j in range(n):
            dp[0][j] = 1
        # 第一列都赋予 1    
        for i in range(m):
            dp[i][0] = 1
        # 两个for循环推导,对于(i,j)来说,只能由上方或者左方转移过来 
        for i in range(1, m):
            for j in range(1, n):
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
        return dp[-1][-1]

解法三:优化解法二,动态规划中每一个需要创建一个二维数组的解法,都可以换成只创建一个一维数组的滚动数组解法,依据的规则是一般二维数组中存放的是所有的结果,但是一般我们需要的结果实在二维数组的最后一行的某个值,前面几行的值都是为了得到最后一行的值而需要的,所以可以开始就创建跟二维数组最后一行一样大的一维数组,每次存放某一行的值,下一次根据这一行的值算出下一行的值,在存入这个数组,也就是把这个数组滚动了,最后数组存储的结果就是原二维数组中最后一行的值。

所以申请一个1*n的数组,转移方程变为** dp[j]=dp[j]+dp[j-1]**

class Solution(object):
    def uniquePaths(self, m, n):
    	dp = [1] *n
    	for i in range(1,m):
    		for i in range(1,n):
    			dp[j]=dp[j]+dp[j-1]
    	 return dp[-1]

力扣39 组合总数
自己的解法:回溯(时间复杂度,空间复杂度高)

class Solution:
    def combinationSum(self, candidates: List[int], target: int):
        def dfs(candidates,target,size,res,ans,path):
            if res == target:
                ans.add(tuple(sorted(path)))
                return
            for i in range(size):
                if target-res>=candidates[i]:
                    res = res+candidates[i]
                    path.append(candidates[i])
                    dfs(candidates,target,size,res,ans,path)
                    path.pop()
                    res = res-candidates[i]
        size=len(candidates)
        res=0
        ans=set()
        dfs(candidates,target,size,res,ans,[])
        return list(ans)
        

刘老师今日课堂

  1. .list不能hash,即set.add(list)错误
  2. list.sort() 与sorted(list)区别,前者无返回值,后者返回一个可迭代的对象

正确的解法:回溯+剪枝
从target开始减去当前数字
剪枝:设置一个递归开始位置

剪枝小技巧
排列问题,讲究顺序(即 [2, 2, 3] 与 [2, 3, 2] 视为不同列表时),需要记录哪些数字已经使用过,此时用 used 数组,比如全排列
组合问题,不讲究顺序(即 [2, 2, 3] 与 [2, 3, 2] 视为相同列表时),需要按照某种顺序搜索,此时使用 begin 变量,比如这道题目

class Solution:
    def combinationSum(self, candidates: List[int], target: int):
        def dfs(candidates, begin,target, size, ans, path):
            if target < 0:
                return
            if target == 0:
               ans.append(path)
            for i in range(begin,size):
                dfs(candidates,i,target-candidates[i],size,ans,path+[candidates[i]])
        size = len(candidates)
        if size == 0:
            return []
        ans = []
        dfs(candidates, 0,target, size, ans, [])
        return ans

        

6.24刷题

力扣14 最长公共前缀
我的代码:暴力扫描,通过一半测试用例

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        if not strs:
            return ""
        if strs == [""]:
            return ""
        i,j = len(strs[0]),len(strs) #i是列 j是行
        for k in range(i):
            c = strs[0][k]
            for l in range(1,j):
                if k <= len(strs[l])-1:
                    if strs[l][k] !=c:
                        return strs[0][:k]

快速解法:找到字符串数组里的最大最小字符串,找他们的公告前缀就可以

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        maxStr = max(strs)
        minStr = min(strs)

        for i,x in enumerate(minStr):
            if x!=maxStr[i]:
                return minStr[:i]
        return minStr

力扣162 寻找峰值
自己的解法:暴力,55555,太菜了,只会暴力

class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
        if len(nums) == 1:
            return 0
        for i in range(len(nums)):
            if i ==0:
                if nums[0] > nums[1]:
                    ans = 0
                    break
            if i == len(nums)-1:
                if nums[-1]>nums[-2]:
                    ans = len(nums)-1
                    break
            if nums[i]>nums[i-1] and nums[i]>nums[i+1]:
                ans =i
                break
        return ans

优秀题解:二分解法

class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
        left, right = 0, len(nums)-1
        while left< right:
            mid = (left+right) //2
            if nums[mid] > nums[mid+1]:
                right = mid
            else:
                left = mid+1
        return left

6.26刷题
力扣40 组合总数||
自己的解法:回溯+去重(超时)
正确解法:回溯+剪枝
剪枝技巧1:设置一个begin,即开始搜索的位置,让begin每次从i+1开始搜索,就不会重复使用自己,保证每个位置被使用一次,还可以保证不会出现123,321这样的情况
剪枝技巧2:还有 1(0) 6(5) 和1(1) 6(5)这种情况,加一个判断i>begin

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: 
        def findPath(nums, begin,target, res, path, size):
            if target < 0:
                return
            if target == 0:
                res.append(path)
                return

            for i in range(begin,size):
                if nums[i] == nums[i-1] and i>begin:
                    continue
                findPath(nums,i+1, target - nums[i], res, path + [nums[i]], size)
        size = len(candidates)
        if size == 0:
            return
        res = []
        candidates.sort()
        findPath(candidates,0, target, res, [], size)
        return res

力扣17 电话号码的数字组合
本人:暴力枚举

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        number_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']
        }
        res = []
        size = len(digits)
        if size ==1:
            return number_dict[digits]
        if size == 2:
            i, j = digits[0],digits[1]
            for v1 in number_dict[i]:
                for v2 in number_dict[j]:
                    res.append(v1+v2)
        if size == 3:
            i, j, k = digits[0],digits[1],digits[2]
            for v1 in number_dict[i]:
                for v2 in number_dict[j]:
                    for v3 in number_dict[k]:
                        res.append(v1+v2+v3)
        if size == 4:
            i, j, k, l = digits[0],digits[1],digits[2],digits[3]
            for v1 in number_dict[i]:
                for v2 in number_dict[j]:
                    for v3 in number_dict[k]:
                        for v4 in number_dict[l]:
                            res.append(v1+v2+v3+v4)
        return res
         

题解:回溯

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        if not digits:
            return []
        number_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']
        }
        def letterCom(combination,digits):
            if len(digits) == 0:
                res.append(combination)
            else:
                for i in number_dict[digits[0]]:
                    letterCom(combination+i,digits[1:])


        res = []
        letterCom("",digits)
        return res

6.29
152 乘积最大子数组
跟和最大子数组不一样的是,这里要维护两个变量`

class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        maxAns = nums[0]
        imax = 1
        imin = 1
        for i in range(len(nums)):
            if nums[i]<0:
                imax, imin = imin, imax
            imax = max(nums[i],imax*nums[i])
            imin = min(nums[i],imin*nums[i])
            maxAns = max(maxAns,imax)
        return maxAns

力扣448 找出消失的数字,原地哈希

class Solution:
    def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
        n = len(nums)
        for num in nums:
            x = (num - 1) % n #还原数组的下标
            nums[x] += n
        ret = [i + 1 for i, num in enumerate(nums) if num <= n]
        return ret
        

7.1
力扣 对称二叉树 easy
递归判断左子树的左子树与右子树的右子树 以及 左子树的右子树和右子树的左子树是否相等,有一个为空或者值不等,false,同时为空,true

class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        if not root:
            return True
        def dfs(left,right):
			# 递归的终止条件是两个节点都为空
			# 或者两个节点中有一个为空
			# 或者两个节点的值不相等
            if not (left or right):
                return True
            if not (left and right):
                return False
            if left.val!=right.val:
                return False
            return dfs(left.left,right.right) and dfs(left.right,right.left)
		# 用递归函数,比较左节点,右节点
        return dfs(root.left,root.right)

力扣 回文数 easy
解法一:变成字符串再反转

return str(x) == str(x)[::-1]

解法二:反转数字再比较

class Solution:
    def isPalindrome(self, x: int) -> bool:
        if x<0:
            return False
        num = x
        cur = 0
        while(num):
            cur = cur*10+num%10
            num =num//10
        return cur == x

力扣 155 最小栈 简单

class MinStack:

    def __init__(self):
        self.stack = []
        self.min_stack = [math.inf]
    def push(self, val: int) -> None:
        self.stack.append(val)
        self.min_stack.append(min(val,self.min_stack[-1]))
    def pop(self) -> None:
        self.stack.pop()
        self.min_stack.pop()
    def top(self) -> int:
        return self.stack[-1]
    def getMin(self) -> int:
        return self.min_stack[-1]

力扣 33 搜索旋转排序数组 中等
解法:二分 注意边界条件的判断

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        if not nums:
            return -1
        left,right = 0,len(nums)-1
        while left <= right:
            mid = (left+right)//2
            if nums[mid] == target:
                 return mid
            #左边有序
            if nums[left] <= nums[mid]: 
                if nums[left] <= target < nums[mid]:
                    right = mid-1
                else:
                    left = mid+1
            else:
                if nums[mid] <= target <=nums[right]:
                    left = mid+1
                else:
                    right = mid-1
        return -1

7.2
力扣 48 旋转图像
思路:先水平反转,再沿着对角线反转

class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        l1 = len(matrix) #行
        for i in range(l1//2):
            for j in range(l1):
                matrix[i][j], matrix[l1-i-1][j] = matrix[l1-i-1][j], matrix[i][j]
        for i in range(l1):
            for j in range(0,i):
                matrix[i][j] ,matrix[j][i] = matrix[j][i], matrix[i][j]

力扣142 环形链表2 找环的入口
自己:用了一个set

# 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
        map = set()
        p = head
        while(p):
            if p in map:
                return p
            map.add(p)
            p = p.next
        return None

7.3
力扣234 回文链表
我的思路:放到list里反转看是否一致 空间复杂度O(n)
降低空间复杂度的办法:用快慢指针找到中点,从中点以后的链表反转,检查是否一致

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        if not head:
            return False
        def reverlist(head):
            pre = None
            cur = head
            while(cur):
                next = cur.next
                cur.next = pre
                pre = cur
                cur = next
            return head
        slow,fast,preslow = head, head,head
        while(fast and fast.next):
            pre = slow
            slow = slow.next
            fast = fast.next.next
        pre.next = None
        list1 = head
        list2 = reverlist(slow)
        while(list1):
            if list1.val != list2.val:
                return False
            list1 = list1.next
            list2 = list2.next
        return True

力扣461 汉明距离
知识点:python & | 表示按位操作
and, or则依据是否非0来决定输出

class Solution:
    def hammingDistance(self, x: int, y: int) -> int:
        z = x^y
        ans = 0
        while(z):
            ans +=z & 1
            z>>=1
        return ans

7.4
力扣63 不同路径2
思路:动态规划,处理一下障碍点即可,如果不在第一行第一列,该出dp赋值0,如果在第一行第一列,那么从故障点往后所有值为0

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        m, n = len(obstacleGrid[0]), len(obstacleGrid) #m列数 n行
        dp = [[0 for _ in range(m)] for _ in range(n)]
        for i in range(m):
            if obstacleGrid[0][i] ==1:
                break
            dp[0][i] = 1
        for i in range(n):
            if obstacleGrid[i][0] ==1:
                break
            dp[i][0] = 1
        for i in range(1,n):
            for j in range(1,m):
                if obstacleGrid[i][j] == 1:
                    continue
                else:
                    dp[i][j] = dp[i-1][j]+dp[i][j-1]
        return dp[-1][-1]

64 最小路径和:还是dp,第一行第一列累加即可,其他地方的值为dp[i][j] = min(dp[i-1][j],dp[i][j-1])+nums[i][j]

class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:
        dp = [float(inf)] * (len(grid[0])+1)
        dp[1] = 0
        for row in grid:
            for i ,num in enumerate(row):
                dp[i+1] = min(dp[i+1],dp[i])+num
        return dp[-1]

7.5
5 最长回文子串 中心扩展法

class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        if n ==1:
            return  s
        if n==2:
            return s[0] if s[0]!=s[1] else s
        res = ''

        def find(left, right, res):
            while (left >= 0 and right <n and s[left] == s[right]):
                left -= 1
                right += 1
            if right - left - 1 > len(res):
                res = s[left+1:right]
            return res

        for i in range(n):
                res=find(i, i + 1, res) #回文子串长度为偶数
                res=find(i, i, res)#回文子串长度为奇数
        return res

7.6 盛最多水的容器 【中等】
思路:双指针

class Solution:
    def maxArea(self, height: List[int]) -> int:
        if not height:
            return 0
        n = len(height)
        l, r = 0, n-1
        maxans = 0
        while l<r:
            pre = min(height[l],height[r])*(r-l)
            maxans =max(pre, maxans)
            if height[l]<height[r]:
                l +=1
            else:
                r -=1 
        return maxans

78 子集 回溯(以为自己学会了,还是要靠题解,心态崩了)

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        res = []
        n = len(nums)
        def dfs(nums,path,start):
            res.append(path[:])
            for i in range(start,n):
                path.append(nums[i])
                dfs(nums,path,i+1)
                path.pop()
        dfs(nums,[],0)
        return  res

7.8 338 比特数计数
小技巧:x&(x-1) 可以消除x的最末一个1
如果x&(x-1) ==0 说明这个数是2的整数幂

class Solution:
    def countBits(self, n: int) -> List[int]:
        if n==0:
            return [0]
        high_hit = 0
        res = [0]
        for i in range(1,n+1):
            if i & (i-1) == 0:
                high_hit = i
            res.append(res[i-high_hit]+1)
        return res

647 回文子串 中等
我的解法:中心扩展

class Solution:
    def countBits(self, n: int) -> List[int]:
        if n==0:
            return [0]
        high_hit = 0
        res = [0]
        for i in range(1,n+1):
            if i & (i-1) == 0:
                high_hit = i
            res.append(res[i-high_hit]+1)
        return res

剑指03 数组中重复的数字

class Solution:
    def findRepeatNumber(self, nums: List[int]) -> int:
        '''
            用值当下标索引,因为有重复值,就会有冲突
        '''
        n = len(nums)
        for i in range(n):
            while i != nums[i]:
                if nums[i] == nums[nums[i]]:
                    return nums[i]
                temp = nums[i]
                nums[i], nums[temp] = nums[temp], nums[i]
        return -1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值