标签:first hnu fast rom n+1 list inline star 比较
在查找刷题攻略的时候,也遇到了一些比较经典、有趣的题目,记录在这里,不断更新。难度保持在LeetCode中的Medium级别左右。
单调栈专用于解决此类问题。其中有一个trick是,查找比他大的数用单调递减栈,查找比他小的数用单调递增栈。
class Soluation(object):
def findFirstBiggerNum(self,nums):
if not nums:return
stack = []
res = [-1 for i in range(len(nums))]
for i in range(len(nums)):
while stack and nums[i] > nums[stack[-1]]:
res[stack.pop()] = nums[i]
stack.append(i)
return res
nums = [9,6,5,7,3,2,1,5,9,10]
Soluation().findFirstBiggerNum(nums)
变形的单调栈问题。实际上是求每个数距离右边第一个比他大的数的距离。
class Soluation(object):
def badHairDay(self,nums):
if not nums:return
stack = []
res = 0
for num in nums:
while stack and num > stack[-1]:
stack.pop()
res+=len(stack)
stack.append(num)
return res
nums = [10,3,7,4,12,2]
Soluation().badHairDay(nums)
两头扫策略。分别做两个辅助数组,从右到左遇到的最小数,以及从左到右遇到的最大数,如果在一个索引上,这两个值相同,则说明是该数组‘中位数’。
class Soluation(object):
def findNumBiggerLeftSmallerRight(self,nums):
if not nums:return
rightmin = [nums[-1] for i in range(len(nums))]
for i in reversed(range(len(nums)-1)):
if nums[i] < rightmin[i+1]:
rightmin[i] = nums[i]
else:
rightmin[i] = rightmin[i+1]
leftmax = nums[0]
for i in range(0,len(nums)):
if nums[i] > leftmax :
leftmax = nums[i]
if leftmax == rightmin[i]:
return nums[i]
return -1
nums = [7, 10, 2, 6, 19, 22, 32]
Soluation().findNumBiggerLeftSmallerRight(nums)
一道递归求解题。
def getNSumCount(n,sum):
if n<1 or sum<n or sum>6*n:
return 0
if n==1:
return 1
res = 0
for i in range(6):
res += getNSumCount(n-1,sum-(i+1))
return res
def PrintProbability(number):
res = 0
for i in range(number,6*number+1):
res += getNSumCount(number,i)
for i in range(number,6*number+1):
ratio = getNSumCount(number,i)/res
print("{}: {:e}".format(i, ratio))
s = PrintProbability(5)
字串必须是连续的,例如对于‘abcdfg‘,‘abdfg‘,其解为‘dfg’,最大长度为3。使用动态规划求解。
#最长公共子串(The Longest Common Substring)
class LCS(object):
def find_lcsubstr(self,s1,s2):
m,n = len(s1),len(s2)
dp = [[0 for j in range(n+1)] for m in range(m+1)]
maxLength = 0
index = 0
for i in range(1,m+1):
for j in range(1,n+1):
if i == 0 or j==0:
dp[i][j] = 0
elif s1[i-1] == s2[j-1]:
dp[i][j] = dp[i-1][j-1]+1
if dp[i][j]>maxLength:
maxLength = dp[i][j]
index = i
return s1[index-maxLength:index],maxLength
print(LCS().find_lcsubstr('abcdfg','abdfg'))
与字串不同,子序列可以是不连续的,例如对于‘abcdfg‘,‘abdfg‘,其解为‘abdfg’,最大长度为5。使用动态规划求解,时间复杂度\(O(N^2)\)。(若只求长度,可以用栈+二分求解,时间复杂度可以达到\(O(NlogN)\))
def find_lcseque(s1, s2):
m,n = len(s1),len(s2)
dp = [[0 for j in range(n+1)] for i in range(m+1)]
dp_d = [[None for j in range(n+1)] for i in range(m+1)]
for i in range(1,m+1):
for j in range(1,n+1):
if s1[i-1] == s2[j-1]:
dp[i][j] = 1+dp[i-1][j-1]
dp_d[i][j] = 'ok'
else:
dp[i][j] = max(dp[i][j-1],dp[i-1][j])
if dp[i][j-1]>dp[i-1][j]:
dp_d[i][j] = 'left'
else:
dp_d[i][j] = 'up'
res = ''
while dp_d[m][n]:
if dp_d[m][n] == 'ok':
res+=s1[m-1]
m-=1
n-=1
elif dp_d[m][n] == 'left':
n-=1
elif dp_d[m][n] == 'up':
m-=1
return res[::-1]
find_lcseque('abdfg','abcdfg')
参考平面中距离最近点对。使用分治的思想求解。
#求出平面中距离最近的点对(若存在多对,仅需求出一对)
import random
import math
#计算两点的距离
def calDis(seq):
dis=math.sqrt((seq[0][0]-seq[1][0])**2+(seq[0][1]-seq[1][1])**2)
return dis
#生成器:生成横跨跨两个点集的候选点
def candidateDot(u,right,dis,med_x):
#遍历right(已按横坐标升序排序)。若横坐标小于med_x-dis则进入下一次循环;若横坐标大于med_x+dis则跳出循环;若点的纵坐标好是否落在在[u[1]-dis,u[1]+dis],则返回这个点
for v in right:
if v[0]>med_x+dis:
break
if v[1]>=u[1]-dis and v[1]<=u[1]+dis:
yield v
#求出横跨两个部分的点的最小距离
def combine(left,right,resMin,med_x):
dis=minDis=resMin[1]
pair=resMin[0]
for u in left:
if u[0]<med_x-dis:
continue
for v in candidateDot(u,right,dis,med_x):
dis=calDis([u,v])
if dis<minDis:
minDis=dis
pair=[u,v]
return [pair,minDis]
#分治求解
def getMinDisPair(seq):
#求序列元素数量
n=len(seq)
seq=sorted(seq)
#递归开始进行
if n<=1:
return None,float('inf')
elif n==2:
return [seq,calDis(seq)]
else:
half=n//2
if n%2 == 1:
med_x = seq[half][0]
else:
med_x = (seq[half][0]+seq[half+1][0])/2
left=seq[:half]
resLeft=getMinDisPair(left)
right=seq[half:]
resRight=getMinDisPair(right)
#获取两集合中距离最短的点对
if resLeft[1]<resRight[1]:
d = resLeft
else:
d = resRight
resMin=combine(left,right,d,med_x)
pair=resMin[0]
minDis=resMin[1]
return [pair,minDis]
seq=[(random.randint(0,10000),random.randint(0,10000)) for x in range(500)]
print("优化算法",getMinlengthPair(seq))
背包问题说的是,给定背包容量W,一系列物品{weiht,value},每个物品只能取一件,获取最大值。
class Solution():
def bag(self,w,v,weight):
n = len(w)
dp = [[0 for j in range(weight+1)] for i in range(n+1)]
for i in range(1,n+1):
for j in range(1,weight+1):
if w[i-1]>j:
dp[i][j] = dp[i-1][j]
else:
dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i-1]]+v[i-1])
return dp[-1][-1]
# 压缩空间
def solve2(vlist,wlist,totalWeight,totalLength):
resArr = np.zeros((totalWeight)+1,dtype=np.int32)
for i in range(1,totalLength+1):
for j in range(totalWeight,0,-1):
if wlist[i] <= j:
resArr[j] = max(resArr[j],resArr[j-wlist[i]]+vlist[i])
return resArr[-1]
v = [60,100,120]
w = [10,20,30]
weight = 50
Solution().bag(w,v,weight)
与LeetCode上的题不同,这里需要同时加上+-*/和括号,可以使用栈+递归解决。
def getRes(resSoFar, sign, num):
if sign == '+':
return resSoFar + num
elif sign == '-':
return resSoFar - num
elif sign == '*':
return resSoFar * num
elif sign == '/':
return int(resSoFar / num)
def calculate(s: str) -> int:
sign = '+'
num = 0
resCur = 0
res = 0
n = len(s)
i = 0
while i < n:
ch = s[i]
if ch.isdigit():
num = num*10 + int(ch)
elif ch == '(':
cnt = 1
j = i + 1
while j < n:
if s[j] == '(':
cnt += 1
elif s[j] == ')':
cnt -= 1
if cnt == 0:
num = calculate(s[i+1:j])
break
j += 1
i = j
if ch in "+-*/" or i == n - 1:
resCur = getRes(resCur, sign, num)
if ch in "+-" or i == n - 1:
res += resCur
resCur = 0
sign = ch
num = 0
i += 1
return res
print(calculate('2+3*4'))
分治解决。
class Solution:
def __init__(self):
self.max_res,self.min_res = -float('inf'),float('inf')
def getMaxMin(self,nums):
if not nums:return
self.helper(nums,0,len(nums)-1)
return self.max_res,self.min_res
def helper(self,nums,left,right):
if right <= left+1:
if nums[left] < nums[right]:
self.max_res = max(self.max_res,nums[right])
self.min_res = min(self.min_res,nums[left])
else:
self.max_res = max(self.max_res,nums[left])
self.min_res = min(self.min_res,nums[right])
else:
mid = left+((right-left)>>1)
self.helper(nums,left,mid)
self.helper(nums,mid,right)
nums = [2,3,4,5,3,8,10,1]
Solution().getMaxMin(nums)
循环整个数组,采用hash存储索引i和到i时总和sun。那么,只要有sum-k在hash表中,即说明hash[sum-k]...i总和为k。
class Solution:
def maxLength(self,nums,k):
dic = {}
dic[0] = -1 #代表没有记录的时候index
res = sum_ = 0
for i in range(len(nums)):
sum_ += nums[i]
if sum_-k in dic:
res = max(res,i-dic[sum_-k])
if sum_ not in dic:
dic[sum_] = i
return res
nums = [2,3,-1,1,3,4,2]
k = 6
Solution().maxLength(nums,k)
参考blog。最终时间复杂度可以达到O(N)。
分两步解决:
#---> 可以O(N)
class Solution:
def maxLength(self,nums,k):
min_value = [0 for i in range(len(nums))]
max_index = [0 for i in range(len(nums))]
for i in reversed(range(len(nums))):
if i==len(nums)-1:
min_value[i] = nums[i]
max_index[i] = i
else:
if min_value[i+1] <= 0:
min_value[i] = nums[i]+min_value[i+1]
max_index[i] = max_index[i+1]
else:
min_value[i] = nums[i]
max_index[i] = i
sum_ = 0
start = end = 0
res = 0
for i in range(len(nums)):
while end < len(nums) and sum_ + min_value[end]<=k:
sum_ += min_value[end]
end = max_index[end]+1
if end > i:
sum_ -= nums[i]
res = max(res, end - i)
end = max(end, i + 1)
return res
nums = [2,3,-1,1,3,4,2]
k = 6
Solution().maxLength(nums,k)
递归实现。
class Solution:
def reverseStack(self,stack):
if not stack:
return
i = self.getAndRemoveLastElement(stack)
self.reverseStack(stack)
stack.append(i)
return stack
def getAndRemoveLastElement(self,stack):
i = stack.pop()
if not stack:
return i
else:
last = self.getAndRemoveLastElement(stack)
stack.append(i)
return last
nums = [1,2,3,4,5]
Solution().reverseStack(nums)
即对于每个数求左边比他小的数的总和。
例如数组【1,3,5,2,4,6】得小和为0+1+4+1+6+15=27
【1,3,5,4,2,6】得小和为0+1+4+4+1+15=25
可以用分治的思想解决。
class Solution:
def getSmallSum(self,nums):
if not nums:
return 0
return self.func(nums,0,len(nums)-1)
def func(self,s,lo,hi):
if lo == hi:
return 0
mid = lo+(hi-lo)//2
return self.func(s,lo,mid)+self.func(s,mid+1,hi)+self.merge(s,lo,mid,hi)
def merge(self,a,lo,mid,hi):
smallSum = 0
i,j = lo,mid+1
aux = a[:]
for k in range(lo,hi+1):
if i>mid:
a[k] = aux[j]
j+=1
elif j>hi:
a[k] = aux[i]
i+=1
elif aux[j] < aux[i]:
a[k] = aux[j]
j+=1
else:
smallSum += aux[i] * (hi-j+1)
a[k] = aux[i]
i+=1
return smallSum
nums = [1,3,5,4,2,6]
Solution().getSmallSum(nums)
分治求解。
class Solution():
def InversePairs(self, data):
if not data:
return 0
return self.func(data,0,len(data)-1) %1000000007
def func(self,s,lo,hi):
if hi <= lo:return 0
mid = lo+(hi-lo)//2
return self.func(s,lo,mid)+self.func(s,mid+1,hi)+self.merge(s,lo,mid,hi)
def merge(self,a,lo,mid,hi):
smallSum = 0
i,j = lo,mid+1
aux = a[:]
for k in range(lo,hi+1):
if i>mid:
a[k] = aux[j]
j+=1
elif j>hi:
a[k] = aux[i]
i+=1
elif aux[j] < aux[i]:
smallSum += mid-i+1
a[k] = aux[j]
j+=1
else:
a[k] = aux[i]
i+=1
return smallSum
nums = [1,2,3,4,5,6,7,0]
Solution().InversePairs(nums)
实际上可以转化为数组最大累加和问题。
#O(n^2 * m)
class Solution():
def maxMatrix(self,matrix):
m,n = len(matrix),len(matrix[0])
res = -float('inf')
for i in range(m):
nums = matrix[i]
res = max(res,self.maxSubArray(nums))
for j in range(i+1,m):
for k in range(n):
nums[k] += matrix[j][k]
res = max(res,self.maxSubArray(nums))
return res
def maxSubArray(self, nums):
if not nums:
return 0
curSum = maxSum = nums[0]
for i in range(1,len(nums)):
curSum = max(nums[i], curSum + nums[i])
maxSum = max(maxSum, curSum)
return maxSum
matrix = [[1,2,3,4,5,6,7,0]]
Solution().maxMatrix(matrix)
可以求两个辅助数组,即从左到右的最大连续子数组和,和从右到左的最大连续子数组和,最后将两个子数组根据索引联合求最大和即可。
class Solution():
def TwoSubarrayMaxSum(self,nums):
N = len(nums)
maxLeft = [0 for i in range(N)]
maxRight = [0 for i in range(N)]
maxsum = cur = 0
for i in range(N):
maxsum = max(maxsum+nums[i],nums[i])
cur = max(maxsum,cur)
maxLeft[i] = cur
maxsum = cur = 0
for i in reversed(range(N)):
maxsum = max(maxsum+nums[i],nums[i])
cur = max(maxsum,cur)
maxRight[i] = cur
res = 0
for i in range(N-1):
res = max(res,maxLeft[i]+maxRight[i+1])
return res
nums = [1,2,0,4,-1]
Solution().TwoSubarrayMaxSum(nums)
因为左部分最大值和右部分最大值必然包括全局最大值,左部分中的最大值减去右部分的最大值的绝对值可以看做是全局最大值减去左部分最大值或者是全局最大值减去右部分最大值。又因为且左部分最大值必然比第一个数大,右部分最大值必然比最后一个数大。这种情况下,左部分最大值取第一个数最小,右部分最大值取最后一个数最小。
class Solution:
def getMaxMeign(self,nums):
if not nums:
return
max_num = max(nums)
min_num = min(nums[0],nums[-1])
return max_num- min_num
nums = [4,7,2,3,5]
Solution().getMaxMeign(nums)
使用双端队列 求滑动窗口内的最大值最小值
两个结论:
#O(N),L,R均不回退
class Solution():
def getRes(self,nums,k):
if not nums:
return
ma = []
mi = []
res = l = r = 0
#l----r的窗口,求最大值最小值
while l<len(nums):
while r<len(nums):
while ma and nums[r] >= nums[ma[-1]]:
ma.pop()
while mi and nums[r] <= nums[mi[-1]]:
mi.pop()
ma.append(r)
mi.append(r)
if nums[ma[0]] - nums[mi[0]] > k:
break
r+=1
if ma[0] == l:
ma.pop(0)
if mi[0] == l:
mi.pop(0)
res += r-l
l+=1
return res
nums = [4,7,2,3,5]
Solution().getRes(nums,2)
分三种情况:
class Solution():
def getRes(self,head1,head2):
loop1 = self.detectCycle(head1)
loop2 = self.detectCycle(head2)
if not loop1 and not loop2:
return self.getIntersectionNode(head1,head2)
if loop1 and loop2:
return self.getSameNodeFromLoop(head1,loop1,head2,loop2)
return
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
node = headA
l1 = l2 = 0
while node:
node = node.next
l1 += 1
node = headB
while node:
node = node.next
l2 += 1
if l1>l2:
for i in range(l1-l2):
headA = headA.next
if l1<l2:
for i in range(l2-l1):
headB = headB.next
while headA and headA!=headB:
headA = headA.next
headB = headB.next
return headA
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
# if not pHead or not pHead.next:return
if not head or not head.next:
return
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
tmp = head
while tmp != slow:
tmp = tmp.next
slow = slow.next
return tmp
return
def getSameNodeFromLoop(self,head1,loop1,head2,loop2):
if loop1 == loop2:
return self.getIntersectionNodeFromLoop(head1, head2,loop)
node = loop1.next
while node!=loop1:
if node == loop2:
return [loop1,loop2]
node = node.next
return
def getIntersectionNodeFromLoop(self, headA, headB,loop):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
node = headA
l1 = l2 = 0
while node!=loop:
node = node.next
l1 += 1
node = headB
while node!=loop:
node = node.next
l2 += 1
if l1>l2:
for i in range(l1-l2):
headA = headA.next
if l1<l2:
for i in range(l2-l1):
headB = headB.next
while headA!=headB and headA!=loop:
headA = headA.next
headB = headB.next
return headA
一个圆分成N个扇形,用m种颜色上色,要求相邻两个颜色不同,求有多少种不同的方法。
参考染色问题——扇形涂色,使用公式推导法,推导出求解公式,直接代入公式进行计算就可以。推导如下:
首先,设A(n)为最后的结果。那么
对第 A1 块扇形,有m种画法,
对第 A2 块扇形,有m-1种画法,
对第 A3 块扇形,有m-1种画法,
(因为只要求相邻的两块颜色不同,所以,只需要看前边的一块就可以了)
…………
…………
对第 An 块扇形,有m-1种画法,
即为 \(m?(m?1)^{n?1}\)
但是这时候要分成两类:
1、\(A_n\) 和 \(A_1\)不同色;
2、\(A_n\) 和 \(A_1\)同色。
当颜色不同时符合题目要求,没有影响,
而当两块相同时,可以将两块看作是一块,这个时候染色方法总数就是\(A(n?1)\),这里可能会有一点点绕,可以想象一下,前面的组合排列方式不变,头尾两块变成了一块,我们需要把这种情况的组合都排除出去。
则,\(A(n)+A(n?1)=m?(m?1)^{n?1}\)
两边同时减去\((m?1)^n\),
得\(A(n)?(m?1)^n=?[A(n?1)?(m?1)^{n?1}]\),
得
\[
A(n)?(m?1)^n=?[A(n?1)?(m?1)^{n?1}] \=(?1)^2[A(n?2)?(m?1)^{n?2}] \=(?1)^3[A(n?3)?(m?1)^{n?3}] \=........ \=(?1)^{n?2}[A(2)?(m?1)^2] \(只剩两块时,m种颜色,共有A(2)=m?(m?1)种) \=(?1)^{n?2}[m?(m?1)?(m?1)^2] \=(?1)^n(m?1)
\]
故,\(A(n)=(?1)^n(m?1)+(m?1)^n\)
推导完成,后面的代码反而不重要了。在这种情境中,数学推导能力是非常重要的。
标签:first hnu fast rom n+1 list inline star 比较
原文地址:https://www.cnblogs.com/hellojamest/p/11711509.html