题号
169.多数元素
题目描述:给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:nums = [3,2,3]
输出:3
示例 2:
输入:nums = [2,2,1,1,1,2,2]
输出:2
提示:
- n == nums.length
- 1 <= n <= 5 ∗ 1 0 4 5 * 10^4 5∗104
- − 1 0 9 -10^9 −109 <= nums[i] <= 1 0 9 10^9 109
尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题
题解:
“同归于尽消杀法” :
由于多数超过50%, 比如100个数,那么多数至少51个,剩下少数是49个。
第一个到来的士兵,直接插上自己阵营的旗帜占领这块高地,此时领主 winner 就是这个阵营的人,现存兵力 count = 1。
如果新来的士兵和前一个士兵是同一阵营,则集合起来占领高地,领主不变,winner 依然是当前这个士兵所属阵营,现存兵力 count++;
如果新来到的士兵不是同一阵营,则前方阵营派一个士兵和它同归于尽。 此时前方阵营兵力count --。(即使双方都死光,这块高地的旗帜 winner 依然不变,因为已经没有活着的士兵可以去换上自己的新旗帜)
当下一个士兵到来,发现前方阵营已经没有兵力,新士兵就成了领主,winner 变成这个士兵所属阵营的旗帜,现存兵力 count ++。
就这样各路军阀一直以这种以一敌一同归于尽的方式厮杀下去,直到少数阵营都死光,那么最后剩下的几个必然属于多数阵营,winner 就是多数阵营。(多数阵营 51个,少数阵营只有49个,死剩下的2个就是多数阵营的人)
代码:
class Solution:
def majorityElement(self, nums: List[int]) -> int:
winner=nums[0]
count=1
for i in range(1,len(nums)):
if count==0:
winner=nums[i]
count+=1
elif winner==nums[i]:
count+=1
elif winner!=nums[i]:
count-=1
return winner
189.轮转数组
题目描述:给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]
提示:
- 1 <= nums.length <= 1 0 5 10^5 105
- − 2 31 -2^{31} −231 <= nums[i] <= 2 31 2^{31} 231 - 1
- 0 <= k <= 1 0 5 10^5 105
进阶:
尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?
解法1:
三次翻转,整体翻转->nums[0:k-1]翻转->nums[k:len(nums)-1]翻转
class Solution(object):
def rotate(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: None Do not return anything, modify nums in-place instead.
"""
k=k%len(nums)
def reverse(nums,start,end):
while start<end:
nums[start],nums[end]=nums[end],nums[start]
start+=1
end-=1
reverse(nums,0,len(nums)-1)
reverse(nums,0,k-1)
reverse(nums,k,len(nums)-1)
注:对nums取余是存在轮转长度大于数组长度的情况,当等于数组长度的时候会恢复原位,所以取余。
解法2:
使用额外的数组来将每个元素放至正确的位置。遍历原数组,将原数组下标为 i 的元素放至新数组下标为 (i+k)%n 的位置,最后将新数组拷贝至原数组。
import copy
class Solution(object):
def rotate(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: None Do not return anything, modify nums in-place instead.
"""
temp_list=[0 for i in range(len(nums))]
# print(temp_list)
for i in range(len(nums)):
temp_list[(i+k)%len(nums)]=nums[i]
# print(temp_list)
# nums=[element for element in temp_list]
# nums=copy.deepcopy(temp_list)
# nums=temp_list
for i in range(len(nums)):
nums[i]=temp_list[i]
注释掉的几种copy方法无法改变原有nums,最后尝试的逐元素复制成功。
238. 除自身以外数组的乘积
题目描述:给你一个整数数组 nums,返回数组answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。
题目数据保证数组 nums之中任意元素的全部前缀元素和后缀的乘积都在32位整数范围内。
请不要使用除法,且在 O(n) 时间复杂度内完成此题。
示例 1:
输入: nums = [1,2,3,4]
输出: [24,12,8,6]
示例 2:
输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]
提示:
- 2 <= nums.length <= 1 0 5 10^5 105
- -30 <= nums[i] <= 30
保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内
题解:
声明两个数组left和right,一个存每个元素从左乘到右的结果,另一个存每个元素从右乘到左的结果,因为要求时间复杂度为O(n),所以在给left和right赋值的时候不能通过双层遍历,可以考虑一次性遍历,对于left数组来说,除了left的第一个元素,其余元素都等于nums[i]*left[i-1],而对于right数组来说,是从后往前存储结果,除了最后一个元素,每个元素都等于nums[i]*right[i+1],最后声明一个结果数组result,每个元素result[i]=left[i-1]*right[i+1]。
class Solution(object):
def productExceptSelf(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
left=[1 for i in range(len(nums))]
right=[1 for i in range(len(nums))]
left[0]=nums[0]
right[len(nums)-1]=nums[len(nums)-1]
for i in range(1,len(nums)):
left[i]=nums[i]*left[i-1]
right[len(nums)-1-i]=nums[len(nums)-1-i]*right[len(nums)-i]
result=[1 for i in range(len(nums))]
result[0]=right[1]
result[len(nums)-1]=left[len(nums)-2]
for i in range(1,len(nums)-1):
result[i]=left[i-1]*right[i+1]
return result
274. H 指数
题目描述:
给你一个整数数组 citations ,其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。计算并返回该研究者的 h 指数。
根据维基百科上 h 指数的定义:h 代表“高引用次数” ,一名科研人员的 h 指数 是指他(她)至少发表了 h 篇论文,并且 至少 有 h 篇论文被引用次数大于等于 h 。如果 h 有多种可能的值,h 指数 是其中最大的那个。
示例 1:
输入:citations = [3,0,6,1,5]
输出:3
解释:给定数组表示研究者总共有 5 篇论文,每篇论文相应的被引用了 3, 0, 6, 1, 5 次。
由于研究者有 3 篇论文每篇 至少 被引用了 3 次,其余两篇论文每篇被引用 不多于 3 次,所以她的 h 指数是 3。
示例 2:
输入:citations = [1,3,1]
输出:1
提示:
- n == citations.length
- 1 <= n <= 5000
- 0 <= citations[i] <= 1000
题解:
题干读着有点儿绕,既然说h指数的第一个条件是至少发表h篇论文,说明h的上限是论文的数量,最后一句说h有多种可能时,是其中最大的,所以可以以论文数量len(citations)为起始,降序遍历,当找到第一个满足条件的h时,跳出循环,返回结果。h指数的第2个条件是至少有h篇论文被引用次数大于等于h,可以定义一个函数,传入citations和候选的h,判断候选h是否符合条件,遍历一遍citations,记录引用大于候选h的数量flag,如果flag>h成立,则候选的h符合条件。
class Solution(object):
def hIndex(self, citations):
"""
:type citations: List[int]
:rtype: int
"""
#定义函数,传入citations和h,判断h是否成立
def panduan_h(citations,h):
flag=0
for element in citations:
if element>=h:
flag+=1
if flag>=h:
return True
else:
return False
for h in range(len(citations),-1,-1):
if panduan_h(citations,h):
return h
134.加油站
题目描述:在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。
示例 1:
输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2]
输出: 3
解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。
示例 2:
输入: gas = [2,3,4], cost = [3,4,3]
输出: -1
解释:
你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。
提示:
- gas.length == n
- cost.length == n
- 1 <= n <= 1 0 5 10^5 105
- 0 <= gas[i], cost[i] <= 1 0 4 10^4 104
题解:
这道题看完有点儿绕,内部逻辑有两个,首先要满足总的消耗数要小于加油数,否则必然回不到起点,第二个条件是当满足总的消耗数小于加油数时,找油表值最小的点,将这个点当做终点才可以满足保证差值的累加和始终大于等于0,也就是要找到油表值最小的点,然后从下一个位置出发。
class Solution(object):
def canCompleteCircuit(self, gas, cost):
"""
:type gas: List[int]
:type cost: List[int]
:rtype: int
"""
#加油总数
add_gas=0
#消耗汽油总数
cost_gas=0
#油箱状态
gas_tank=0
min_status=gas[0]-cost[0]
min_index=0
for i in range(len(gas)):
add_gas+=gas[i]
cost_gas+=cost[i]
gas_tank=gas_tank+gas[i]-cost[i]
if gas_tank<=min_status:
min_status=gas_tank
min_index=i
if cost_gas>add_gas:
return -1
else:
return (min_index+1)%len(gas)
gas_tank记录的是从0号加油站出发时,每次的油表值,min_status记录油表最低值,min_index是油表值最低时的索引,最后判断总消耗是否大于加油数,取余是当油表最低值在末位时,要从第一个位置出发,用gas_tank<=min_status而没用小于号是因为测试案例中有的位置加油和消耗都是0,这时油表的最低值会一直维持,题干要求解唯一,所以当多个位置满足油表最低值时,需要找到最后一个最低值。
125.回文串
题目描述:如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
字母和数字都属于字母数字字符。
给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 。
示例 1:
输入: s = "A man, a plan, a canal: Panama"
输出:true
解释:"amanaplanacanalpanama" 是回文串。
示例 2:
输入:s = "race a car"
输出:false
解释:"raceacar" 不是回文串。
示例 3:
输入:s = " "
输出:true
解释:在移除非字母数字字符之后,s 是一个空字符串 "" 。
由于空字符串正着反着读都一样,所以是回文串。
提示:
- 1 <= s.length <= 2 ∗ 1 0 5 2 * 10^5 2∗105
- s 仅由可打印的 ASCII 字符组成
题解:这道题思路很是很清晰的,主要考察的也是API的使用,可以首先将字符串中的非字母数字字符去除,单独保存,然后验证单独保存的结果是否符合回文串,用双指针一前一后的遍历,有不同就返回False,如果能顺利走完循环说明是回文串,返回True。
class Solution(object):
def isPalindrome(self, s):
"""
:type s: str
:rtype: bool
"""
if len(s)==1:
return True
else:
temp_str="".join(element.lower() for element in s if element.isalnum())
print(temp_str)
if len(temp_str)==0:
return True
else:
i=0
j=len(temp_str)-1
while(i<j):
if(temp_str[i]!=temp_str[j]):
return False
i+=1
j-=1
return True
392.判断子序列
题目描述:
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
示例 1:
输入:s = "abc", t = "ahbgdc"
输出:true
示例 2:
输入:s = "axc", t = "ahbgdc"
输出:false
提示:
- 0 <= s.length <= 100
- 0 <= t.length <= 1 0 4 10^4 104
- 两个字符串都只由小写字符组成。
题解:用双指针解法的话可以考虑将指针分别放在两个字符串的起始位置,如果所指位置相等则两个指针同时后移,如果不等则只有t的指针后移,最后如果s的指针能顺利移到最后则说明是子串。
class Solution(object):
def isSubsequence(self, s, t):
"""
:type s: str
:type t: str
:rtype: bool
"""
if len(s)==0:
return True
s_index=0
for t_index in range(len(t)):
if t[t_index]==s[s_index]:
s_index+=1
if s_index==len(s):
return True
return False
我加的s_index==len(s)是因为s的指针走到最后如果能到len(s)就已经说明成功了,再接着执行循环会发生越界了,到这儿就可以停了。
167. 两数之和 II - 输入有序数组
题目描述:给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 n u m b e r s [ i n d e x 1 ] numbers[index_1] numbers[index1] 和 n u m b e r s [ i n d e x 2 ] numbers[index_2] numbers[index2] ,则 1 <= i n d e x 1 index_1 index1 < i n d e x 2 index_2 index2 <= n u m b e r s . l e n g t h numbers.length numbers.length 。
以长度为 2 的整数数组 [ i n d e x 1 , i n d e x 2 ] [index_1, index_2] [index1,index2] 的形式返回这两个整数的下标 i n d e x 1 index_1 index1 和 i n d e x 2 index_2 index2。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
你所设计的解决方案必须只使用常量级的额外空间。
示例 1:
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
示例 2:
输入:numbers = [2,3,4], target = 6
输出:[1,3]
解释:2 与 4 之和等于目标数 6 。因此 index1 = 1, index2 = 3 。返回 [1, 3] 。
示例 3:
输入:numbers = [-1,0], target = -1
输出:[1,2]
解释:-1 与 0 之和等于目标数 -1 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
提示:
- 2 <= numbers.length <= 3 ∗ 1 0 4 3 * 10^4 3∗104
- 1000 <= numbers[i] <= 1000
- numbers 按 非递减顺序 排列
- 1000 <= target <= 1000
- 仅存在一个有效答案
题解:最简单的就是双重遍历,外层i从起始到末尾,内层从i+1到末尾,相加的和大于target时跳出内层循环,这么做就是在某些特别长的测试用例上超时。第二种做法就是双指针,两个指针一个left在起始位置,一个right在末尾,因为数组是顺序的,所以left+right>target时,right左移,这样和就会变小,left+right<target时left右移,直至left与right相遇,在中间过程中当left+right=target时返回结果,注意索引从1开始,返回时要+1。
class Solution(object):
def twoSum(self, numbers, target):
"""
:type numbers: List[int]
:type target: int
:rtype: List[int]
"""
index1=0
index2=len(numbers)-1
while index1<index2:
if numbers[index1]+numbers[index2]==target:
return [index1+1,index2+1]
elif numbers[index1]+numbers[index2]<target:
index1+=1
else:
index2-=1
15. 三数之和
题目描述:
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
示例 2:
输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。
示例 3:
输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。
提示:
- 3 < = n u m s . l e n g t h < = 3000 3 <= nums.length <= 3000 3<=nums.length<=3000
- − 1 0 5 < = n u m s [ i ] < = 1 0 5 -10^5 <= nums[i] <= 10^5 −105<=nums[i]<=105
题解:
这道题需要考虑的细节很多,首先可以将三数之和转换为上一道题的两数之和,只需将数组进行排序,并且看成两数相加=0-第三个数。
遍历排序后的数组,当前遍历元素为k,剩下的就是从当前元素后面的数组找出两者和=0-k的,为了对结果去重,第一个条件是如果当前元素大于0,直接跳出循环,因为后面的必然大于0了,三者之和不可能大于0。第二个条件是如果当前遍历元素与上一次遍历元素相同,跳出本轮循环。第三个条件是在剩余数组中找两者之和等于-k时,每次在指针移动后都要判断是否与上一次的数相等,如果相等要接着移动。
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
def twoSum(nums,target):
index_left=0
index_right=len(nums)-1
temp_list=[]
while(index_left<index_right):
if nums[index_left]+nums[index_right]==target:
temp_list.append([0-target,nums[index_left],nums[index_right]])
index_left+=1
index_right-=1
while index_left<index_right and nums[index_left]==nums[index_left-1]:index_left+=1
while index_left<index_right and nums[index_right]==nums[index_right+1]:index_right-=1
elif nums[index_left]+nums[index_right]<target:
index_left+=1
while index_left<index_right and nums[index_left]==nums[index_left-1]:index_left+=1
else:
index_right-=1
while index_left<index_right and nums[index_right]==nums[index_right+1]:index_right-=1
return temp_list
nums=sorted(nums)
result_list=[]
for i in range(len(nums)):
if(i!=0 and nums[i]==nums[i-1]):
continue
elif(nums[i]>0):
break
else:
temp_nums=nums[i+1:]
target=0-nums[i]
temp_list=twoSum(nums[i+1:],target)
result_list+=temp_list
return result_list
151. 反转字符串中的单词
题目描述:
给你一个字符串 s ,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
示例 1:
输入:s = "the sky is blue"
输出:"blue is sky the"
示例 2:
输入:s = " hello world "
输出:"world hello"
解释:反转后的字符串中不能存在前导空格和尾随空格。
示例 3:
输入:s = "a good example"
输出:"example good a"
解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。
提示:
- 1 < = s . l e n g t h < = 1 0 4 1 <= s.length <= 10^4 1<=s.length<=104
- s 包含英文大小写字母、数字和空格 ’ ’
- s 中 至少存在一个 单词
题解:
通过Python实现比较简单,首先去除两侧空格,然后对字符串利用空格分隔,对得到的列表进行翻转并去除空元素,最后对列表进行拼接。
class Solution(object):
def reverseWords(self, s):
"""
:type s: str
:rtype: str
"""
s=s.strip()
str_list=s.split(" ")
str_list.reverse()
new_list=[x for x in str_list if x!=""]
s=" ".join(new_list)
return s
58. 最后一个单词的长度
题目描述:
给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。
单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。
示例 1:
输入:s = "Hello World"
输出:5
解释:最后一个单词是“World”,长度为 5。
示例 2:
输入:s = " fly me to the moon "
输出:4
解释:最后一个单词是“moon”,长度为 4。
示例 3:
输入:s = "luffy is still joyboy"
输出:6
解释:最后一个单词是长度为 6 的“joyboy”。
提示:
- 1 < = s . l e n g t h < = 1 0 4 1 <= s.length <= 10^4 1<=s.length<=104
- s 仅有英文字母和空格 ’ ’ 组成
- s 中至少存在一个单词
题解:
去除左右两侧空格,通过空格分割后返回最后一个元素的长度。
class Solution(object):
def lengthOfLastWord(self, s):
"""
:type s: str
:rtype: int
"""
s=s.strip()
str_list=s.split()
return len(str_list[-1])
11.盛最多水的容器
题目描述:
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
提示:
- n == height.length
- 2 < = n < = 1 0 5 2 <= n <= 10^5 2<=n<=105
- 0 < = h e i g h t [ i ] < = 1 0 4 0 <= height[i] <= 10^4 0<=height[i]<=104
题解:
采用双指针,面积area=(right-left)*min(height[left],height[right]),现在就要考虑指针的移动问题了,要考虑两个条件:1.相同条件下两边距离越远越好 2.区域受限于较短边。根据这两个条件,无论哪个指针移动,两个指针的距离都在缩小,此时只能让边更高才会让面积更大,所以每次比较两个指针,固定长的指针,移动短的指针。
class Solution(object):
def maxArea(self, height):
"""
:type height: List[int]
:rtype: int
"""
#面积=两者的横坐标差值*两者的纵坐标最小值
max_area=0
left=0
right=len(height)-1
while left<right:
area=(right-left)*min(height[left],height[right])
max_area=max_area if max_area>area else area
if height[left]>height[right]:right-=1
else:left+=1
return max_area
每日更新ing…
如果各位大佬有更优解并且通俗易懂,欢迎大家交流(PS:因为官网有的题解看不懂所以自己整理整理)