我也不知道为什么这个可以通过,没细想.
我是采用两个一比对,这样的话需要看一看最后一位取的到吗?
# 首先的想法:
# 1.他已经说明输入一定为正确的罗马数字(字符串形式)
# 2.将六种特殊情况现在字符串中排查一遍,转化成数字,并在字符串中删除
# 3.将其他字符将字符串中转化为数字
def fun1(s):
#创建一个字典
dic_liu={
'IV':4,'IX':9,'XL':40,'XC':90,'CD':400,'CM':900}
dic_={
'I':1,'V ':5,'X ':10,'L':50,'C':100,'D':500,'M':1000}
#用来求和的变量
sum_=0
#用来记录当前位置的变量
i=0
if len(s)==1:
return dic_[s]
while i<len(s)-1:
key=s[i:i+2:]
print(key)
if key in dic_liu.keys():
sum_+=dic_liu[key]
if i+2==len(s)-1:
sum_+=dic_[s[i+2]]
i+=2
else:
sum_+=dic_[key[0]]
if i+1==len(s)-1:
sum_+=dic_[s[i+1]]
i+=1
return sum_
def fun2(s):
#就是什么样的情况-,什么样的情况+
dic_={
'I': 1,
'V': 5,
'X': 10,
'L': 50,
'C': 100,
'D': 500,
'M': 1000,
}
sum_=0
n=len(s)
for i,ch in enumerate(s):
item=dic_[ch]
#输入的一定是正确的
if i<n-1 and item<dic_[s[i+1]]:#这样判断的前提是输入是''正确''的
sum_-=item
else:
sum_+=item
return sum_
=============================================================================================================================================================================================================================================================================================
#首先的想法是每一次将数组中的遍历一遍
#将首位进行比对,如果都一样,在继续比对第二位
def fun1(strs):
if len(strs)==1:
return strs[0]
first=strs[0]
for i in range(len(first)):#总共遍历数组的次数
for j in range(1,len(strs)):#把数组走一遍
if i<len(strs[j]):
if first[i]!=strs[j][i]:
#不相等时进来
#假设当前i等于2,后面的代码又是return
#说明前面都没有进来过,也就是说first的第0位和第1位与其他元素均一样
#这时候i==2在与某个元素判断时却进来了,那么就不可能继续往后面走了
return first[0:i:]
else:
#i和len(strs[j])只有两种情况小于或等于
#进来的一定是等于情况
#能进来说明此时i对应的数字大于等于当前元素的长度,所以
return first[0:i:]
return first
#又有了一种想法,先将字符串数组中的第一个和第二个拿出来比对一下
#1.如果没有共同前缀,则直接输出没有
#2.如果有公共前缀(假设为flx),那么最长公共前缀长度一定小于等于flx
#3.通过上述可以减少比对的次数
def fun2(strs):
if len(strs) == 1:
return strs[0]
# 用来记录相同元素
str = ""
# 假设一个为abcdffg
# 一个为abcd
# 只需要循环到长度最短的末尾就行了
first = strs[0]
dier = strs[1]
i = 0
while i < len(first) and i < len(dier):
if first[i] == dier[i]:
str += first[i]
else:
break
i += 1
# 得到了他俩的公共前缀
for i in range(2, len(strs)):
item = strs[i]
j = 0
panduan = ""
while j < len(item) and j < len(str):
if str[j] == item[j]:
panduan += str[j]
else:
if panduan == "":
return ""
break
j += 1
str = panduan
return str
#上面两种方法本质上都是暴力枚举
#他可以分解成更小的相似部分进行对比,所以可以使用
#递归
def fun3(strs):
right=len(strs)-1
if right==0:
#说明只有一个元素
return strs[0]
mid=right//2
leftStr,rightStr=fun3(strs[0:mid+1:]),fun3(strs[mid+1::])#这里为了防止有一个list为空所以这样切分
#返回的结果为字符串,开始两个两个的找公共部分
i=0
while i<len(leftStr) and i<len(rightStr):
if leftStr[i]!=rightStr[i]:
#这里的问题是当前的i对应的值不相等所以后面就不用看了
#前面的也没有问题所以直接return(和上面fun1()有点像的对比)
return leftStr[0:i:]
i+=1
#循环结束还没有return的话就将最短的那个输出
if len(leftStr)<len(rightStr):
return leftStr
else:
return rightStr
if __name__ == '__main__':
fun3(["flower","flow","flight"])
=============================================================================================================================================================================================================================================================================================
#首先给定的是有序的数组
#返回值是不重复元素组成数组的长度
#她会将其输出
#这也就是说,你只需要把重复的挪到后面就行了
#双指针的写法
def fun1(nums):
#数组为空单独拿出来判断
if nums==[]:
return 0
if len(nums)==1:
#只有一个元素可以直接输出
return 1
quick,slow=1,1
while quick<len(nums):
#进行一下判断
#数组是有序的,相同的元素会一窝一窝的聚集在一起
#那么当,当前的quick和quick-1对应的元素不同时
#你就到了另一窝
if nums[quick]==nums[quick-1]:
quick+=1
else:
nums[slow]=nums[quick]
slow+=1
quick+=1
return slow
=============================================================================================================================================================================================================================================================================================
#
#这个好像是双指针优化
def fun1(nums,val):
right=len(nums)-1
cur=0
while cur<=right:
if nums[cur]!=val:
#不相等
cur+=1
else:
#相等
#和right换位置
nums[right],nums[cur]=nums[cur],nums[right]
#因为你不知道换位置的那个元素,是否与val的值相等所以需要再进行一次判断
right-=1
return cur
#正常的双指针
#就是把应该输出的往前移动
#不应该输出的会随着'''应该输出'''的前移而不断后移
def fun2(nums,val):
quick=0
slow=0
while quick<len(nums):
if nums[quick]!=val:
#将其前移
nums[quick],nums[slow]=nums[slow],nums[quick]
slow+=1
quick+=1
return slow
#其实fun2的代码nums[quick],nums[slow]=nums[slow],nums[quick]是不需要的
#因为你本身slow位置的元素从来都没有通过''slow下标''被比较过
#也就是说无论你那个位置放什么都行
def fun3(nums,val):
quick=0
slow=0
while quick<len(nums):
if nums[quick]!=val:
#将其前移
nums[slow]=nums[quick]
slow+=1
quick+=1
return slow
=============================================================================================================================================================================================================================================================================================
28. 实现 strStr()
这里其实组好是使用KMP,但是我看了半天吐了
注意fun1和fun2都是暴力方法.但fun1逻辑上有些许问题,不够简洁,错误较多
fun2在80个测试点通过了78个,后面的数据他大了.
注意fun2中采用双指针时,用 slow+quick.这使得slow不需要移动,只需要移动quick
方便了回溯(当匹配不成功回到原点+1)
def fun1(haystack,needle):
if len(needle) == 0:
return 0
i = 0
while i < len(needle):
j = 0
while j < len(haystack):
if needle[i] != haystack[j]:
# 如果是不等于有两种情况
# 1.他是needle的首位元素:只需要j+1就行了
# 2.他不是needle的首位元素:你是需要退回来
if i == 0:
j += 1
else:
i = 0
else:
if i == len(needle) - 1:
return j - i
j += 1
i += 1
# 当你查完了一遍,i还等于0那么肯定是没对应的结果
if i == 0:
return -1
return j - i
def fun2(haystack,needle):
if len(needle) == 0:
return 0
slow=0
while slow+len(needle)<=len(haystack):#(slow相当于匹配的起点)
#这个变量使用了记录needle的位置
quick = 0
while quick<len(needle):
#这里使用的是slow+quick所以在这个quick<len(needle)循环中slow始终不需要
#进行数值的更改
if needle[quick]==haystack[slow+quick]:
#继续下一个字符的匹配
quick+=1
else:
slow+=1
break
if quick==len(needle):
return slow-1
#如果没进来说明
#needle的长度是大于haystack的
return -1
=============================================================================================================================================================================================================================================================================================
35. 搜索插入位置
def fun1(nums,target):
#这里使用left和right而不使用len(nums)的原因:
#需要返回下标或插入元素.使用len(nums)就必须将<<列表进行切分,和加入变量保证索引的正确>>
left=0
right=len(nums)-1
while left<right:
mid = (left + right)//2
zhongzhi = nums[mid]
if(zhongzhi==target):
#这里就可以直接返回了
#题目<<提示>>中说:nums 为无重复元素的升序排列数组
return mid
elif(zhongzhi<target):
#因为mid这个位置的值已经比较过了
#所以赋值的时候为mid+1,不需要再比较
left=mid+1
elif(zhongzhi>target):
right=mid-1
#出循环是里面只有一个元素
zhongzhi=nums[left]
if (zhongzhi == target):
# 这里就可以直接返回了
# 题目<<提示>>中说:nums 为无重复元素的升序排列数组
return left
elif (zhongzhi < target):
#这里有个问题,如果插入元素比所有元素都大
#那么一定是插入到最后面,但是这不存在溢出数组的问题
#所以可以直接插入
#将元素插进来
return left+1
elif (zhongzhi > target):
#这种情况需要注意一下
#因为是插入到你的左边,所以存在当前位置为下标0的情况
if left==0:
return 0
return left
=============================================================================================================================================================================================================================================================================================
动态规划
53. 最大子序和
fun1对应的方法是暴力破解,没有一点优化,每种情况都需要走一遍.一个203个测试点通过了201个,所以应该写的没问题,时间上需要优化
def fun1(nums):
slow = 0
max = 0
panduan = True #这里是防止第一次的到的结果是个负数,或者所有结果都是负数,导致最后输出了0
while slow < len(nums):
quick = 0
sum = 0
while slow + quick < len(nums):
sum += nums[slow + quick]
if max < sum or panduan:
#第一次不管怎么样都能进来,因为panduan为True
#这时候:
#1.sum为负数,这是需要进行更改的
#2.sum为正数,本来就要进来更改
#3.sum为0,不影响
#之后:
#只有sum的值大于max才能进来
panduan = False
max = sum
quick += 1
# while slow+quick<len(nums):循环结束,slow后移一下
slow += 1
return max
只有最后一个测试用例没通过,相对于最开始第一次的代码,更加的简洁,已读,高效
#由于有几天没有碰这一题了,所以先把暴力再写一遍
#基本思路就是设置一个最大值列表,用来记录每一个数对应的最大值
#而对于某个数的最大值,可以直接通过sum来记录
def fun2(nums):
max_list=[]
for i in range(len(nums)):
sum=nums[i]
max_sum=nums[i]
for j in range(i+1,len(nums)):#从当前元素的后一位开始计算
sum+=nums[j] #加上当前元素,可能变大也可能变小,但都不能确定后面是否会有"超级super数"
if sum>max_sum:
#进行一个更换
max_sum=sum
#当循环结束所得到的max一定是在这个过程中的最大值
max_list.append(max_sum)
return max(max_list)
动态规划
#采用动态规划的方式写,可以看到有大量的重复性计算
#从后往前,用哈希表/字典进行记录(可以是对数据<<但细细想想nums中数据是有可能重复的>>,最好是对下标,下标具有唯一性)
#连续的最大子序和,所以假设当前位置为i
#你必须经过i+1,又可知i+1对应的最大值为x,所以i对应的最大值
#只能是i本身或i+x
def fun3(nums):
n=len(nums)
#创建列表,记录下标i处对应的值
nums_copy=nums.copy()
for i in range(n-1,0,-1):#这样的话,每一次的i需要-1
nums_copy[i-1]=max(nums[i-1],nums[i-1]+nums_copy[i])
return max(nums_copy)
动态规划,递归版本
def fun4(nums):
if len(nums) == 1:
return nums[0] #因为在该种情况下返回值为数字,不是列表,所以直接拿出来单独讨论
max_list=nums.copy()
max_list=bijiao(max_list,0)
return max(max_list)
def bijiao(max_list,i):
if i==len(max_list)-1:#在遇见最后一位的时候停下来
return max_list[i]
max_list[i]=max(max_list[i],max_list[i]+bijiao(max_list,i+1))
if i==0:
return max_list
return max_list[i]
=============================================================================================================================================================================================================================================================================================
动态规划
300. 最长递增子序列
这个通过了23个测试用例,因为碰到第22个那个测试用例太大了
def fun1(nums):
#不做一点优化,每一种情况都走一遍
#即使有重复的/以前走过的也继续走下去
list1=[]
for i in range(len(nums)):
list1.append(bijiao(nums,i))
return max(list1)
def bijiao(nums,i):
max_len = 1
for j in range(i + 1, len(nums)):
if nums[i] < nums[j]:
max_len=max(max_len,bijiao(nums,j)+1)
return max_len
这个是模拟递归的非递归版本,想法和递归的思想没什么两样,就是用栈来模仿
主要问题就是代码写起来很难受
注意:错误代码
def fun2(nums):
#这种方法是f1的迭代版本
n=len(nums)
max_list=[]
for s in range(n): #0<=s<n
#创建一个栈
#一个当前s元素对应一个栈
stack=[]
q=s
i=1
max_len=1 #设置当前元素对应的最长度初始值
while q+i<n or stack:
# 记录从当前q向后走过的长度,在栈中释放元素时
len_ji = 0
if nums[q+i]>nums[q]:
#当前q入栈
#q更换位置
#i回归原来
stack.append(q)
q=q+i
i=1
len_ji+=1
continue
#如果没有进来
i+=1 #向后继续判断
#如果走到末尾,就判断栈中元素是否全部被释放
if q+i==n-1:
if stack:
#释放当前
q=stack.pop()+1
max_len=max(max_len,max_len+len_ji)
i=1
#假如说0,3,6,7是由0和3,6,7拼凑起来的
#3,6,7是由3和6,7拼凑起来的
#6,7
#----所以可以将列表从后往前遍历
#记忆化,把自己走过的路记住
def fun3(nums):
#获取nums数组长度
n=len(nums)
#以数组长度开辟空间
nums_copy=[1]*n
for i in range(n,0,-1):
for j in range(i,n): #因为nums_copy设置的默认值为1,所以nums数组尾部元素可以不进入循环,默认为1
if nums[j]>nums[i-1]:
#这样写是错误的原因在于
#[3,7,101,18]若出现这种情况在运行的3时nums_copy的整体情况为
#[1,2,1,1] 这时3,101,18均大于3,所以都会对3对应的最大长度
#造成影响
#nums_copy[i]=nums_copy[j]+1
#所以我需要最大进行比较
#就是当前该数(3)对应的最大长度和进行更改后的最大长度进行比较
nums_copy[i-1]=max(nums_copy[i-1],nums_copy[j]+1)
return max(nums_copy)
该方法是直接基于f1的改进通过字典,进行记录
def fun4(nums):
dic={
}
max_list=[]
for i in range(len(nums)):
max_list.append(bijiao1(nums,i,dic))
return max(max_list)
def bijiao1(nums,i,dic):
if i in dic.keys():
return dic[i]
else:
max_len=1
for j in range(i+1,len(nums)):
if nums[i]<nums[j]:
#在字典里面一定是最长长度
dic[j]=bijiao1(nums,j,dic)
max_len=max(max_len,dic[j]+1)
return max_len
=============================================================================================================================================================================================================================================================================================
58. 最后一个单词的长度
#这个题目感觉玩不出来什么花样,就是进行简单的if,elif的判断
def fun1(s):
sum = 0
for i in reversed(range(len(s))):
if s[i]!=" ":
sum+=1
elif s[i]==" " and sum!=0:#说明不是第一次进来
break
return sum
=============================================================================================================================================================================================================================================================================================
#第一种方法,很常见的,直接将所有数字拿出
#算出来对应的数,然后加1,在放进去(时间复杂度相对高,且并没有什么技术含量)
#第二种方法,将尾部进行加1操作后进行判断
#---如果为10将其上一位进行加1操作
#---如果该位为10,将其上一位进行加1操作
#......
#直到若为列表首位,加1后为10,则需要进位
def fun2(digits):
#首先将特殊情况999999排除
n=len(digits)
i=0
stop=False
while i<n and not stop:
if digits[i]!=9:
stop=True
if stop:
j=n-1
while j>=0:
if digits[j]+1==10:#该位不加1,让首位加1,该位变为0
#试想一下如果digits[j]+x(x为任意数)不是一定为10怎么办
#答案在最后
digits[j]=0
j-=1
continue
#执行若没进入则执行
digits[j]+=1
break
return digits
else:
#说明全部都是9,那么直接变成1000...就好了
list1=[0]*(n+1)
list1[0]=1
return list1
#(digits[j]+x)%10==1
=============================================================================================================================================================================================================================================================================================
67. 二进制求和
#第一想法二进制转化为10进制进行求和
def fun1(a,b):
#将a转化为10进制
a_len=len(a)-1
b_len=len(b)-1
a_shi=0
b_shi=0
for i in a:
a_shi+=int(i)*2**a_len
a_len-=1
for i in b:
b_shi+=int(i)*2**b_len
b_len-=1
sum_=a_shi+b_shi
#再转化成二进制
he=[]
while sum_>0:
he.append(sum_%2)
sum_//=2
sum_=0
beishu=10
for j in range(len(he),0,-1):
sum_=sum_*beishu+he[j-1]
return sum_
=============================================================================================================================================================================================================================================================================================
260. 只出现一次的数字 III
#这个题目有一种很巧妙的方法来求解
#下面就是代码
def fun1(nums):
eor=0
#首先是将数组遍历
for i in nums:
eor^=i
#因为恰好有两个元素只出现一次
#其他元素均出现偶数次(两次)
#所以通过异或后
#eor=a^b(a,b为不同数)
#eor!=0
#因为eor不为0,所以eor至少有一位(二进制存储)对应数为1
#假设该位为"第10位"
#第10位只有两种情况:0 or 1
#所以可以通过第十位将所有数字进行分类 甲类和乙类
#a,b一定一个为甲类,一个为乙类
#找一个只有"第十位"为一的数 与 甲类做与运算,可以将属于甲类的全部提前出来,这样最后的结果一定为a or b
right=eor&(~eor+1) #这里可以将最右侧的1提取出来
#eor=101011001
#~eor=010100110
#~eor+1=010100111
#eor&(~eor+1)=000000001
eor_a=0
for i in nums:
if(right&i==0):#说明该位置不为1
eor_a^=i
eor_b=eor^eor_a
list1=[eor_a,eor_b]
return list1
=============================================================================================================================================================================================================================================================================================
122. 买卖股票的最佳时机 II
#首先买入为-
#卖出为+
#这样的话这个题目就有点像最小子序和问题了
def fun1(prices):
#开辟空间
len_=len(prices)
maichu=[0]*len_
mairu=[0]*len_
smdbg=[0]*len_
for i in reversed(range(len(prices))):
if(i==len_-1):
maichu[i]=prices[i]
elif(i==0):
#只有买入情况
mairu[i]=-prices[i]+max(max(maichu[i+1::]),0)
else:
#当前位置卖出可以得到的最大利润
maichu[i]=prices[i]+max(max(mairu[i+1::]),0)
#当前位置买入可以得到的最大利润
mairu[i]=-prices[i]+max(max(maichu[i+1::]),0)
return max(mairu)
print(fun1([1,2,3,4,5]))
=============================================================================================================================================================================================================================================================================================
217. 存在重复元素
#这个题目就是给一个数组,只要有一值存在两次即以上就可以输出了
#首先的思路就是遍历数组,字典记录还是很常规的操作o(n)的时间复杂度
def fun1(nums):
dic={
}
for i in nums:
if i in dic.keys():
return True#因为说明i不是第一次出现了
else:
dic[i]=1
return False
#排序也是可以的,归并排序,快速排序都行,很基础就不写了
#排序结束后相邻数两两比对
=============================================================================================================================================================================================================================================================================================
136. 只出现一次的数字
这个题目当时在学算法的时候是靠位运算写的,不得不说是一种很好的计算方法
以后可以往这里想一想,在这里推荐一下最近在学的算法课程
一周刷爆LeetCode,算法大神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教
讲的是很好的
#这个题目在我最近学习的马士兵算法里面已经做过了,不过现在复习复习还是挺好的
'''
神奇的位运算
这里是二进制的存储方式
1、与(&),按位与运算符:参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0
同时为真,结果为真
2、或(|),按位或运算符:只要对应的两个二进位有一个为1时,结果位就为1
同时为假,结果为假
3、异或(^),按位异或运算符:当两对应的二进位相异时,结果为1
不同为1,相同为0
0与任何数异或结果都为该数本身
4、取反(~),按位取反运算符:对数据的每个二进制位取反,即把1变为0,把0变为1
按位取反
5、左位移(<<),运算数的各二进位全部左移若干位,由<<右边的数字指定了移动的位数,高位丢弃,低位补0
6、右位移(>>),把‘>>’左边的运算数的各二进制位全部右移若干位,>>右边的数字指定了移动的位数
这两个好像相当于乘以2,除以2,不过记不太清了
'''
#第一种方法使用"异或"运算符,因为一个数出现一次,其他数都出现两次,完全相同的数异或结果为0
def fun1(nums):
eor=0
for i in nums:
eor^=i
return eor
#第二种方法
#依旧是遍历数组
#依旧是哈希表
def fun2(nums):
dic={
}
for i in nums:
dic[i]=dic.get(i,0)+1#这个代码一定是先执行右边如果有就返回对应value,如果没有就返回0
#这样的到的结果你也不知道谁是1
#所以你可以将字典.items()转化成列表查找,不过感觉有些麻烦,突然想到了一种更好的方法
def fun3(nums):
dic={
}
for i in nums:
dic[i]=dic.get(i,0)+1
if(dic[i]==2):
dic.pop(i)
for i in dic.keys():
return i
=============================================================================================================================================================================================================================================================================================
350. 两个数组的交集 II
'''
题目要求:
给定两个数组,编写一个函数来计算他们的交集
注意:
这里是交集,是指只要nums1中出现该元素,且nums2中也出现该元素,则可说该元素存在于交集中
并且输出的列表中每个元素出现的次数,等同于该元素在两个列表中出现的最小次数
'''
'''
第一种方法,遍历大法+哈希表
1.遍历nums1,将结果记录在哈希表(haxi1)当中
2.遍历nums2,并查找haxi1中是否有,有则删除添加到输出列表中
'''
def fun1(nums1,nums2):#这种方法是我所喜爱的方法哈哈哈
ls=[]
dic={
}
for i in nums1:
dic[i]=dic.get(i,0)+1
#这样的话就得到了存储了所有的数据的字典
for j in nums2:
if j in dic.keys():
if dic[j]==1:#说明现在还有一个值,直接删除掉
dic.pop(j)
ls.append(j)
else:
#只能是大于1
dic[j]-=1
ls.append(j)
return ls
'''
第二种方法:
想到了一种很蠢的方法,排序+双指针的遍历
这样的话nlogn+mlogm+遍历哈哈哈
不了不了
'''
'''
第三种方法:
可以直接双指针吗??????
两个数组本身都是无序的
这就是说你只有暴力,把每种情况走一遍
因为你不知道什么时候p_nums1移动,什么时候p_nums2移动
所以这种方法可以说是暴力枚举哈哈哈
比第二种还蠢,想偷懒,不可能
'''
=============================================================================================================================================================================================================================================================================================
283. 移动零
'''
题目要求:
给定一个数组nums,编写一个函数将所有的0移动到数组的末尾,同时保持非0元素的相对顺序
'''
'''
方法一:
遇到一个删除一个,最后一起加到最后面
#这样的画每次的删除操作都是o(n)最后汇总虽然也是o(n)但是太慢了感觉
'''
def fun1(nums):
sum1 = 0 # 用来计数的
i = 0
n = len(nums)
while (i < n - sum1): # 这里不能给len(nums),因为nums的长度在减少这就是:len(nums)<=>n-sum1
if (nums[i] == 0):
nums.pop(i)
sum1 += 1
else:
i += 1
for i in range(sum1):
nums.append(0)
'''
第二种方法
开辟空间,用来存放非0数,后面再添加为0数
这样的话会快一些,但需要消耗空间
并且这个题目好像不想要你这样搞
说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。
'''
'''
那我们想一想怎么才能不额外开辟空间,同时时间复杂度还低
换位置可以吗?
试想一下,我现在的位置上对应的是个0,我可不可以循环找到第一个非0数(i)和他换位置,如果这样i与其他的相对元素位置不变
但是0现在还是不在最后面?????
哈哈哈我去看答案了,我发现就是这样,不过他用的是双指针,我是循环
当然直接双指针更好
'''
'''
改进写法,循环+单指针
1.假设当前位置为0,循环找到第一个非0数(i)和其换位置
2.直到指针溢出
需要说明的是这种写法并不好,他只是一种思路
我就是因为遍历次数过多才抛弃的
'''
def fun2(nums):
for i in range(len(nums)):
if nums[i]==0:
for j in range(i+1,len(nums)):
if nums[j]!=0:
nums[i],nums[j]=nums[j],nums[i]
break
#交换位置结束循环就好,如果为0,先啥都不干吧
'''
上面的单指针+循环主要的问题是走了太多的重复路,对于每一个元素都什么都不管的遍历
而采用双指针可以有效的避免这个问题
基本思路:
来两个指针一个慢,一个快
快的用来找,慢的用来换
在这个过程中有两种情况:
[q]=0, q+1
[q]!=0 交换q+1,s+1
假设有一数组[0,7,0,0,9,3,5]
1.s=0,[q]=7 交换[7,0,0,0,9,3,5] 此时s=1,q=2
2.都为[q]=0,只改变q,此时q=4
3.[q]=9,交换[7,9,0,0,0,3,5],此时s=2,q=5
4.交换
5.交换
...
在这个过程中观察可以看到,s始终在已经排序的最后一位,等待着q找到!=的数和他交换
所以整个过程始终是有序的,如果一直没有碰到0就是自己和自己交换
'''
def fun3(nums):
s=q=0
n=len(nums)
while q<n:
if nums[q]!=0:
nums[s],nums[q]=nums[q],nums[s]
s+=1
q+=1
=============================================================================================================================================================================================================================================================================================
1. 两数之和
'''
梦的开始,两数之和,人生第一道leetcode,今天他又回来了
要求:
给定一个整数数组nums,和一个整数目标值target,找出两个目标值(其和为target)返回对应下标
看到这个题直接两种解放上来,双指针和哈希表
'''
'''
解法1:字典+遍历
这种方法就是记录,下标及其对应的值,因为下标唯一,且数组nums中可能存在重复元素,所以使用下标做键
元组做值.不这样不好,由于下标做键,你需要遍历键值对,感觉很浪费,并且题目要求的只是返回一组结果
所以我们还是元素做键,下标做值.
我又感觉每一次查找很麻烦,不知道到底那个好,反正我写的是元素做键,下标做值
'''
def fun1(nums,target):
dic={
}
for i in range(len(nums)):
key=nums[i]
for j in dic.keys():
if j+key==target:
return i,dic[j]#如果循环正常结束,则说明没有找到
if key not in dic.keys():#不在则创建
dic[key]=i
#看了一下答案,发下最好的写法就是哈希表
#然后我发现他是这样写的,查找target-i是否在哈希表中
def fun4(nums,target):
dic = {
}
for i in range(len(nums)):
key = nums[i]
if target - key in dic.keys():#字典的查找是常数级操作
return i, dic[target - key]
# 能走到这说明没找到
dic[key] = i # 无论有没有key在哈希表中,直接赋值准没错
'''
双指针懒得写了,有点暴力的感觉,准确来说就是暴力.不知道能不能优化一下
在时间复杂度上落后太多
'''
def fun2(nums,target):
pass
'''
可不可以先排序,再二分????
因为大于目标值的是可以排除掉的选项
那么我先通过二分,找到合适的区间
再开始查找???
有个问题,记录最开始的下标
突然又想到,我都已经排序了
我直接拿target-当前数得到需要的数,然后二分查找?
这不是非常完美
那么重点来了,如何记录下标这个玩意
我不知道,一会看答案有没有记录,我就直接查找好吧,得到这两个数之后
再在未排序数组中直接查找这两个数对应的下标
他不香吗??????????
'''
def fun3(nums,target):
nums1=nums.copy()#复制一个
nums.sort()#排序,偷个懒直接用人家的,毕竟我最近刚过完排序,这个代码的核心也是二分
for i in range(len(nums1)):
j=target-nums1[i]#如果要达到target需要j
xiabiao=erfen(nums,j)
if xiabiao:#这里是说这个j在nums中是否存在,存在
#如果存在进来
for c in range(len(nums1)):
if nums1[c]==j:
if i!=c:#确定不是同一元素
return i,c
def erfen(nums,item):
l=0
r=len(nums)-1
while l<=r:
mid=(l+r)//2
if nums[mid]<item:
l=mid+1
elif nums[mid]==item:
return True
else:
r=mid-1
return False
print(fun3([3,2,4],6))
=============================================================================================================================================================================================================================================================================================
36. 有效的数独
这个题目没有做出来,存在一定的问题
今天给他做出来了10.11
注意python中创建多维数组
#这个题目是二维数组
#可以说是二维数组的查找
#找不到重复元素就是正确的
'''
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
只会出现重复和刚刚好两种情况
注意:
我只需要验证已经填入的数字是否是有效的,而不需要在意怎么解
'''
'''
方法一(暴力循环+字典):
直接暴力,这时很简单的思路假如说遍历一次是n,一共需要遍历3次,每一次遍历过程中
只需要一个字典进行记录,再说了9*9的二维数组还是很小的
'''
'''
今天是10月11日,我又一次做这个题,很快的就做出来了,但是很明显,做麻烦了
还记得上次做时判断行和列都很简单,唯独一个格子一个格子搞不出来
今天我就这个样子想的
用当前行(i)//3和当前列(j)//3
可以得到:
0,0 0,1 0,2
1,0 1,1 1,2
2,0 2,1 2,2
这样就可以以:
通过i//3与j//3的大小进行分组
'''
def fun1(board):
ge_all_list1=[[],[],[]]#对角线
ge_all_list2=[[],[],[]]#对角线上方
ge_all_list3=[[],[],[]]#对角线下方
for i in range(9): #总的遍历次数
hang_list=board[i]#第几行,对应的列表
dic_hang={
}#字典进行记录
for j in range(9):
if hang_list[j]!='.':
#说明是数字,进行一个查找,有直接报错
if hang_list[j] in dic_hang:#这就是查在不在字典里面
return False
else:
dic_hang[hang_list[j]]=0#这里只要有就行了
#这里还需要把格子元素放进去
if i//3==j//3:#这样可以证明是对角线
ge_all_list1[i//3].append(hang_list[j])
elif i//3<j//3:#如果为空所以可以放元素
ge_all_list2[i//3+j//3-1].append(hang_list[j])
elif i//3>j//3:
ge_all_list3[i//3+j//3-1].append(hang_list[j])
'''
搞列就好了
'''
for i in range(9):
dic_lie={
}
for j in range(9):
#board[j][i],这样写,只动列不动行
item=board[j][i]
if item!='.':
if item in dic_lie:
return False
else:
dic_lie[item]=0
#看格子:
for i in range(3):#里面所具有的列表个数
dic_lie1= {
}
dic_lie2= {
}
dic_lie3= {
}
item1=ge_all_list1[i]
item2=ge_all_list2[i]
item3=ge_all_list3[i]
for j in range(len(item1)):
if item1[j] in dic_lie1:
return False
dic_lie1[item1[j]]=0
for j in range(len(item2)):
if item2[j] in dic_lie2:
return False
dic_lie2[item2[j]]=0
for j in range(len(item3)):
if item3[j] in dic_lie3:
return False
dic_lie3[item3[j]] = 0
return True
print(fun1([["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]))
'''
看一下答案是怎么写的,答案只写了一个循环,但是思想没什么区别
都是行看一遍,列看一遍,格子里面的看一遍
'''
'''
注意看一格一格是这样的:
假设i是行,j是列,会有i//3,j//3
0,0 0,1 0,2
1,0 1,1 1,2
2,0 2,1 2,2
这样可以开辟一个三维数组,长这个样子
gezi=int[3][3][9]
这样可以以i//3和j//3进行划分
'''
def fun2(board):
hang=[[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0]]
#lie=[[0]*9]*9,我最开是这样开辟数组的,但是有一个问题,对一个列表的更改,会影响其他
#元素,我猜测是因为,他们其实是指向投以列表的内存地址,本质上是一个元素
lie=[[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0]]
gezi=[[[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]],
[[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]],
[[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]]]
for i in range(9):
for j in range(9):
item=board[i][j]
if item!='.':
ind=eval(item)-1
hang[i][ind]+=1#hang
lie[j][ind]+=1
gezi[i//3][j//3][ind]+=1
if hang[i][ind]>1 or lie[j][ind]>1 or gezi[i//3][j//3][ind]>1:
return False
return True
print(fun2([["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]))
ls=[[0]*9]*9#确实是指向同一个内存地址,所以牵一发而动全身
print(ls)#[[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]]
for i in ls:
print(id(i))
# 2352051450176
# 2352051450176
# 2352051450176
# 2352051450176
# 2352051450176
# 2352051450176
# 2352051450176
# 2352051450176
# 2352051450176
=============================================================================================================================================================================================================================================================================================
#这玩意和数组中的第一个数字一样的hash表就能做
'''
方法一:
循环+hash表
'''
def fun1(s):
dic={
}#哈希表
for i in range(len(s)):
if s[i] in dic:#[i]已经在字典当中
#可以进行删除操作
#dic.pop(s[i]) 这里不能删除,如果该元素出现了奇数次那么会导致第三次出现的时候被误替了
dic[s[i]]=-1#索引不可能出现-1
else:
#不在字典中
dic[s[i]]=i#让他进去
#检查一下字典
for i in dic:
if dic[i]!=-1:
return dic[i]#一般元素都是看似尾部的添加
return -1
'''
方法二:
队列,队列本身具有先进先出的性质,在每次入对的时候进行一个判断,看元素的首位是否等同于
入对元素,如果是进行出队操作.这样的话,遍历一遍字符串得到的队列的首位就是首个不重复元素
'''
'''
写的有问题
'''
def fun2(s):
#开辟队列空间
duilie=[-1]*len(s)
duilie[0]=[s[0],0]
#记录出现多次但未删除元素
dic={
}
#当前首位
shouwei=0
for i in range(1,len(s)):
if s[i]==s[shouwei][0]:
pass
print(fun2("aabb"))
=============================================================================================================================================================================================================================================================================================
344. 反转字符串
#题目要求原地反转(不使用额外空间)
#题目本身的难度并不高所以思路很简单
'''
方法1:
取中值使得
i和n-i不断的调换位置
'''
def fun1(s):
n=len(s)-1
mid=n//2
i=0
while i<=mid:
s[i],s[n-i]=s[n-i],s[i]
i+=1
print(s)
fun1(["H","a","n","n","a","h"])
=============================================================================================================================================================================================================================================================================================
7. 整数反转
'''
首先直接转化为字符串,反转一下是非常简单的,也是非常愚蠢的,因为在大量使用别人的代码
'''
'''
根据数学:
不断的除以10取余数,是非常方便的
'''
def fun1(x):
t=10
sum1=0
a=abs(x)
while a>0:
sum1=sum1*t+a%10
a=a//10
if not -2**31<=sum1<=2**31-1:
return 0
if x<0:
return -sum1
return sum1
print(fun1(123))
=============================================================================================================================================================================================================================================================================================
'''
字母异位词:
若s和t中每个字符出现的次数相同,则称s和t互为字母异位词
从这个概念中不难看出:
--你必须遍历完s和t,且每一次遍历都需要进行保存
--在遍历结束后需要进行比较
--如果长度不同,那么不可能字母异位词*****
'''
'''
方法一:遍历+哈希表
'''
def fun1(s,t):
cur=0#当前下标
len_s=len(s)
len_t=len(t)
#首先判断长度是否相同
if len_s!=len_t:
return False#不可能了,我俩
s_dic={
}
t_dic={
}
while cur<len_s:
s_dic[s[cur]]=s_dic.get(s[cur],0)+1
t_dic[t[cur]]=t_dic.get(t[cur],0)+1
cur+=1
for t_key in t_dic:#循环拿出键
if t_key not in s_dic or t_dic[t_key]!=s_dic[t_key]:
#只要t的键不在s字典里面,或者该元素,在两个表中出现的次数不同则结束了
return False
return True
print(fun1("anagram","nagaram"))
'''
第二种方法:
排序,对字符串进行排序(通过unicode编码转化为数字进行比较)
对于字母异位词,排序后,s和t是两个一模一样得字符串
'''
'''
这个排序直接用的人家的,要我写的话只能是先转化为对应得数字
排序,再转回来,当然因为字符本身是可以比较的,所以不转也行
'''
def fun2(s,t):
if len(s)!=len(t):
return False
s=list(s)
t=list(t)#需要注意一下,字符串是不可以字节排序的,需要转化为列表
s.sort()
t.sort()
for i in range(len(s)):
if s[i]!=t[i]:
return False
return True
fun2("anagram","nagaram")