刷题记录2

本文介绍了多种算法在实际问题中的应用,包括回溯算法解决图题,滑动窗口优化求解最大子数组和,以及数独求解策略。同时讨论了不同算法的时间复杂度,如暴力解法和优化后的线性复杂度解决方案。此外,还涵盖了交易策略、找零问题和皇后问题的解法,展示了如何通过算法提升效率和找到最优解。
摘要由CSDN通过智能技术生成

2.3
在这里插入图片描述
回溯算法,bfs搜索,研究了好久,最后还是看题解

class Solution:
    def getMaximumGold(self, grid):
        m = len(grid)
        n = len(grid[0])
        mon=[]
        def dfs(x, y, gold):
            rec = grid[x][y]	#保存当前位置的数据
            gold = gold + rec
            mon.append(gold)
            grid[x][y] = 0		#将走过的路变为零,防止走回头路

            for nx, ny in ((x - 1, y), (x + 1, y), (x, y + 1), (x, y - 1)):
                if 0 <= nx < m and 0 <= ny < n and grid[nx][ny] != 0:
                    dfs(nx, ny, gold)
            grid[x][y] = rec	#复原,回溯

        for i in range(m):
            for j in range(n):
                if grid[i][j] != 0:
                    dfs(i, j, ans)
        return max(mon)

在这里插入图片描述
纯暴力,时间复杂度O(k*n)

class Solution:
    def maxSlidingWindow(self, nums, k: int):
        max_nums=[]
        for i in range(len(nums)):
            max_win=max(nums[i:i+k]) #获取每个窗口的最大值
            max_nums.append(max_win)
            if i+k>=len(nums):
                break
        return max_nums

优化一下下,时间复杂度最快O(n),没有进入过取max更新的方法,最慢O(k*n),一直在取max()

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        def ret_max(max_num,new_num,win):		#不再是直接取max,而是根据加进来的数据判断是否要取max
            if max_num in win and new_num<=max_num:	#加进来的数没原来的数大,而且原来的数还在里面,不更新max_num
                return max_num
            elif new_num>max_num:				#加进来的数比原来的数大,更新
                return new_num
            else:
                return max(win)					#一直到最大的数被踢出窗口都没更新数据,直接在现在的窗口取max更新数据
        arr=nums[0:k]
        max_nums=[(max(arr))]
        for i in nums[k:]:
			arr.pop(0)
            arr.append(i)
            win_max=ret_max(max_nums[-1],i,arr)
            max_nums.append(win_max)
        return max_nums

学习一下优先队列
在这里插入图片描述
因为heap构造优先队列默认构造小根堆,把最小的放前面,所以可以把往里面加的数取反,这样最小的数就会一直在前面

class Solution:
    def maxSlidingWindow(self, nums, k: int):
        q=[(-nums[i],i) for i in range(k)]
        heapq.heapify(q)
        ans=[-q[0][0]]
        for i in range(k,len(nums)):
            heapq.heappush(q,(-nums[i],i))
            while q[0][1] <= i - k:
                heapq.heappop(q)
            ans.append(-q[0][0])
        return ans

2.4
在这里插入图片描述

图+回溯

from collections import defaultdict
from typing import List


class Solution:
    def findItinerary(self, tickets: List[List[str]]) -> List[str]:

        tickets_dict = defaultdict(list)
        for item in tickets:
            tickets_dict[item[0]].append(item[1])		#构建一张图

        path = ["JFK"]
        def backtracking(start_point):
            # 终止条件,走过的路的数量比总共的机票多一个起点就行
            if len(path) == len(tickets) + 1:
                return True
            tickets_dict[start_point].sort()		#字典序排序
            for _ in tickets_dict[start_point]:
                end_point = tickets_dict[start_point].pop(0)	#走过的路删除,避免死循环
                path.append(end_point)
                if backtracking(end_point): #只要有一个返回了True,结束
                    return Ture
                path.pop()		#回溯
                tickets_dict[start_point].append(end_point)		#复原图

        backtracking("JFK")
        return path
			
	

回溯算法递归不能细想,细想就炸了,自己写了好久,总是到了终点就停,没有走完全部路径
2.5
在这里插入图片描述
纯暴力,时间复杂度O(n^2),只要把两个排序后,比较一下大小就行,一个孩子依次从小到大吃所有饼干,吃到可以满足的,删除这块饼干并返回让下一个孩子吃

class Solution:
    def findContentChildren(self, g: List[int], s: List[int]) -> int:
        g.sort()
        s.sort()
        cot=0
        for i in g:
            for j in s:
                if i<=j:
                    cot+=1
                    s.remove(j)
                    break
                
        return cot

优化,双指针,时间复杂度O(n)
在这里插入图片描述

class Solution:
    def findContentChildren(self, g: List[int], s: List[int]) -> int:
        g.sort()
        s.sort()
        cot = 0
        i,j=0,0
        
        while True:
            if i>=len(g) or j>=len(s):
                break
            if g[i]<=s[j]:
                cot+=1
                j+=1
                i+=1
                continue
            j+=1
        return cot

在这里插入图片描述

class Solution:
    def wiggleMaxLength(self, nums):
    	#设置两个数,保存两次数据一个持续更新,一个只有在满足摆动序列的情况下更新
        preC, curC, res = 0, 0, 1	#如果序列长度为1的话,就进不了循环,所以将序列初始长度设置为1
        for i in range(len(nums) - 1):	
            curC = nums[i + 1] - nums[i]	#相邻两个的差值
            print(curC,preC)
            if curC * preC <= 0 and curC != 0:	
                res += 1
                preC = curC		
        return res

在这里插入图片描述
O(n^2)的算法,把范围内的数加起来,然后找最大的就行

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        max_num=nums[0]
        sums=0
        for i in range(len(nums)):
            for j in range(i,len(nums)):
                sums=sums+nums[j]
                if sums>max_num:
                    max_num=sums
            sums=0
        return max_num

看大佬的思路优化了一下下,时间复杂度O(n),没必要在全部加完后才进行初始化,从下一个点开始,只要在发现前面加起来的数小于0了,直接舍去前面所有的数,直接归零,重复,如果后面的数没有前面大,那么就不更新最大的数

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        max_rec=nums[0]		#这里不用最小的数,只要取第一个数作为初始值就行
        cot=0
        for i in nums:
            cot = cot + i
            if cot >max_rec:
                rec=cot
            if cot<0:
                cot=0	#如果小于0了,归零,从新开始累加
        return rec

在这里插入图片描述

2.6
在这里插入图片描述
只要第二天大于第一天,那么立刻结算利润

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        ans=0
        for i in range(len(prices)-1):
            if prices[i+1]-prices[i]>0:
                ans=prices[i+1]-prices[i]+ans
        return ans

在这里插入图片描述

一开始被绕进去了,没有立刻结算,而是维护了买入和要卖出的时候的价钱,卖出的时候再结算利润,所以代码写的很复杂

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        start_num=0
        profit=0
        now_num=0
        bol=True
        for i in range(len(prices)-1):

            if prices[i+1]-prices[i]>0:
                if bol:
                    start_num=prices[i]
                    bol=False

                now_num=prices[i+1]
            elif prices[i+1]-prices[i]<=0 and not bol:
                profit=now_num-start_num+profit
                start_num=0
                bol=True
        if now_num==prices[-1] and prices[-1]!=prices[-2]:
            profit = now_num - start_num + profit
        return profit

在这里插入图片描述
每一步都跳一下看看,维护一个跳的最远的位置,作为当前的位置,
判断是否能跳:当现在的位置被开始跳的下标超过,那么就说明这一步到不了了,不能跳

class Solution:
    def canJump(self, nums: List[int]) -> bool:
        cot = 0
        for i in range(len(nums)):
            if i <= cot:	#这一步想了好久都没想到,判断是否能跳的条件
                cot = max(nums[i] + i, cot)  # nums[i]+i,下一步跳的位置,cot现在的位置
                if i + nums[i] >= len(nums)-1:
                    return True
        return False

2.7
在这里插入图片描述
这题之前写过,没写出来在这里插入图片描述

class Solution:
    def jump(self, nums):
        cot = 0
        max_jump = 0
        now_jump = 0
        if len(nums)==1:							#如果长度为1那么不用走,直接就是终点,返回0
            return 0
        for i in range(len(nums)):	
            max_jump = max(nums[i] + i, max_jump)	#持续更新在(i,nums[i]+i)范围内最大的数
            if i == now_jump:						#走到了范围内能走的最远的路
                now_jump = max_jump					#把当前位置更新为在i,nums[i]+i范围内最大的数
                cot+=1								#跳一步
                if now_jump >= len(nums) - 1:		#如果已经到了终点返回
                    return cot
	

先跳,跳的同时维护一个能走的范围内的最大的值,走到了范围外时,准备走下一步,这下一步的步数就是一直在维护的范围内的最大值,
由每一步的最大值得出整体的最优解,果然贪心算法还是得多刷题,不然根本想不到怎么实现

在这里插入图片描述

class Solution:
    def largestSumAfterKNegations(self, nums: List[int], k: int) -> int:
        cot = 0
        nums.sort()
        for i in range(k):
            for j in range(len(nums)):
                if nums[j] < 0:
                    nums[j] = -nums[j]
                    cot += 1
                    break   
        if (k-cot)%2!=0 and cot<k:
            min_num=min(nums)
            nums.remove(min_num)
            nums.append(-min_num)
        return sum(nums)

时间复杂度(O(kn+nlogn),勉强能过
在这里插入图片描述
优化(我都用上排序了我还用两个for循环嵌套干嘛)

class Solution:
    def largestSumAfterKNegations(self, nums: List[int], k: int) -> int:
        cot = 0
        nums.sort()

        for j in range(len(nums)):
            if nums[j] < 0:
                nums[j] = -nums[j]
                cot += 1
                if cot==k:	#优化点,只算次数,消耗完次数立刻返回
                    return sum(nums)
        if (k-cot)%2!=0 and cot<k:
            min_num=min(nums)
            nums.remove(min_num)
            nums.append(-min_num)
        return sum(nums)

时间复杂度变为(O(n+nlogn),nlog是排序所需要的时间
在这里插入图片描述
总感觉用内置的排序算法有点作弊
2.8
在这里插入图片描述
第一题,当时写的

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

当时想的管他那么多,先上两个for循环,结果一看时间
在这里插入图片描述

现在想起来了,过来优化一下,达不到O(n)的水平,但比O(n^2)快多了

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        for i in range(0, len(nums)):
            a=target-nums[i]
            if a in nums[i+1:]:
                nums.remove(nums[i])
                return [i,nums.index(a)+1]

在这里插入图片描述
嗯,舒服了

今天来题难的,9月份就想解决的一题
先去玩回数独

在这里插入图片描述
首先要写一个判断能不能填的方法,返回bool,随时调用

def is_valid(x,y,val,borad):
	for i in range(9):
		if str(key)==borad[x][i]:	#行
			return False
		if str(key)==borad[i][y]:	#列
			return False
	row_x=(x//3)*3
	col_y=(y//3)*3 #获取数字所在宫的位置
	for i in range(row_x,row_x+3):	#宫
		for j in range(col_y,col_y+3):
			if str(val)==borad[i][j]
				return False
	#都没有说明能填,返回True
	return True
def backtracking(borad ):
	for i in range(9):
		for j in range(9):
			if borad[i][j]!='.':
				continue	#有数字,不能填
			for k in range(1,10):
				if is_valid(i,j,k,borad):
					borad[i][j]=str(k)
					if backtracking(borad):		#填一次数走一遍判断流程,不能填的复原,能填的先填上,下一个数再走一遍流程
						return True
					borad[i][j]='.'	#不能填,复原
			return False	#如果1-9都填过了,没一个返回True 那么直接返回False,回退一步
	return True

这种方法就是拿数字一个一个去试,这个合规,那么就填下一个,如果下一个一个数字都填不进去,那么上一个数字一定是违规的,回退一步
时间复杂度非常高,纯暴力,
在这里插入图片描述
总体代码:

class Solution:
    def solveSudoku(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        def backtracking(borad ):
            for i in range(9):
                for j in range(9):
                    if borad[i][j]!='.':
                        continue	#有数字,不能填
                    for k in range(1,10):
                        if is_valid(i,j,k,borad):
                            borad[i][j]=str(k)
                            if backtracking(borad):
                                return True
                            borad[i][j]='.'	#不能填,复原
                    return False	#如果1-9都填过了,没一个返回True 那么直接返回False,回退一步
            return True
        def is_valid(x,y,val,borad):
            for i in range(9):
                if str(val)==borad[x][i]:	#行
                    return False
                if str(val)==borad[i][y]:	#列
                    return False
            row_x=(x//3)*3
            col_y=(y//3)*3 		#获取数字所在宫的位置
            for i in range(row_x,row_x+3):	#宫
                for j in range(col_y,col_y+3):
                    if str(val)==borad[i][j]:
                        return False
            #都没有说明能填,返回True
            return True
        backtracking(board)

这题写了我两天,还去玩了几把数独,终于看懂了别人代码,写了出来

2.10
在这里插入图片描述

纯暴力
在这里插入图片描述

    def canCompleteCircuit(self, gas: List[int], cost: List[int]):

        for i in range(len(gas)):
            rest = gas[i]	#记录起点送的油
            add = [k for k in gas[i + 1:len(gas)]] + [k for k in gas[0:i + 1]] 	#每一站加油的路线
            reduce = [k for k in cost[i:len(cost)]] + [k for k in cost[0:i]]	#每一站要花掉的油,因为消耗的油是cost[i] 所以要比要加的油慢一步
            for l in range(len(add)):
                if rest - reduce[l] < 0:	#如果走不到则返回
                    break
                rest = rest - reduce[l] + add[l]
                if add[l] == add[-1] and reduce[l] == reduce[-1]: #到了终点,退出
                    return i
        return -1	#都走完了,则说明无法到达,返回-1
        

一顿操作猛如虎,一看击败5%
优化
换种思路,首先,如果sum(gas)-sum(cost),那么就不可能到达终点,直接返回-1
因为耗油总和是大于零的,所以必定可以到达终点,从0开始一个一个当成起点,如果有一个站到不了,那么就说明在这个站之前的数都是不能当作起点的,所以下一个起点应该从i+1开始,直到跑完全程

class Solution:
    def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
        start = 0		#起点
        curSum = 0		#剩余油量
        if sum(gas) - sum(cost) < 0:	#如果跑不完直接返回
            return -1
        for i in range(len(gas)):		
            curSum += gas[i] - cost[i]	#计算剩余由量
        
            if curSum < 0:	
                curSum = 0			#剩余油量归零
                start = i + 1		#起点从i+1从新开始

        return start

2.11
在这里插入图片描述

    def maxProfit(self, prices: List[int], fee: int) -> int:
        result = 0
        minPrice = prices[0]
        for i in range(1, len(prices)):
            if prices[i] < minPrice:
                minPrice = prices[i]    	#买入
            elif prices[i] - fee > minPrice:  # 售价加上卖出所需要的手续费还大于最小售价,那么就可能有得赚,卖出
                result += prices[i] - minPrice - fee    # 计算利润,minPrice = prices[i] - fee,那么总体就是:
                # prices[i] -(minPrice-fee) - fee=prices[i] - minPrice
                # 有可能会多次计算利润,当最后一次计算利润的时候才是真正的卖出

                minPrice = prices[i] - fee	#当前卖出并不一定是最大获利,所以要记录当前卖出能赚多少钱
        return result

这题比上次做的不需要手续的难了很多,因为要考虑三种情况了,因为有手续费的存在,所以要在最少交易次数的情况下拿到最多的钱,上一题只要能赚就能卖,minPrice = prices[i] - fee 这一步很巧妙,如果我自己来绝对想不到

2.12
在这里插入图片描述
模拟过程:

class Solution:
    def lemonadeChange(self, bills: List[int]) -> bool:
        nums = []
        for i in range(len(bills)):
            nums.append(bills[i])
            zhao = bills[i] - 5
            if sum(nums) < zhao:
                return False
            if zhao== 0:
                continue
            if zhao  == 5:
                if 5 in nums:
                    nums.remove(5)

                else:
                    return False
            if zhao  == 15:
                if 10 in nums:
                    nums.remove(10)
                    if 5 in nums:
                        nums.remove(5)
                    else:
                        return False
                else:
                    if nums.count(5) >= 3:
                        nums.remove(5)
                        nums.remove(5)
                        nums.remove(5)
                    else:
                        return False
        return True

感觉没用什么算法,只有判断和删除,硬要说算法的话就是收到20的先找10块钱的,感觉这是生活经验,时间复杂度O(n^2)
在这里插入图片描述
不带零钱卖什么柠檬水,哪有柠檬水卖这么贵的,现在都是用的微信支付宝,既然是排队购买,就不能让有零钱的先来吗

吐槽完毕,现在开始优化
不需要用一个整个数组去存放得到的钱,因为只要三种钱,而20的毫无卵用,所以只需要设置两个变量,five,ten来记录自己得到的钱方便找钱就行

class Solution:
    def lemonadeChange(self, bills: List[int]) -> bool:
        five=0
        ten=0
        for i in range(len(bills)):
            zhao = bills[i] - 5
            if zhao== 0:
                five+=1
                continue
            if zhao  == 5:
                ten+=1
                if five>0:
                    five-=1
                else:
                    return False
            if zhao  == 15:
                if ten>0:
                    ten-=1
                    if five>0:
                        five-=1
                    else:
                        return False
                else:
                    if five >= 3:
                        five-=3
                    else:
                        return False
        return True

摸了
2.14
在这里插入图片描述

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        if not n: return []
        board = [['.'] * n for _ in range(n)]
        res = []
        def isVaild(board,row, col):
            #判断同一列是否冲突,不用判断同一行,因为单层递归只会选择同一行里的元素
            for i in range(len(board)):
                if board[i][col] == 'Q':
                    return False
            # 判断左上角是否冲突
            i = row -1
            j = col -1
            while i>=0 and j>=0:
                if board[i][j] == 'Q':
                    return False
                i -= 1
                j -= 1
            # 判断右上角是否冲突
            i = row - 1
            j = col + 1
            while i>=0 and j < len(board):
                if board[i][j] == 'Q':
                    return False
                i -= 1
                j += 1
            return True

        def backtracking(board, row, n):
            if row == n:
                temp_res = []
                for temp in board:	#终止条件
                    temp_str = "".join(temp)	#把二维列表转为一维
                    temp_res.append(temp_str)
                res.append(temp_res)
            for col in range(n):
                if not isVaild(board, row, col):
                    continue
                board[row][col] = 'Q'
                backtracking(board, row+1, n)
                board[row][col] = '.'	#回溯
        backtracking(board, 0, n)
        return res
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值