26、删除排序数组中的重复项
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
解:这道题读懂了示例就知道要做什么了,也就是说返回一个不重复元素的个数n,然后前n个数组的元素恰好是不重复元素的个数。这个问题用两个指针a和b就能解决,a记录不重复元素的最新位置,b记录索引位置。
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
a=0
b=1
while b<len(nums):
if nums[b]==nums[a]:
b=b+1
else:
a=a+1
nums[a]=nums[b]
b=b+1
return a+1
执行用时:44 ms, 击败了88.94%的用户
内存消耗:14.8 MB
27、移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
解:和26题的要求相同,题目类似,可以按相同方法处理。
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
a=b=0
while b<len(nums):
if nums[b]==val:
b=b+1
else:
nums[a]=nums[b]
a=a+1
b=b+1
return a
执行用时:36 ms, 中击败了88.09%的用户
内存消耗:13.7 MB,击败了23.30%的用户
28、实现 strStr()
实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
解:
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
n=len(needle)
if not needle:
return 0
if len(haystack)<len(needle):
return -1
a=0
b=n-1
while b <len(haystack):
if haystack[a:b+1]==needle:
return a
else:
a=a+1
b=b+1
return -1
执行用时:36 ms, 中击败了91.67%的用户
内存消耗:13.6 MB
29、两数相除
给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。
返回被除数 dividend 除以除数 divisor 得到的商。
整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2
解法一:总体来说就是一点点减除数,看要减几次。之所以要分那么多类是为了避免取绝对值时溢出(python不会有这个风险,但c会有,要时刻保持警惕)。
class Solution:
def divide(self, dividend: int, divisor: int) -> int:
if dividend==0:
return 0
if divisor==-1:
if -dividend>=-2**31 and -dividend<=2**31-1:
return -dividend
elif -dividend<-2**31:
return -2**31
else:
return 2**31-1
if divisor==1:
if dividend>=-2**31 and dividend<=2**31-1:
return dividend
elif dividend<-2**31:
return -2**31
else:
return 2**31-1
result=0
if (dividend<0 and divisor>0):
while dividend<0:
dividend=dividend+divisor
result=result+1
if dividend>0:
result=result-1
result=-result
elif(dividend>0 and divisor<0):
while dividend>0:
dividend=dividend+divisor
result=result+1
if dividend<0:
result=result-1
result=-result
elif (dividend>0 and divisor>0):
while dividend>0:
dividend=dividend-divisor
result=result+1
if dividend<0:
result=result-1
else:
while dividend<0:
dividend=dividend-divisor
result=result+1
if dividend>0:
result=result-1
if result>2**31-1:
return 2**31-1
elif result<-2**31:
return -2**31
else:
return result
该算法在算特别大的数时会超时,因此需要加速算法。
解法二:
每次可以把除数扩大二倍,确定被除数在哪个大致区间,然后不断缩小范围,这有点类似于求一个数的二进制。
编程序时需要特殊注意等于号是否需要加
class Solution:
def divide(self, dividend: int, divisor: int) -> int:
if dividend==0:
return 0
if divisor==-1:
if -dividend>=-2**31 and -dividend<=2**31-1:
return -dividend
elif -dividend<-2**31:
return -2**31
else:
return 2**31-1
if divisor==1:
if dividend>=-2**31 and dividend<=2**31-1:
return dividend
elif dividend<-2**31:
return -2**31
else:
return 2**31-1
result=0
if (dividend<0 and divisor>0) or (dividend>0 and divisor<0):
sgn=-1
else:
sgn=1
dividend=abs(dividend)
divisor=abs(divisor)
t=divisor
if t>dividend:
return 0
if t==dividend:
return 1
a=[0]
b=[0]
s=1
while dividend>=t:
a.append(t)
t=t+t
b.append(s)
s=s+s
k=len(a)
for i in range(k-1,0,-1):
if dividend>=a[i]:
dividend=dividend-a[i]
result=result+b[i]
result=sgn*result
if result>2**31-1:
return 2**31-1
elif result<-2**31:
return -2**31
else:
return result
执行用时:44 ms, 击败了73.16%的用户
内存消耗:13.7 MB
30、串联所有单词的子串
给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。
注:重复单词要各用一次
输入:
s = "wordgoodgoodgoodbestword",
words = ["word","good","best","word"]
输出:[]
解法一:遍历法
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if not words:
return []
if not s:
return []
n=len(words[0])
a=0
ans=[]
while a+len(words)*n-1<len(s):
clist=words.copy()
find=True
for i in range(len(words)):
if s[a+i*n:a+i*n+n] in clist:
clist.remove(s[a+i*n:a+i*n+n])
else:
find=False
if find==False:
break
if find==True:
ans.append(a)
a=a+1
return ans
执行用时:1948 ms, 击败了14.90%的用户
内存消耗:13.9 MB, 击败了51.35%的用户
解法二:双指针移动
跟遍历法相比,该方法的巧妙之处在于它是固定了起始点i(0,1,…len(words)),然后每次移动len(words)距离。这样可以借用之前一步的部分信息,从而提高程序效率。而+1的遍历每一步都需要重复判断,但本方法不需要。
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if not words:
return []
if not s:
return []
adict={}
ans=[]
for i in words:#计数
if i in adict:
adict[i]=adict[i]+1
else:
adict[i]=1
n=len(words[0])
for i in range(n):
left=right=i
currentdict={}
count=0
for j in range(len(words)):
currentString=s[right:right+n]
if currentString in adict and currentString in currentdict:
currentdict[currentString]+=1
count=count+1
elif currentString in adict:
currentdict[currentString]=1
count=count+1
right=right+n
while True:
if count==len(words) and currentdict==adict:
ans.append(left)
if right+n>len(s):
break
s1=s[left:left+n]
if s1 in currentdict:
currentdict[s1]-=1
count=count-1
left=left+n
s2=s[right:right+n]
if s2 in adict and s2 in currentdict:
currentdict[s2]+=1
count=count+1
elif s2 in adict:
currentdict[s2]=1
count=count+1
right=right+n
return ans
执行用时:84 ms, 击败了87.73%的用户
内存消耗:13.8 MB, 击败了64.86%的用户