'''
第二次做这个题,我对这个题完全一点印象都没有,所以怎么办呢?
当然是再想一遍白
'''
'''
思路1:
可以看到,找到具有最大和的连续子数组
--也就是说如果你从右往左找最大的,那么一定是绕不过每一个数的(除非不选)
如:[-2,1,-3,4,-1,2,1,-5,4]
从右往左看:
[2,4,3,6,2,3,1,-1,4]
就是如果你想要过去这个位置,就一定要看一下后面能不能变大,就是说后面的该位置的最大值是不是正数是不是
我能走下去的依据
'''
#这个代码的本质是动态规划
#还有一种分治的写法,不过感觉挺难,先放着
def fun1(nums):
max=nums[len(nums)-1]
for i in reversed(range(len(nums)-1)):
if nums[i+1]>0:
nums[i]=nums[i]+nums[i+1]
if nums[i]>max:
max=nums[i]
return max
fun1([-2,1,-3,4,-1,2,1,-5,4])
====================================================================================================================================================================================================================================
198. 打家劫舍
'''
首先我是一个专业的小偷,那我要怎么偷,才可以偷的最多?
因为两间相邻的房屋,不能在一晚上同时被偷,所以我要好好指定策略
--能偷的时候一定要偷(基本素养问题哈哈哈)
--那么既然我见人就偷,不就只有两种情况了第一次偷,或者第一次不偷?
'''
'''
方法一:
既然见人就偷,才有美好明天(金额最大),那么
只能分为两种情况,第一次偷和第一次不偷
就直接一次遍历结束掉
试了一下,有个问题,我没有考虑到,就是如果一群穷人中有一个富人,那么我见到穷人就偷,富人不就偷不到了?
这样的话亏的反而更多
'''
def fun1(nums):
s=0
l=1
sum_s=0
sum_l=0
while s<len(nums) or l<len(nums):
if s<len(nums):
sum_s+=nums[s]
if l<len(nums):
sum_l+=nums[l]
s+=2
l+=2
return max(sum_s,sum_l)
print(fun1([2,1,1,2]))
'''
像这种最优解问题,动态规划是非常有效的
首先能偷就偷是一定的,在这个房子里面偷了,有赚的,不偷毛都没有
max(f(i))=f(i)+max(f(i+2<i))
'''
def fun2(nums):
if len(nums)==1:
return nums[0]
n=len(nums)
ls=[0]*n
ls[n-1]=nums[n-1]
ls[n-2]=nums[n-2]
max_=max(ls[n-1],ls[n-2])
for i in reversed(range(len(nums)-2)):
ls[i]=nums[i]+max(ls[i+2::])
if max_<ls[i]:
max_=ls[i]
return max_
print(fun2([2,1,1,2]))
'''
这里其实可以再化简一下,每次还额外的查找max(ls[i+2::])其实不需要,那个变量或者列表记录就好
'''
====================================================================================================================================================================================================================================
384. 打乱数组
这里有个名字听起来很高级的算法,但其实逻辑很简单
就是迭代的过程中,减少了列表的修改所需时间
就是无论如何,把剩下没有抽取的"签"放在一起,等我抽就行了,已经抽了的
签在也看不到就好了
'''
这是是一类设计题目:
我现在需要设计一个类:
其中包含:
--一个初始化函数
--一个返回初始状态函数
--一个返回随机打乱数组函数
'''
'''
这类题目我做的并不多,所以可能会出现问题
我就先试试random的随机打乱
不用试了,他需要返回随机打乱所有的可能结果
那么random本身属于伪随机,根本做不到
'''
'''
昨天,因为没有什么思路,看了一下答案的解析,发现了他是进行抽签
抽签(无放回)中每一个签被抽到的概率是等可能的,1/n所以就可以
把所有情况随机出来
'''
import random
class Solution:
def __init__(self, nums):
self.nums=nums
self.arr=nums.copy()
def reset(self):
self.nums=self.arr.copy()
return self.nums
def shuffle(self):
#返回打乱后的数组
shuzhu=self.nums.copy()
for i in range(len(self.nums)):#总共抽取的次数
self.nums[i]=shuzhu.pop(random.randint(0,len(shuzhu)-1))
return self.nums
nums=[[0,-12893,128384]]
ls=list(nums)
ls[0][1]=10
print(nums)
#Fisher-Yates 洗牌算法
#这种算法,是很像暴力枚举算法的,但是避免掉了每次迭代过程中,修改列表的时间
'''
基本思想:
--就像抽签一样,每次抽一个,已经被抽取过的不能抽了
--那么我就需要对已经抽取过的进行删除
--上面的删除是真删除,对列表进行了修改
--但其实有更好的方法:
假设有一数组:[0,-3,-4,9,5,1],当前位置为cur,抽取元素下标为:index,此时cur还没动等于0
第一次(0,len):数组中每个元素都能取,假设取index=3,与当前位置交换变为:[9,-3,-4,0,5,1],cur+1
第二次(1,len):....
...
不断的迭代
这就相当于不放回的抽取
'''
import random
class Solution1:
def __init__(self, nums):
self.nums=nums
self.arr=nums.copy()
def reset(self):
self.nums=self.arr.copy()
return self.nums
def shuffle(self):
le=len(self.nums)-1
for i in range(len(self.nums)):#总共抽取的次数
index=random.randint(i,le)
self.nums[i],self.nums[index]=self.nums[index],self.nums[i]
return self.nums
====================================================================================================================================================================================================================================
155. 最小栈
'''
这个题目怎么说?
刚学数据结构的应该就会写
增长一下自信心
最小栈:
肯定也是一个栈,只不过会有一个返回最小元素的函数
'''
'''
push(x) —— 将元素 x 推入栈中。
pop()—— 删除栈顶的元素。
top()—— 获取栈顶元素。
getMin() —— 检索栈中的最小元素。
如果在别的语言中写,你可能会遇见栈的空间不够用的情况
这时候需要重新开空间
但是在python中,他写的好,你不够用的时候会自动帮你开
'''
class MinStack:
def __init__(self):
self.min=float('inf')#设置成正无穷,这样一定会改变
self.nums=[]
def push(self, val: int) -> None:
self.nums.append(val)
if self.min>val:
self.min=val
def pop(self) -> None:
val=self.nums.pop()#删除栈顶元素
if val==self.min:
#需要再次找到栈中的最小元素
#self.min=min(self.nums),栈可能会空,需要判断一下
if self.nums==[]:
self.min=float('inf')
else:
self.min = min(self.nums)
def top(self) -> int:
return self.nums[len(self.nums)-1]
def getMin(self) -> int:
return self.min
'''
上面的写法是取巧了,正常的方式应该是牺牲空间来
达到记录最小元素的情况,这里不需要进行排序
而是利用栈的性质:
如果在元素a入栈时,b,c,d已经在栈中,那么可想而知,a不出栈,b,c,d也是不可能出栈的
那么使用另外一个栈(辅助栈),每次入栈的都是最小值,每次主栈要出栈时,辅助栈也出
'''
class MinStack1:
def __init__(self):
self.stack=[float('inf')]
self.nums=[]
def push(self, val: int) -> None:
self.nums.append(val)
self.stack.append(min(val,self.stack[-1]))
def pop(self) -> None:
self.nums.pop()#删除栈顶元素
self.stack.pop()
def top(self) -> int:
return self.nums[-1]
def getMin(self) -> int:
return self.stack[-1]
'''
看到一个解放,不使用额外的空间,也没有用顺序查找
采用了,栈中记录当前元素与最小值的差,并随时对最小值进行更改
'''
class MinStack2:
def __init__(self):
self.min=float('inf')
self.nums=[]
def push(self, val: int) -> None:
diff=val-self.min#当前入栈元素,与最小值的差
self.nums.append(diff)
if diff<0:
self.min=val
def pop(self) -> None:
diff=self.nums.pop()#删除栈顶元素
#这里我需要判断一下,最小值是否发生改变
if diff<0:
top=self.min
#说明刚才入栈的时候,他比最小值小,所以变成了最小值,所以需要对最小值进行更改
self.min=self.min-diff
else:
top=self.min+diff
return top
def top(self) -> int:
if self.nums[-1]<0:
return self.min
else:
return self.min+self.nums[-1]
def getMin(self) -> int:
return self.min
====================================================================================================================================================================================================================================
412. Fizz Buzz
'''
?????
这也太看不起我了,这题目
?????
卧槽,我看了一下,这个题解下面的文章
离谱?
这个,写不出来,写出来要十几分钟?
妈的
没赶上好时代
'''
def fun1(n):
s_ls=[]
for i in range(1,n+1):
if i%15==0:
s_ls.append('FizzBuzz')
elif i%3==0:
s_ls.append('Fizz')
elif i%5==0:
s_ls.append('Buzz')
else:
s_ls.append(str(i))
return s_ls
====================================================================================================================================================================================================================================
204. 计数质数
这里面有两种算法埃式筛和线性筛
都是很不错的寻找质数的算法,并且线性筛还可以用在别处
'''
题目要求:
统计所有小于非负整数 n 的质数的数量。
质数:
除了1和他本身外,不能被其他数整除
1不是质数
'''
'''
方法一:
不管3,7二十一
先暴力,看能不能把题解出来
'''
def fun1(n):
su = 0 # 进行统计
for i in range(2, n):
for j in range(2, i):
if i % j == 0:
break
else:
su += 1
return su
#哈哈哈,不出以外,超时了
'''
现在我们来想一想,如何化简
我的循环是从2开始,那么除2以外还有 偶数 可能是质数吗,不可能有吧?,那么可以省一半的循环
'''
def fun2(n):
su = 0 # 进行统计
if n >= 3:
su += 1
for i in range(2, n):
if i % 2 != 0:
for j in range(2, i):
if i % j == 0:
break
else:
su += 1
return su
'''超出时间限制'''
'''
我们可以看到,如果一个数x是质数,那么他的2x,3x,4x......都不会是质数
如果一个数x不是质数,那么它本身,他的2x,3x,4x....都不会是质数
基本思路:
我需要进行几个记录,不然后面的数还是要走一遍
那么就需要开空间
'''
def fun3(n):
su = 0 # 进行统计
ls = [0] * n # 开空间进行记录
for i in range(2, n):
if ls[i] == 0: # i是当前数,i的整数被不可能为质数
beishu = 2
x = beishu * i
while x < n:
ls[x] = 1
beishu += 1
x = beishu * i
for j in range(2, i): # 判断当前数是不是质数
if i % j == 0:
break
else:
su += 1
return su
'''
超出时间限制
'''
'''
这里是还可以继续化简的:
原因在于当前数的n倍可能已经被走过了
'''
def fun4(n):
su = 0 # 进行统计
ls = [0] * n # 开空间进行记录
for i in range(2, n):
if ls[i] == 0: # i是当前数,i的整数被不可能为质数
x = i* i
while x < n:
ls[x] = 1
x = x+i
for j in range(2, i): # 判断当前数是不是质数
if i % j == 0:
break
else:
su += 1
return su
'''
我看了下答案,发现他直接没有通过循环判断是不是质数
而是列表对应数等于0时直接加
为什么?
'''
'''
吐了,提交通过了,我们来想一下为什么
埃氏筛
'''
import time
start=time.time()
def fun5(n):
su = 0 # 进行统计
ls = [0] * n # 开空间进行记录
for i in range(2, n):
if ls[i] == 0: # i是当前数,i的整数被不可能为质数
su+=1 #我觉得这是看规律可以得到的,未排除的数就是可以直接加的
#当然,更数学的是这个样子
#当前数(i)列表的下标对应元素为0,也就是说,i不是2~~i-1中任意一个的倍数
#即i无法整除2~~i-1中的任意一个,所以i是质数
#对就是这样
x = i* i #这样可以避免重复记录
#为什么?
#当前数为2,会把所有2的倍数筛选出来
#当前数为3,会把所有3的倍数筛选出来
#....
#这样基本不会出现重复问题
#而如果采用倍数问题2*i,3*i,4*i....
#每一次都会经过2的倍数,3的倍数,4的倍数...
#这样的话,会有大量重复记录
while x < n:
ls[x] = 1
x = x+i
return su
'''
线性筛:
优化方向:每个合数只被标记一次
因为,前面的
基本思路:
--标记过程不再仅当x为质数时才进行,而是对每个整数x,都进行
--对于整数x,不再标记其所有的倍数 x*x, x*(x+1) .....
而是只标记,质数集合中的数,与x相乘的数
即:x*质数1,x*质数2,x*质数3......
并且在x%质数i==0时停止
*****因为这里的x不一定为质数
如果是质数,会在超出范围后停止
核心点:
如果x%质数i==0,那么对于合数y=x*质数i+1他一定会在遍历到(x//质数i)*质数i+1这个数时被标记
这里x是可以整除质数i,所以(x//质数i)*质数i+1最后一定会乘以质数i的可以得到y
保证每一个合数,只能被其最小质因数得到
个人理解:
合数会有多个质因子
'''
def fun6(n):
ls=[0]*n
zhishu=[]
for i in range(2,n):
if ls[i]==0:#这个和前面是一样的,i位置的2--i-1都不能整除他
zhishu.append(i)
for j in zhishu:#代码在leetcode里面运行不通过,我发现是因为,她会每次执行这个循环
#然后会导致,多次循环,但什么都不干
if i*j<n:
ls[i*j]=1
if i%j==0:#结束掉循环,因为y=i*j+1(这个是指位置+1),而这在后面一定会遇到(y),所以不会重复
break
return len(zhishu)
def fun7(n):
ls=[0]*n
zhishu=[]
for i in range(2,n):
if ls[i]==0:#这个和前面是一样的,i位置的2--i-1都不能整除他
zhishu.append(i)
for j in zhishu:
if i*j<n:
ls[i*j]=1
else:
break
if i%j==0:#结束掉循环,因为y=i*j+1(这个是指位置+1),而这在后面一定会遇到(y),所以不会重复
break
return len(zhishu)
print(fun6(10))
print(time.time()-start)
====================================================================================================================================================================================================================================
326. 3的幂
'''
这种题目,或者说数学问题,基本上都是先暴力,然后再看可不可以优化思路
基本不存在数学公式解决问题,哈哈哈如果能解决,一般也不会让你写代码
'''
'''
方法一:
直接暴力,先剔除小于等于0的数
'''
def fun1(n):
if n<=0:
return False
while n>=3:
if n%3!=0:
return False
n=n//3
if n==1:
return True
else:
return False
====================================================================================================================================================================================================================================
13. 罗马数字转整数
'''
这个题目我做过,s中仅仅包含字符(I,V,X,L,C,D,M)
I可以放在V和X,来表示4和9
X可以放在L和C,来表示40和90
C可以放在D和M,来表示400和900
然后这个题目说,s 是一个有效的罗马数字,所以不会出现I在L的左边的情况
'''
def fun1(s):
#可以用双指针,i和i+1比较,
#如果[i]小于[i+1]可以用[i+1]-[i]然后加上去
#如果[i]大一些就不需要了
dic={
'I':1,
'V':5,
'X':10,
'L':50,
'C':100,
'D':500,
'M':1000
}
su=0
i=0
while i<len(s)-1:
z=dic[s[i]]
z1=dic[s[i+1]]
if z<z1:
su=su-z
i+=1
else:
su+=z
i+=1
su+=dic[s[-1]]
return su
print(fun1('IV'))
====================================================================================================================================================================================================================================
191. 位1的个数
没写出来,现在也不想看了
'''
位1的个数:
这个题目给我的第一感觉,是依靠补码,反码,等来计算
第二感觉是转化成字符串的循环
'''
'''
方法一:
直接暴力,转化成字符串循环就好,反正常数级别
'''
def fun1(n):
su=0
n=str(n)
for i in n:
su+=eval(i)
return su
print(fun1(0b00000000000000000000000000001011))
====================================================================================================================================================================================================================================
876. 链表的中间结点
'''
这个题目就是直接快慢指针写就好了
'''
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def fun1(head):
#从head的下一个,第二个节点开始
if head==None or head.next==None:
return head#直接返回了
s=head.next
q=head.next
while q.next and q.next.next:
s=s.next
q=q.next.next
return s
====================================================================================================================================================================================================================================
24. 两两交换链表中的节点
'''
这个题目的要求是,你不能只换值,你要换的是链表(如果真的在笔试,就直接换值就好了)
但现在,我需要学习所以,就改变列表的结构
'''
'''
这种链表题目,没别的,就是修改指针,就好了
对于s处于头部时
s.next=q.next
q.next=s
对于s不处于头部是
s.next=q.next
q.next=s
pre.next=q#这里pre是s的上一位,如果不走这一步,那么q.next是s,pre.next也是s
'''
def fun1(head):#已通过
if head==None or head.next==None:
return head
#这是第一次转换,因为与其他不一样就单独拿出来了
s=head
q=head.next
s.next=q.next
q.next=s
head=q
while s.next.next and s.next:
q = s.next.next
s = s.next
s.next=q.next
q.next=s
pre.next=q
pre=s
return head
====================================================================================================================================================================================================================================
725. 分隔链表
'''
分隔链表:
--这个题目要求分隔中的每一部分都是连续的片段,不然可以直接一次循环,直接结束
--所以可以第一次先遍历一遍链表,并进行计数得到n
--n//k得链表被分成了多少份
--n%k得余数,这个余数会分配给第一个链表分段到后面得,直到没有
'''
def fun1(head,k):
ls = []
pre = head
jishu = 0
while pre:
jishu += 1
pre = pre.next
zhengshu = jishu // k
yushu = jishu % k
for i in range(k): # 分k段
cur = head
shuzhi = 0
if (yushu > 0):
while shuzhi < zhengshu:
cur = cur.next
shuzhi += 1
yushu -= 1
ls.append(head)
head = cur.next
cur.next = None
else:
if head == None:
ls.append(None)
else:
while shuzhi < zhengshu - 1:
cur = cur.next
shuzhi += 1
ls.append(head)
head = cur.next
cur.next = None
return ls
====================================================================================================================================================================================================================================
'''
哈哈哈,啥东西都喜欢搞个回文
'''
'''
这个题目的思路很简单:
循环+栈
第一遍的时候循环记录
第二遍的时候开始比对
没办法,只有头节点
'''
def fun1(head):
stack=[]
node=head
while node:
stack.append(node.val)#这里保存值就行了,因为最后比的就是这个
node=node.next
#这里不想再出栈了,直接双指针(左右指针)
left=0
right=len(stack)-1
while left<right:
if stack[left]!=stack[right]:
#左右不相等
return False
left+=1
right-=1
return True
'''
哈哈哈,题目问我能不能用o(n)的时间复杂度和o(1)的空间复杂度
我写上一个写法的时候突然想到,转换
--第一次的循环是不可避免的,这是由于链表的性质导致
--那么想一下我第一次的循环干什么?
记录呗,一定是需要记录的,没毛病吧?
那么用栈可以记录用字符串是不是也可以?
那么字符串是不是可以转整数(他只有数字)?
--字符串记录,转整数
--然后变成了判断该数是不是回文数的问题
没毛病吧,常数级别的空间吧?
'''
def fun2(head):
st=''
while head:
st+=str(head.str)
head=head.next
#我可以转换成数字,然后开始判断是不是回文数,但是偷个懒吧
shuzhi=eval(st)
left=0
right=len(st)-1
while left<right:
if st[left] != st[right]:
# 左右不相等
return False
left += 1
right -= 1
return True
'''
果然这种操作还有经典的快慢指针
quick=quick.next.next
slow=slow.next
一个一次走两个,一个一次走一个,那么可以看到quick到了尾部,slow刚好到达中部
这样将slow和quick之间的链表进行链表反转,继续比较,这样什么空间都没有开
哈哈哈,流弊(因为上面其实有部分空间开了,只不过数据后面不用了,一切都只是假象哈哈哈)
'''
====================================================================================================================================================================================================================================
'''
方法一:
直接暴力
cur为当前数
然后不断从右向左遍历,找到[i]==[cur]
然后开始同时动,不满足回退
直到cur==i时结束,cur+1
'''
def fun1(s):#提交通过,离谱
len_=len(s)
if len_==1:
return s
#s中至少有两个元素
begin=0
max_=1
for cur in range(len_):#注意是否存在溢出情况
for j in range(cur+1,len_):#注意是否存在溢出情况
if j-cur+1>max_ and huiwen(cur,j,s):
max_=j-cur+1
begin=cur
print(s[begin:begin+max_:])
def huiwen(left,right,s):
while left<right:
if s[left]!=s[right]:
return False
left+=1
right-=1
return True
def fun2(s):#运行超时,最后一个测试用例没通过
len_=len(s)
max_=''
for cur in range(len_):
i=len_-1
chongx=cur
huitui=i
while cur<i:
if s[cur]!=s[i]:
i=huitui-1
huitui=i
cur=chongx
else:
i-=1
cur+=1
if len(max_)<=len(s[chongx:huitui+1:]):
max_=s[chongx:huitui+1:]
print(max_)
def fun3(s):#中心扩散方法,一共有n个中心,每个中心o(n)的时间复杂度
len_ = len(s)
# 不知道有没有边界条件
if len_ == 1:
return s
begin = 0
max_ = 1
for i in range(0, len_ - 1):
jhw_begin, jhw_max = jhw(i, s)
ohw_begin, ohw_max = ohw(i, i + 1, s)
cur_max = max(ohw_max, jhw_max)
if jhw_max < ohw_max:
if max_ < ohw_max:
begin = ohw_begin
max_ = ohw_max
else:
if max_ < jhw_max:
begin = jhw_begin
max_ = jhw_max
return s[begin:begin + max_:]
def jhw(zhongxing,s):
len_=len(s)
left=zhongxing-1
right=zhongxing+1
while left>=0 and right<len_:
if s[left]!=s[right]:
return left+1,right-left-1#因为这里[left]!=[right]所以left和right是不可取的
left-=1
right+=1
return left+1,right-left-1
def ohw(left,right,s):
len_=len(s)
while left>=0 and right<=len_:
if s[left]!=s[right]:
return left+1,right-left-1
left-=1
right+=1
return left+1,right-left-1