力扣刷题之数组(二)
200.岛屿数量
class Solution {
private:
void dfs(vector<vector<char>>& grid, int r, int c) {
int nr = grid.size();
int nc = grid[0].size();
grid[r][c] = '0';
if (r - 1 >= 0 && grid[r-1][c] == '1') dfs(grid, r - 1, c);
if (r + 1 < nr && grid[r+1][c] == '1') dfs(grid, r + 1, c);
if (c - 1 >= 0 && grid[r][c-1] == '1') dfs(grid, r, c - 1);
if (c + 1 < nc && grid[r][c+1] == '1') dfs(grid, r, c + 1);
}
public:
int numIslands(vector<vector<char>>& grid) {
int nr = grid.size();
if (!nr) return 0;
int nc = grid[0].size();
int num_islands = 0;
for (int r = 0; r < nr; ++r) {
for (int c = 0; c < nc; ++c) {
if (grid[r][c] == '1') {
++num_islands;
dfs(grid, r, c);
}
}
}
return num_islands;
}
};
204.计数质数
法一:枚举(超时)
class Solution {
public:
bool isPrime(int x) {
for (int i = 2; i * i <= x; ++i) {
if (x % i == 0) {
return false;
}
}
return true;
}
int countPrimes(int n) {
int ans = 0;
for (int i = 2; i < n; ++i) {
ans += isPrime(i);
}
return ans;
}
};
法二:埃氏筛(好神奇)
class Solution {
public:
int countPrimes(int n) {
vector<int> isPrime(n, 1);
int ans = 0;
for (int i = 2; i < n; ++i) {
if (isPrime[i]) {
ans += 1;
if ((long long)i * i < n) {
for (int j = i * i; j < n; j += i) {
isPrime[j] = 0;
}
}
}
}
return ans;
}
};
209.长度最小的子数组
法一:暴力法
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int n = nums.size();
if (n == 0) {
return 0;
}
int ans = INT_MAX;
for (int i = 0; i < n; i++) {
int sum = 0;
for (int j = i; j < n; j++) {
sum += nums[j];
if (sum >= s) {
ans = min(ans, j - i + 1);
break;
}
}
}
return ans == INT_MAX ? 0 : ans;
}
};
法二:滑动窗口
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int n = nums.size();
if (n == 0) {
return 0;
}
int ans = INT_MAX;
int start = 0, end = 0;
int sum = 0;
while (end < n) {
sum += nums[end];
while (sum >= s) {
ans = min(ans, end - start + 1);
sum -= nums[start];
start++;
}
end++;
}
return ans == INT_MAX ? 0 : ans;
}
};
法三:前缀和+二分查找
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int n = nums.size();
if (n == 0) {
return 0;
}
int ans = INT_MAX;
vector<int> sums(n + 1, 0);
// 为了方便计算,令 size = n + 1
// sums[0] = 0 意味着前 0 个元素的前缀和为 0
// sums[1] = A[0] 前 1 个元素的前缀和为 A[0]
// 以此类推
for (int i = 1; i <= n; i++) {
sums[i] = sums[i - 1] + nums[i - 1];
}
for (int i = 1; i <= n; i++) {
int target = s + sums[i - 1];
auto bound = lower_bound(sums.begin(), sums.end(), target);
if (bound != sums.end()) {
ans = min(ans, static_cast<int>((bound - sums.begin()) - (i - 1)));
}
}
return ans == INT_MAX ? 0 : ans;
}
};
215.数组中的第K个最大元素(经典!)
法一:大根堆
大根堆调库:
class Solution
{
public:
int findKthLargest(vector<int>& nums, int k)
{
priority_queue<int, vector<int>, less<int>> maxHeap;
for (int x : nums)
maxHeap.push(x);
for (int _ = 0; _ < k - 1; _ ++)
maxHeap.pop();
return maxHeap.top();
}
};
手撸大根堆:
class Solution {
public:
void maxHeapify(vector<int>& a, int i, int heapSize) {
int l = i * 2 + 1, r = i * 2 + 2, largest = i;
if (l < heapSize && a[l] > a[largest]) {
largest = l;
}
if (r < heapSize && a[r] > a[largest]) {
largest = r;
}
if (largest != i) {
swap(a[i], a[largest]);
maxHeapify(a, largest, heapSize);
}
}
void buildMaxHeap(vector<int>& a, int heapSize) {
for (int i = heapSize / 2; i >= 0; --i) {
maxHeapify(a, i, heapSize);
}
}
int findKthLargest(vector<int>& nums, int k) {
int heapSize = nums.size();
buildMaxHeap(nums, heapSize);
for (int i = nums.size() - 1; i >= nums.size() - k + 1; --i) {
swap(nums[0], nums[i]);
--heapSize;
maxHeapify(nums, 0, heapSize);
}
return nums[0];
}
};
法二:快排
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
int n=nums.size();
int le=0, ri=n-1;
while(true){
//快排框架
int i=le, j=ri;
int idx = rand() % (ri - le +1) + le;//随机选择pivot
swap(nums[le], nums[idx]);
while(i<j){
while(i<j&&nums[j]>=nums[le]) --j;
while(i<j&&nums[i]<=nums[le]) ++i;
swap(nums[i], nums[j]);
}
swap(nums[i], nums[le]);//nums[le]应处的位置在索引i处
if(i==n-k) return nums[i];//若恰为倒数第K的位置
else if(i>n-k) ri=i-1;//将查找范围放在该位置左侧
else le=i+1;//将查找范围放在该位置右侧
}
}
};
220.存在重复元素III(经典)
法一:滑动窗口+有序集合
class Solution {
public:
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
int n = nums.size();
set<int> rec;
for (int i = 0; i < n; i++) {
auto iter = rec.lower_bound(max(nums[i], INT_MIN + t) - t);
if (iter != rec.end() && *iter <= min(nums[i], INT_MAX - t) + t) {
return true;
}
rec.insert(nums[i]);
if (i >= k) {
rec.erase(nums[i - k]);
}
}
return false;
}
};
法二:桶
class Solution {
public:
int getID(int x, long w) {
return x < 0 ? (x + 1ll) / w - 1 : x / w;
}
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
unordered_map<int, int> mp;
int n = nums.size();
for (int i = 0; i < n; i++) {
long x = nums[i];
int id = getID(x, t + 1ll);
if (mp.count(id)) {
return true;
}
if (mp.count(id - 1) && abs(x - mp[id - 1]) <= t) {
return true;
}
if (mp.count(id + 1) && abs(x - mp[id + 1]) <= t) {
return true;
}
mp[id] = x;
if (i >= k) {
mp.erase(getID(nums[i - k], t + 1ll));
}
}
return false;
}
};
221.最大正方形
动态规划
class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
if (matrix.size() == 0 || matrix[0].size() == 0) {
return 0;
}
int maxSide = 0;
int rows = matrix.size(), columns = matrix[0].size();
vector<vector<int>> dp(rows, vector<int>(columns));
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
if (matrix[i][j] == '1') {
if (i == 0 || j == 0) {
dp[i][j] = 1;
} else {
dp[i][j] = min(min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
}
maxSide = max(maxSide, dp[i][j]);
}
}
}
int maxSquare = maxSide * maxSide;
return maxSquare;
}
};
229.求众数II
摩尔投票法
时间复杂度为O(n),空间复杂度为O(1)
class Solution {
public:
vector<int> majorityElement(vector<int>& nums) {
vector<int> ans;
if (nums.empty()) return ans;
int cand1, cand2;
int count1, count2;
cand1 = cand2 = count1 = count2 = 0;
// 抵消阶段
for (int i = 0; i < nums.size(); i++) {
if (nums[i] == cand1) count1++;
else if (nums[i] == cand2) count2++;
else if (count1 && count2) {
count1--;
count2--;
}
else if (!count1) {
cand1 = nums[i];
count1++;
}
else {
cand2 = nums[i];
count2++;
}
}
// 计数阶段
count1 = count2 = 0;
for (int i = 0; i < nums.size(); i++) {
if (nums[i] == cand1) count1++;
else if(nums[i] == cand2) count2++; // 必须用else if,保证每个数只算到一个候选人头上
else {}
}
if (count1 > nums.size() / 3) ans.push_back(cand1);
if (count2 > nums.size() / 3) ans.push_back(cand2);
return ans;
}
};
238.除自身以外数组的乘积
法一:左右乘积列表
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int length = nums.size();
// L 和 R 分别表示左右两侧的乘积列表
vector<int> L(length, 0), R(length, 0);
vector<int> answer(length);
// L[i] 为索引 i 左侧所有元素的乘积
// 对于索引为 '0' 的元素,因为左侧没有元素,所以 L[0] = 1
L[0] = 1;
for (int i = 1; i < length; i++) {
L[i] = nums[i - 1] * L[i - 1];
}
// R[i] 为索引 i 右侧所有元素的乘积
// 对于索引为 'length-1' 的元素,因为右侧没有元素,所以 R[length-1] = 1
R[length - 1] = 1;
for (int i = length - 2; i >= 0; i--) {
R[i] = nums[i + 1] * R[i + 1];
}
// 对于索引 i,除 nums[i] 之外其余各元素的乘积就是左侧所有元素的乘积乘以右侧所有元素的乘积
for (int i = 0; i < length; i++) {
answer[i] = L[i] * R[i];
}
return answer;
}
};
法二:空间复杂度为O(1)
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int length = nums.size();
vector<int> answer(length);
// answer[i] 表示索引 i 左侧所有元素的乘积
// 因为索引为 '0' 的元素左侧没有元素, 所以 answer[0] = 1
answer[0] = 1;
for (int i = 1; i < length; i++) {
answer[i] = nums[i - 1] * answer[i - 1];
}
// R 为右侧所有元素的乘积
// 刚开始右边没有元素,所以 R = 1
int R = 1;
for (int i = length - 1; i >= 0; i--) {
// 对于索引 i,左边的乘积为 answer[i],右边的乘积为 R
answer[i] = answer[i] * R;
// R 需要包含右边所有的乘积,所以计算下一个结果时需要将当前值乘到 R 上
R *= nums[i];
}
return answer;
}
};
240.搜索二维矩阵II
法一:单调性扫描(线性扫描)
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if(!matrix.size() && !matrix[0].size()) return false;
int i = 0, j = matrix[0].size() - 1; //矩阵右上角
while(i < matrix.size() && j >= 0)
{
if(matrix[i][j] == target) return true;
else if( matrix[i][j] < target) i++; //排除一行
else if( matrix[i][j] > target) j--; //排除一列
}
return false;
}
};
法二:二分法
class Solution {
public:
bool bsearch(vector<vector<int>>& matrix, int target,int line){
int l = 0; int r = matrix[line].size()-1;
while(l<r){
int mid = l+r>>1;
if(matrix[line][mid] >= target){ r =mid; }
else { l = mid+1;}
}
if(matrix[line][l] == target) return true;
return false;
}
bool searchMatrix(vector<vector<int>>& matrix, int target) {
for(int i = 0; i < matrix.size();i++){
if(matrix[i][0] > target){break;}
if( matrix[i].back() < target) {continue;}
if(bsearch(matrix,target,i) == true) return true;
}
return false;
}
};
260.只出现一次的数字III
异或法(线性时间复杂度,常数空间复杂度)
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
int ret = 0;
for (int n : nums)
ret ^= n;
int div = 1;
while ((div & ret) == 0)
div <<= 1;
int a = 0, b = 0;
for (int n : nums)
if (div & n)
a ^= n;
else
b ^= n;
return vector<int>{a, b};
}
};
剑指Offer II 080.含有k个元素的组合
回溯+递归
// 回溯算法
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
path.clear();
ans.clear();
backtrack(n, k, 1);
return ans;
}
private:
vector<int> path;
vector<vector<int>> ans;
void backtrack(int n, int k, int index) {
// 当子集里面有两个数的时候,则终止递归
if (path.size() == k) {
ans.push_back(path);
return;
}
// for循环遍历集合区间
for (int i = index; i <= n; i++) {
path.push_back(i);
backtrack(n, k, i + 1);//递归
path.pop_back();//回溯
}
}
};
剑指Offer II 079.所有子集
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
void dfs(vector<int> nums,int pos){
ans.push_back(path);
for(int i = pos; i < nums.size() ; i++){
path.push_back(nums[i]);
dfs(nums,i+1);
path.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
int n = nums.size();
dfs(nums,0);
return ans;
}
};