1.长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。
示例1:
输入: s = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。
第一思路:时间度为 O(N2)的解法
def minSubArrayLen(self, s,nums):
if not nums:
return 0
if s>sum(nums):
return 0
min_num = len(nums)
for left in range(0,len(nums)):
for i in range(1,len(nums)+1):
if sum(nums[left:i])>=s:
min_num = min(min_num,len(nums[left:i]))
return min_num
优化版本:利用双指针的方法,如果当前列表大于目标值则计算出长度并且更新左边的指针,如果小于目标值则更新右边的指针。
def minSubArrayLen(self, s: int, nums: List[int]) -> int:
if not sum or s>sum(nums):
return 0
left ,right,end = 0,1,len(nums)+1
min_num = len(nums)
while right<end:
if sum(nums[left:right])>=s:
min_num = min(min_num,len(nums[left:right]))
left+=1
else:
right+=1
return min_num
C++ 思路:如果还要进行优化的话,就优化 sum 函数,我们不使用这个库函数,直接对和进行运算。
def minSubArrayLen(self, s: int, nums: List[int]) -> int:
if not sum or s>sum(nums):
return 0
n = len(nums)
start = 0
min_len = n+1
cur_sum = 0
for i in range(n):
cur_sum += nums[i]
while cur_sum >= s:
min_len = min(min_len, i-start+1)
cur_sum -= nums[start]
start += 1
return min_len
2.打家劫舍 II
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例1
输入: [2,3,2]
输出: 3
解释: 你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例2
输入: [1,2,3,1]
输出: 4
解释: 你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)
偷窃到的最高金额 = 1 + 3 = 4
思路:如果总户数小于三家,则选择其中最大的一家,因为第一家和最后一家是连接起来的。
需要分为两次判断,第一次把最后一家剔除,第二次把第一家剔除,然后在这两个取出最大值。
def rob(self, nums: List[int]) -> int:
if not nums:
return 0
if len(nums)<=3:
return max(nums)
def rbotwo(nums):
res = [0]*len(nums)
res[0],res[1] = nums[0],max(nums[:2])
for i in range(2,len(nums)):
res[i] = max(res[i-2]+nums[i],res[i-1])
return res[-1]
return max(rbotwo(nums[1:]),rbotwo(nums[:-1]))
C++版本:不像python 直接把数组中第一个或最后一个删除,C++思路是经过错位的方法,而且转义方程也变化了,不太好理解。
int rob(vector<int>& nums) {
if(nums.size()==0){
return 0;
}
if(nums.size()==1){
return nums[0];
}
vector<int>dp1(nums.size(),0);
vector<int>dp2(nums.size(),0);
dp1[0] = 0;
dp1[1] = nums[0];
for(int i=2;i<nums.size();++i){
dp1[i] = max(dp1[i-1],nums[i-1]+dp1[i-2]);
}
dp2[0] = 0;
dp2[1] = nums[1];
for(int i = 2;i<nums.size();++i){
dp2[i] = max(dp2[i-1],nums[i]+dp2[i-2]);
}
return max(dp1[nums.size()-1],dp2[nums.size()-1]);
}
3. 最短回文串
难度:困难 类型: 字符串
给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。
示例1
输入: “aacecaaa”
输出: “aaacecaaa”
示例2
输入: “abcd”
输出: “dcbabcd”
思路:startswith 判断参数字符是否是指定字符串的开头,开头
的概念默认是参数字符的长度,另外参数字符也可以指定范围。
def shortestPalindrome(s):
r = s[::-1]
for i in range(len(s) + 1):
# startswith() 方法用于
# 检查字符串是否是以指定子字符串开头
# 如果是则返回 True,否则返回 False
if s.startswith(r[i:]):
return r[:i] + s
4.存在重复元素
给定一个整数数组,判断是否存在重复元素。
如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。
示例 1:
输入: [1,2,3,1]
输出: true
示例 2:
输入: [1,2,3,4]
输出: false
python 中 set 函数 可以去重return len(set(nums)) != len(nums)
在C++ 中也可以利用 set 特性:容器不可以有两个相同的键值
bool containsDuplicate(vector<int>& nums) {
set<int>value;
for(int i =0;i<nums.size();++i){
// 如果存在相同的键值,则不会 insert
value.insert(nums[i]);
}
return nums.size()!=value.size();
}
5.最大正方形
在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。
示例
输入:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
输出: 4
动态规划:初始化一个二维列表,赋初值为0
动态转移方程是:res[i][j] = min(res[i-1][j-1],res[i-1][j],res[i][j-1])+1
最大的 res[i][j] 是边数,最后返回平方值
def maximalSquare(self, matrix: List[List[str]]) -> int:
if not len(matrix) or not len(matrix[0]):
return 0
b,row,col = 0,len(matrix)+1,len(matrix[0])+1
res = [[0]*col for _ in range(row)]
for i in range(1,row):
for j in range(1,col):
if matrix[i-1][j-1]=='1':
res[i][j] = min(res[i-1][j-1],res[i-1][j],res[i][j-1])+1
b = max(b,res[i][j])
return b**2
案列:
6.组合总和III
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字
示例1
输入: k = 3, n = 7
输出: [[1,2,4]]
示例2
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
思路:深度优先搜索遍历
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
res = []
def dfs(k,n,index,path):
if n<0:
return
if k==0 and n==0:
res.append(path)
for i in range(index,10):
dfs(k-1,n-i,i+1,path+[i])
dfs(k,n,1,[])
return res
C++版本
vector<vector<int>>res;
vector<int>temp;
vector<vector<int>> combinationSum3(int k, int n) {
if(k>n){
return res;
}
combination(k,1,n);
return res;
}
void combination(int k,int start,int n){
if(n<0 || k<0){
return;
}
if (n==0 && k==0){
res.push_back(temp);
}
for(int i = start;i<10;++i){
temp.push_back(i);
combination(k-1,i+1,n-i);
temp.pop_back();
}
}
7.翻转二叉树
翻转一棵二叉树
def invertTree(self, root: TreeNode) -> TreeNode:
if root:
root.left,root.right = self.invertTree(root.right),self.invertTree(root.left)
return root
C++ 版本
TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
TreeNode temp = root.left;
root.left = invertTree(root.right);
root.right = invertTree(temp);
return root;
}
8.求众数 II
给定一个大小为 n 的数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1)
示例1
输入: [3,2,3]
输出: [3]
示例2
输入: [1,1,1,3,3,2,2,2]
输出: [1,2]
可以利用库函数 count 来计算一个数在列表中出现的次数。
def majorityElement(self, nums: List[int]) -> List[int]:
n = len(nums)
res = []
for i in range(n):
if nums.count(nums[i])>n//3:
res.append(nums[i])
return set(res)
优化版本
def majorityElement(nums):
n = len(nums)
dic = {}
for x in nums:
if x in dic:
dic[x]+=1
else:
dic[x]=1
return [x for x in dic.keys() if dic[x]>n/3]
但是我们会发现,并不符合题意,题目要求时间复杂度O(N),空间为O(1)
下面这种思路利用的是一种计数方法,因为众数的概念是出现的次数要大于n/3次,所以无论任何的数组,里面的众数最多有两个。
那我们定义两个列表,由于要求时间复杂度是O(1),所以列表中一种有两个元素,初始化为0
第一个元素代表 nums 中的值,第二个代表的是出现的次数
这种方法也叫 投票法
超过n/3的数最多只能有两个,选两个候选人a,b,每个候选人的表示方法为[数值,票数]
遍历数组
当前元素为a时,a的票数+1
当前元素为b时,b的票数+1
当前元素既不等于a也不等于b时:
- 如果当前有候选人票数为0,新元素上位,票数更新为1
- 如果当前所有候选人票数都大于0,a,b两人票数均-1
def majorityElement(nums):
candidate1 = [0, 0]
candidate2 = [0, 0]
for n in nums:
# nums =[1,1,1,3,3,2,2,2]
if n == candidate1[0]:
candidate1[1] += 1
elif n == candidate2[0]:
candidate2[1] += 1
elif candidate1[1] == 0:
candidate1 = [n, 1]
elif candidate2[1] == 0:
candidate2 = [n, 1]
else:
candidate1[1] -= 1
candidate2[1] -= 1
return [x for x in set([candidate1[0], candidate2[0]]) if nums.count(x) > len(nums)/3]
if __name__ == "__main__":
nums = [1, 1, 1, 3, 3, 2, 2, 2]
a = majorityElement(nums)
print(a)
9.汇总区间
给定一个无重复元素的有序整数数组,返回数组区间范围的汇总
示例 1:
输入: [0,1,2,4,5,7]
输出: [“0->2”,“4->5”,“7”]
解释: 0,1,2 可组成一个连续的区间; 4,5 可组成一个连续的区间。
示例 2:
输入: [0,2,3,4,6,8,9]
输出: [“0”,“2->4”,“6”,“8->9”]
解释: 2,3,4 可组成一个连续的区间; 8,9 可组成一个连续的区间。
思路:利用两个指针的方法将符合条件的数列转换为字符串添加到返回列表中。
def summaryRanges(self, nums: List[int]) -> List[str]:
if not nums:
return []
res = []
n = len(nums)
if n==1:
return [str(nums[0])]
start = 0
end = 1
while end < n:
if nums[end]-nums[end-1]==1:
while end<n and nums[end]-nums[end-1]==1:
end+=1
res.append(str(nums[start])+'->'+str(nums[end-1]))
else:
res.append(str(nums[start]))
start = end
if start==n-1:
res.append(str(nums[start]))
end+=1
return res
10、2的幂
给定一个整数,编写一个函数来判断它是否是 2 的幂次方
示例
输入: 1
输出: true
解释: 20 = 1
示例2
输入: 16
输出: true
解释: 24 = 16
示例3
输入: 218
输出: false
思路:二进制位移的思想。因为如果一个数是二的幂次方,那么除了最高的那位是1,其余位都是0.
def isPowerOfTwo(self, n: int) -> bool:
if n==0:
return False
while n!=1:
if n&1 != 0:
return False
n = n>>1
return True
当然也有更简单的方法,也是利用二进制的思想,如果 n 是2的次方,n-1 和 n 的二进制位恰相反。
if n<=0:
return False
return not n&(n-1)
来源:CSDN
作者:M-aaron
链接:https://blog.csdn.net/qq_43763344/article/details/103543776