雙指標
922. 按奇偶排序陣列 II
#include <vector>
using namespace std;
class Solution {
public:
// 時間複雜度 O(n),額外空間複雜度 O(1)
vector<int> sortArrayByParityII(vector<int> &nums) {
int even = 0;
int odd = 1;
while (even < nums.size() - 1 && odd < nums.size()) {
// 找到偶數下標,但元素是奇數的位置
while (even < nums.size() - 1 && ((nums[even] & 1) == 0)) even += 2;
// 找到奇數下標,但元素是偶數的位置
while (odd < nums.size() && ((nums[odd] & 1) != 0)) odd += 2;
// 交換
if (even < nums.size() - 1 && odd < nums.size()) swap(nums[even], nums[odd]);
}
return nums;
}
};
287. 尋找重複數
- 要求 不修改 陣列
nums
且只用常量級 O(1)
的額外空間。
#include <vector>
using namespace std;
class Solution {
public:
// 時間複雜度 O(n),額外空間複雜度 O(1)
// 類似環形連結串列找入口節點,nums 陣列類似靜態連結串列
int findDuplicate(vector<int> &nums) {
int slow = 0, fast = 0;
slow = nums[slow];
fast = nums[nums[fast]];
while (slow != fast) {
// slow = slow.next
slow = nums[slow];
// fast = fast.next.next
fast = nums[nums[fast]];
}
fast = 0;
while (slow != fast) {
slow = nums[slow];
fast = nums[fast];
}
return slow;
}
};
42. 接雨水
#include <vector>
using namespace std;
class Solution {
public:
// 按行累積,每次累加當前行上能接多少水(超時)
int trap(vector<int> &height) {
int n = height.size();
// 找最大高度
int maxHeight = 0;
for (const auto &item: height)
maxHeight = max(maxHeight, item);
int res = 0;
// 每次找一層,一格一格的加
for (int level = 1; level <= maxHeight; ++level) {
int i = 0;
// 找到第一個不低於當前 lever 的作為左邊界
while (i < n && height[i] < level) i++;
// 找不到左邊界,這層以及上面的層都接不了水
if (i >= n) break;
for (int water = 0; i < n; i++) {
if (height[i] < level) {
// 已有左邊界,並且比當前層低,說明這個格子 (i, lever) 可以放水
water++;
} else if (height[i] >= level) {
// 找到大於或等於當前層的右邊界,就把之前累積的水加到結果中,並清空 water
// 當前的右邊界變成下一個左邊界,在繼續尋找下一個右邊界
res += water;
water = 0;
}
}
}
return res;
}
};
#include <vector>
using namespace std;
class Solution {
public:
// 時間複雜度 O(n),額外空間複雜度 O(n)
// 按列累積,每次累加當前列上能接多少水
int trap(vector<int> &height) {
int n = height.size();
int res = 0;
// 記錄當前元素左邊的最大值
int leftMax = height[0];
// 可以在後續累積雨水時遍歷陣列的操作中,用 leftMax 最佳化掉 leftMaxArr
vector<int> leftMaxArr(n);
// 更新每個元素左邊的最大值
for (int i = 1; i <= n - 2; ++i) {
leftMaxArr[i] = leftMax;
leftMax = max(leftMax, height[i]);
}
// 記錄當前元素右邊的最大值
int rightMax = height[n - 1];
vector<int> rightMaxArr(n);
for (int i = n - 2; i >= 1; i--) {
rightMaxArr[i] = rightMax;
rightMax = max(rightMax, height[i]);
}
// 只有左邊最高的和右邊最高的,二者中的較小者比當年的列高,當前列才能接得住水
for (int i = 1; i <= n - 2; ++i)
res += max(0, min(leftMaxArr[i], rightMaxArr[i]) - height[i]);
return res;
}
};
- 雙指標最佳化掉 leftMaxArr、rightMaxArr(最優解)
#include <vector>
using namespace std;
class Solution {
public:
// 時間複雜度 O(n),額外空間複雜度 O(1)
int trap(vector<int> &height) {
int n = height.size();
int l = 1;
int r = n - 2;
// 左邊最高
int lMax = height[0];
// 右邊最高
int rMax = height[n - 1];
int res = 0;
while (l <= r) {
if (lMax <= rMax) {
// 左邊的最高柱子較低些
res += max(0, lMax - height[l]);
lMax = max(lMax, height[l++]);
} else {
// 右邊的最高柱子較低些
res += max(0, rMax - height[r]);
rMax = max(rMax, height[r--]);
}
}
return res;
}
};
#include <vector>
#include <stack>
using namespace std;
class Solution {
public:
int trap(vector<int> &height) {
int res = 0;
stack<int> stk;
for (int i = 0; i < height.size(); i++) {
// 當前高度大於棧頂高度,說明之前的地方有可能能接水
// 持續出棧,直到棧頂高度大於等於當前高度或者棧為空
while (!stk.empty() && height[i] > height[stk.top()]) {
int top = height[stk.top()];
stk.pop();
if (stk.empty()) break;
// 兩個柱子間的距離,不包含兩端
int distance = i - stk.top() - 1;
// height[stk.top()] 為左邊界,height[i] 為右邊界
int smaller = min(height[stk.top()], height[i]);
res += distance * (smaller - top);
}
stk.emplace(i);
}
return res;
}
};
881. 救生艇
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
// 時間複雜度 O(n * logn),因為有排序,額外空間複雜度 O(1)
int numRescueBoats(vector<int> &people, int limit) {
sort(people.begin(), people.end());
int res = 0;
for (int l = 0, r = people.size() - 1; l <= r; res++) {
int sum = l == r ? people[l] : people[l] + people[r];
// 如果沒超重,最輕的可以和最重的共用一條船
if (sum <= limit) l++;
r--;
}
return res;
}
};
11. 盛最多水的容器
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
// 時間複雜度 O(n),額外空間複雜度 O(1)
int maxArea(vector<int> &height) {
int res = 0;
for (int l = 0, r = height.size() - 1; l < r;) {
int water = (r - l) * min(height[l], height[r]);
res = max(res, water);
// 每次把短板往中間靠,短板可能變長,總面積才可能變大
// 如果移動長板,底一定變小,高度不會超過之前的那個短板,高只會原來越低,面積只會變小
if (height[l] < height[r]) {
l++;
} else {
r--;
}
}
return res;
}
};
475. 供暖器
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
// 返回當前的地點 houses[i] 由 heaters[j] 來供暖是否是最優
// 當前的地點 houses[i] 由 heaters[j] 來供暖,產生的半徑是a
// 當前的地點 houses[i] 由 heaters[j + 1] 來供暖,產生的半徑是 b
// 如果 a < b, 說明是最優,供暖不應該跳下一個位置
// 如果 a >= b, 說明不是最優,應該跳下一個位置
bool best(vector<int> &houses, vector<int> &heaters, int i, int j) {
return j == heaters.size() - 1
|| abs(heaters[j] - houses[i]) < abs(heaters[j + 1] - houses[i]);
}
// 時間複雜度 O(n * logn),因為有排序,額外空間複雜度 O(1)
int findRadius(vector<int> &houses, vector<int> &heaters) {
sort(houses.begin(), houses.end());
sort(heaters.begin(), heaters.end());
int res = 0;
// i 號房屋,j 號供暖器
for (int i = 0, j = 0; i < houses.size(); i++) {
while (!best(houses, heaters, i, j)) j++;
res = max(res, abs(heaters[j] - houses[i]));
}
return res;
}
};
41. 缺失的第一個正數
- 原地對映:把值為 value 的元素對映到陣列中下標為 value - 1 的位置
#include <vector>
using namespace std;
class Solution {
public:
// 原地對映:把值為 value 的元素對映到陣列中下標為 value-1 的位置
// 最佳化了對對映後覆蓋掉的值的處理
int firstMissingPositive(vector<int> &nums) {
int n = nums.size();
for (int i = 0; i < n; ++i) {
// 如果 nums[i] 也能對映到陣列中,並且尚未對映過
while (nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {
// 把 nums[i] 對映到 nums[nums[i] - 1],nums[nums[i] - 1] 放在 nums[i]
// 然後繼續判斷新的 nums[i] 是否也需要對映
swap(nums[nums[i] - 1], nums[i]);
}
}
// 遍歷尋找第一個空缺
for (int i = 0; i < n; ++i)
if (nums[i] != i + 1)
return i + 1;
// 重新對映後,陣列中沒有空缺,說明缺失的第一個正數就是 n + 1
return n + 1;
}
};
#include <vector>
#include <valarray>
using namespace std;
class Solution {
public:
// 原地對映:把可以對映到的地方的元素改成負數,對映規則還是 value 對映到 nums[value-1]
int firstMissingPositive(vector<int> &nums) {
int n = nums.size();
// 非正數改成 n + 1
for (int i = 0; i < n; ++i)
if (nums[i] <= 0)
nums[i] = n + 1;
for (int i = 0; i < n; ++i) {
// 待對映的值
int value = abs(nums[i]);
// 如果可以對映到陣列裡,就把對映到的地方的元素改成負數
if (value <= n) nums[value - 1] = -abs(nums[value - 1]);
}
for (int i = 0; i < n; ++i)
if (nums[i] > 0)
return i + 1;
return n + 1;
}
};
#include <vector>
using namespace std;
class Solution {
public:
// 時間複雜度 O(n),額外空間複雜度 O(1)
int firstMissingPositive(vector<int> &nums) {
// [0, l) 為已經對映的區域,l 是待處理的位置
int l = 0;
// [r, ...) 為無法對映的區域
// r 的第二個含義是這 r 個數最好的情況下能對映到 [0, r-1]
// 當有一個數字無法對映的時候,就剩下 r-1 個待對映的,最好情況下能對映到 [0, r-2]
int r = nums.size();
// [l, r) 為待對映的區域
while (l < r) {
if (nums[l] == l + 1) {
// 已經對映好了,已對映區域右擴
l++;
} else if (nums[l] <= l || nums[l] > r || nums[nums[l] - 1] == nums[l]) {
// 把對映不了的數字或者重複出現的數字,與後面待對映的數字交換,無法對映的區域左擴
// nums[l] <= l 說明 nums[l] 已經對映好了,已經對映的區域中有重複
// nums[l] > r 說明無法對映
// nums[nums[l] - 1] == nums[l] 也是說明 nums[l] 已經對映好了,待對映區域中有重複
swap(nums[l], nums[--r]);
} else {
// 可以對映
swap(nums[l], nums[nums[l] - 1]);
}
}
return l + 1;
}
};