力扣高频|算法面试题汇总(一):开始之前
力扣高频|算法面试题汇总(二):字符串
力扣高频|算法面试题汇总(三):数组
力扣高频|算法面试题汇总(四):堆、栈与队列
力扣高频|算法面试题汇总(五):链表
力扣高频|算法面试题汇总(六):哈希与映射
力扣高频|算法面试题汇总(七):树
力扣高频|算法面试题汇总(八):排序与检索
力扣高频|算法面试题汇总(九):动态规划
力扣高频|算法面试题汇总(十):图论
力扣高频|算法面试题汇总(十一):数学&位运算
力扣链接
目录:
给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字)。
示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组
思路一:暴力,这就不做多介绍了。
思路二:
循环求解,这有点想剑指offer中的《连续子数组的最大和》
同时也参考了思路
需要从前向后遍历数组,
[2,3,-2,4]
为例:class Solution {
public:
int maxProduct(vector<int>& nums) {
int Max;
int imax = 1;
int imin = 1;
for(int i = 0; i < nums.size(); ++i){
if(nums[i] < 0){
// 如果是负数,则交换局部最大值和最小值
int temp = imax;
imax = imin;
imin = temp;
}
// 局部最大值
imax = (imax * nums[i] > nums[i]) ? imax * nums[i] : nums[i];
// 局部最小值
imin = (imin * nums[i] < nums[i]) ? imin * nums[i] : nums[i];
// 局部最大值和最大值进行比较
if(i == 0)
Max = imax;
else
Max = Max > imax ? Max :imax;
}
return Max;
}
};
Python
class Solution:
def maxProduct(self, nums: List[int]) -> int:
imax = 1
imin = 1
for i in range(len(nums)):
if nums[i] < 0:
imax, imin = imin, imax
imax = max(imax*nums[i], nums[i])
imin = min(imin*nums[i], nums[i])
if i == 0:
Max = imax
else:
Max = max(Max, imax)
return Max
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入: [3,2,3]
输出: 3
示例 2:
输入: [2,2,1,1,1,2,2]
输出: 2
思路:排序,出现在数组中间的那个数字一定是出现次数大于n/2
的元素。
其余解法可参考《数组中出现超过一半的数字》
C++
class Solution {
public:
int majorityElement(vector<int>& nums) {
// 排序
sort(nums.begin(), nums.end());
return nums[nums.size() / 2];
}
};
Python
class Solution:
def majorityElement(self, nums: List[int]) -> int:
nums = sorted(nums)
return nums[len(nums)//2]
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: [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:
输入: [-1,-100,3,99] 和 k = 2
输出: [3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
说明:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
要求使用空间复杂度为 O(1) 的 原地 算法。
思路1:暴力循环,成功超时。
思路2:
直接计算元素移动的最终位置。比如移动k
次,那么索引为i
的元素会被移动到(i + k)%n
的位置上去,其中n
是数组的长度。但是这样会存在一个问题:无法全部移动,以输入nums: [1, 2, 3, 4, 5, 6];k: 2
为例。挨着交换i%k==0
上的元素,是无法交换全部的元素的,所以当完成索引满足i%k==0
的元素交换之后,需要对i%k==1
上的元素进行交换,一共交换n
次。如力扣官方图示:
C++
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int temp;
int counts = 0;
k = k % nums.size();
for(int start = 0; counts < nums.size(); ++start){
int currentID = start;
int pre = nums[start];
do{
int nextID = (currentID + k)% nums.size();
temp = nums[nextID];
nums[nextID] = pre;
pre = temp;
++counts;
currentID = nextID;
}while(currentID != start);
}
}
};
Python:
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
k = k % len(nums)
start = 0
counts = 0
while counts < len(nums):
currentID = start
pre = nums[currentID]
while True:
nextID = (currentID + k) % len(nums)
nums[nextID], pre = pre, nums[nextID]
counts += 1
currentID = nextID
if currentID == start:
start += 1
break
思路3:
翻转,这个和剑指offer的《翻转单词顺序》类似。
一下参考力扣官方:
假设 n=7n=7 且 k=3k=3 。
原始数组 : 1 2 3 4 5 6 7
反转所有数字后 : 7 6 5 4 3 2 1
反转前 k 个数字后 : 5 6 7 4 3 2 1
反转后 n-k 个数字后 : 5 6 7 1 2 3 4 --> 结果
C++
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int temp;
k = k % nums.size();
int pStart = 0;
int pEnd = nums.size() - 1;
while(pStart < pEnd){
temp = nums[pStart];
nums[pStart] = nums[pEnd];
nums[pEnd] = temp;
++pStart;
--pEnd;
}
pStart = 0;
pEnd = k - 1;
while(pStart < pEnd){
temp = nums[pStart];
nums[pStart] = nums[pEnd];
nums[pEnd] = temp;
++pStart;
--pEnd;
}
pStart = k;
pEnd = nums.size() - 1;
while(pStart < pEnd){
temp = nums[pStart];
nums[pStart] = nums[pEnd];
nums[pEnd] = temp;
++pStart;
--pEnd;
}
}
};
Python
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
k = k % len(nums)
start = 0
counts = 0
nums.reverse()
pStart = 0
pEnd = k-1
while pStart < pEnd:
nums[pStart], nums[pEnd] = nums[pEnd], nums[pStart]
pStart += 1
pEnd -= 1
pStart = k
pEnd = len(nums)-1
while pStart < pEnd:
nums[pStart], nums[pEnd] = nums[pEnd], nums[pStart]
pStart += 1
pEnd -= 1
给定一个整数数组,判断是否存在重复元素。
如果任意一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。
示例 1:
输入: [1,2,3,1]
输出: true
示例 2:
输入: [1,2,3,4]
输出: false
示例 3:
输入: [1,1,1,3,3,4,3,2,4,2]
输出: true
思路1:
使用哈希表记录每个元素出现的次数,如果第二次遇到该数字,直接return true
C++
class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
map<int, int> idHash;
for(auto itear = nums.begin(); itear < nums.end(); ++itear){
// 第一次遇到数字
if(idHash.find(*itear) == idHash.end()){
idHash[*itear] = 1;
}else{
// 第二次遇到
return true;
}
}
return false;
}
};
Python
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
idHash = {}
for num in nums:
if not num in idHash:
idHash[num] = 1
else:
return True
return False
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。
思路:
参考这位博主的思路。
把0元素往后移动,实质上等于把非0元素往前移动。
用一个标志记录非0元素,先把非0元素全部移动到前面,剩下的部分再进行填充。
C++
class Solution {
public:
void moveZeroes(vector<int>& nums) {
if(nums.size() == 0)
return;
// 记录非0的位置
int k = 0;
for(int i = 0; i < nums.size(); ++i){
if(nums[i] != 0){
// 移动非0元素
nums[k] = nums[i];
++k;
}
}
// 从k开始填补0、
while(k < nums.size()){
nums[k] = 0;
++k;
}
}
};
Python
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
if len(nums) == 0:
return
k = 0
for i in range(len(nums)):
if nums[i] != 0:
nums[k] = nums[i]
k += 1
while k < len(nums):
nums[k] = 0
k = k + 1
打乱一个没有重复元素的数组。
示例:
// 以数字集合 1, 2 和 3 初始化数组。
int[] nums = {1,2,3};
Solution solution = new Solution(nums);
// 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。
solution.shuffle();
// 重设数组到它的初始状态[1,2,3]。
solution.reset();
// 随机返回数组[1,2,3]打乱后的结果。
solution.shuffle();
C++
class Solution {
private:
vector<int> stroeNums;
vector<int> shuffleNums;
public:
Solution(vector<int>& nums) {
stroeNums = nums;
}
/** Resets the array to its original configuration and return it. */
vector<int> reset() {
return stroeNums;
}
/** Returns a random shuffling of the array. */
vector<int> shuffle() {
shuffleNums = stroeNums;
random_shuffle(shuffleNums.begin(),shuffleNums.end());
return shuffleNums;
}
};
/**
* Your Solution object will be instantiated and called as such:
* Solution* obj = new Solution(nums);
* vector param_1 = obj->reset();
* vector param_2 = obj->shuffle();
*/
Python:
class Solution:
def __init__(self, nums: List[int]):
self.storeNum = nums
def reset(self) -> List[int]:
"""
Resets the array to its original configuration and return it.
"""
return self.storeNum
def shuffle(self) -> List[int]:
"""
Returns a random shuffling of the array.
"""
shuffleNums = self.storeNum.copy()
random.shuffle(shuffleNums)
return shuffleNums
# Your Solution object will be instantiated and called as such:
# obj = Solution(nums)
# param_1 = obj.reset()
# param_2 = obj.shuffle()
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2,2]
示例 2:
输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [4,9]
说明:
输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
我们可以不考虑输出结果的顺序。
进阶:
如果给定的数组已经排好序呢?你将如何优化你的算法?
如果 nums1 的大小比 nums2 小很多,哪种方法更优?
如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
思路1:
本题的交集是不考虑顺序的,也就是等同于找出两个数组共同的数字。
那么首先遍历第一个数字,建立哈希表,记录每个数字出现的次数。
再遍历第二个数字,如果哈希表存在这个数字,且这个数字的次数大于0,那么为其中一个答案,且将出现的次数减1。
C++
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
map<int, int> idHash;
for(auto num : nums1){
// 第一次遇到字符
if(idHash.find(num) == idHash.end()){
idHash[num] = 1;
}else{
++idHash[num];
}
}
vector<int> ans;
for( auto num : nums2){
// 如果有字符,且大于0
if(idHash.find(num) != idHash.end() && idHash[num] > 0){
--idHash[num];
ans.push_back(num);
}
}
return ans;
}
};
Python
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
idHash = {}
for num in nums1:
if not num in idHash:
idHash[num] = 1
else:
idHash[num] += 1
ans = []
for num in nums2:
if num in idHash and idHash[num] > 0:
idHash[num] -= 1
ans.append(num)
return ans
思路2:
对两个数字进行排序,小的前,大的在后。依次比较两个数组的值,如果值相等,则为共同元素。如果数组1的值大于数组2的值,则把数组2的索引加1反之对数组1的索引加1(因为排序的缘故)。
C++
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
vector<int> ans;
// 排序
sort(nums1.begin(), nums1.end());
sort(nums2.begin(), nums2.end());
size_t index1 = 0;
size_t index2 = 0;
while( index1 < nums1.size() && index2 <nums2.size()){
if(nums1[index1] == nums2[index2]){
ans.push_back(nums1[index1]);
++index1;
++index2;
}else if(nums1[index1] > nums2[index2]){
++index2;
}else if(nums1[index1] < nums2[index2]){
++index1;
}
}
return ans;
}
};
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
ans = []
nums1 = sorted(nums1)
nums2 = sorted(nums2)
index1 = 0
index2 = 0
while index1 < len(nums1) and index2 < len(nums2):
if nums1[index1] == nums2[index2]:
ans.append(nums1[index1])
index1 += 1
index2 += 1
elif nums1[index1] < nums2[index2]:
index1 += 1
else:
index2 += 1
return ans
给定一个未排序的数组,判断这个数组中是否存在长度为 3 的递增子序列。
数学表达式如下:
如果存在这样的 i, j, k, 且满足 0 ≤ i < j < k ≤ n-1,
使得 arr[i] < arr[j] < arr[k] ,返回 true ; 否则返回 false 。
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1) 。
示例 1:
输入: [1,2,3,4,5]
输出: true
示例 2:
输入: [5,4,3,2,1]
输出: false
思路1:
参考这位大佬的思路。
思路非常的巧妙。
1.使用两个变量small
和mid
,用来保存序列的最小值和中间值,且满足关系small
2.遍历数组,遇到的值num
,如果比small
小,则替换small
。如果比mid
小,则替换mid
。如果比mid大,则找到递增序列。
3.比较巧妙的地方1.:如果遇到比small
小的值,则替换。比如序列small
和mid为[4,9],当遇到num
=1时,则small
和mid
则变为[1, 9]。如果不替换这个small
,那么接下来遇到2,3,其实是存在递增序列的,但却没有找到,所以需要替换。
4.比较巧妙的地方2.:当[4, 9]被替换成[1, 9]之后,虽然1和9不是序列递增的关系,但是当遇到的num
比mid
(即9)大,仍然是存在递增序列的,这是因为即使我们更新了 small
,这个 small
在 mid
后面,没有严格遵守递增顺序,但它隐含着的真相是,有一个比 small
大比 mid 小的前·最小值出现在 mid
之前。因此,当后续出现比 mid
大的值的时候,我们一样可以通过当前 small
和 mid
推断的确存在着长度为 3
的递增序列。 所以,这样的替换并不会干扰后续的计算。
C++
class Solution {
public:
bool increasingTriplet(vector<int>& nums) {
int small = INT_MAX;
int mid = INT_MAX;
for(auto num : nums){
if(num <= small){
small = num;
}else if(num <= mid){
mid = num;
}else{
return true;
}
}
return false;
}
};
Python
class Solution:
def increasingTriplet(self, nums: List[int]) -> bool:
small = sys.maxsize
mid = sys.maxsize
for num in nums:
if num <= small:
small = num
elif num <= mid:
mid = num
else:
return True
return False
思路2:
动态规划,每次寻找比当前数小的元素个数,当大于等于3时则退出。
C++
class Solution {
public:
bool increasingTriplet(vector<int>& nums) {
int size = nums.size();
vector<int> dp(size, 1);
for(int i = 0; i < nums.size(); ++i)
for(int j = 0; j < i; ++j){
// 从左边开始计算比i小的数
if(nums[j] < nums[i]){
dp[i] = max(dp[i], dp[j] + 1);
}
if(dp[i] >= 3)
return true;
}
return false;
}
};
Python
class Solution:
def increasingTriplet(self, nums: List[int]) -> bool:
dp = [1] * len(nums)
for i in range(len(nums)):
for j in range(0, i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j] + 1)
if dp[i] >= 3:
return True
return False
见:力扣高频|算法面试题汇总(一):开始之前
给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。
示例:
输入: [1,2,3,4]
输出: [24,12,8,6]
提示:题目数据保证数组之中任意元素的全部前缀元素和后缀(甚至是整个数组)的乘积都在 32 位整数范围内。
说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。
进阶:
你可以在常数空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)
思路1:
首先这题是不能使用除法的。假设有数组 [ a 0 , a 1 , a 2 , a 3 ] [a_0,a_1,a_2,a_3] [a0,a1,a2,a3],那么得到的答案为: [ a 1 a 2 a 3 , a 0 a 2 a 3 , a 0 a 1 a 3 , a 0 a 1 a 2 ] [a_1a_2a_3,a_0a_2a_3,a_0a_1a_3,a_0a_1a_2] [a1a2a3,a0a2a3,a0a1a3,a0a1a2]。拆分答案,可以得到的数组 [ 1 , a 0 , a 0 a 1 , a 0 a 1 a 2 ] [1,a_0,a_0a_1,a_0a_1a_2] [1,a0,a0a1,a0a1a2]和 [ a 1 a 2 a 3 , a 2 a 3 , a 3 , 1 ] [a_1a_2a_3,a_2a_3,a_3,1] [a1a2a3,a2a3,a3,1],这两个数组分布通过正序和逆序遍历得到。
时间复杂度: O ( N ) O(N) O(N),其中 N 指的是输入数组的大小。
空间复杂度: O ( N ) O(N) O(N),使用了 L 和 R 数组去构造答案
C++
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
vector<int> temp1(nums.size(), 1);
vector<int> temp2(nums.size(), 1);
for(int i = 1; i < nums.size(); ++i){
temp1[i] = temp1[i - 1] * nums[i - 1];
}
for(int i = nums.size() - 2; i > -1; --i){
temp2[i] = temp2[i + 1] * nums[i + 1];
}
for(int i = 0; i < nums.size(); ++i){
temp1[i] *= temp2[i];
}
return temp1;
}
};
Python
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
temp1 = [1] * len(nums)
temp2 = temp1.copy()
for i in range(1, len(nums)):
temp1[i] = temp1[i - 1] * nums[i - 1]
for i in range(len(nums) - 2, -1, -1):
temp2[i] = temp2[i + 1] * nums[i + 1]
for i in range(len(nums)):
temp1[i] *= temp2[i]
return temp1
思路2:
由于输出数组不算在空间复杂度内,那么可以将 L 或 R 数组在用输出数组来计算,然后再动态构造另一个。也就是首先得到数组得到的数组 [ 1 , a 0 , a 0 a 1 , a 0 a 1 a 2 ] [1,a_0,a_0a_1,a_0a_1a_2] [1,a0,a0a1,a0a1a2],再使用一个中间变量temp
,temp = temp * nums[i]
来从1开始到 a 1 a 2 a 3 a_1a_2a_3 a1a2a3依次赋值。
C++
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
vector<int> ans(nums.size(), 1);
for(int i = 1; i < nums.size(); ++i)
ans[i] = ans[i - 1] * nums[i - 1];
int temp = 1;
for(int i = nums.size() -1; i > -1; --i){
ans[i] = temp * ans[i];
temp *= nums[i];
}
return ans;
}
};
Python
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
ans = [1] * len(nums)
# 第一次构造
for i in range(1 , len(nums)):
ans[i] = ans[i - 1] * nums[i - 1]
# 使用中间变量,动态构造
temp = 1
for i in reversed(range(len(nums))):
ans[i] = temp * ans[i]
temp = temp * nums[i]
return ans