在sorted array中尋找兩個值的和。
vector<int> nums{1, 2, 4, 5, 7};
// 所有可能認兩個element的和
// 2,3,4,5,6,6,7,8,8,9,9,10,11,12,14
// 從最左邊和最右邊開始
int left = 0, right = nums.size() - 1;
// {1}, 2, 4, 5, [7],就是選中所以組合的中間值
// 2,3,4,5,6,6,7,8,[8],9,9,10,11,12,14
// case 1如果left++,就是往比較大的值移動
// 2,3,4,5,6,6,7,8,8,[9],9,10,11,12,14
// case 2如果right--,就是往比較小的值移動
// 2,3,4,5,6,[6],7,8,8,9,9,10,11,12,14
// 所以靠移動left, right就可以快速找到任何想要的two sum
while(left <= right) // 因為left和right可以一樣,使用等於
{
int sum = nums[left] + nums[right];
if(sum == target) return true;
else if(sum > target) --right;
else ++left;
}
return false;
給一個遞增的array,輸出每個數字平方的遞增array。
使用兩個pointer,一個指向開頭,一個指向結尾,因為這是一個遞增array,平方後的最大值只會出現在頭尾兩端。
vector<int> sortedSquares(vector<int>& nums) {
auto n = nums.size();
int i = 0, j = n - 1, k = n - 1;
vector<int> ret(n);
while(j > i) {
if(abs(nums[i]) > abs(nums[j])) {
ret[k--] = nums[i] * nums[i];
i++;
} else {
ret[k--] = nums[j] * nums[j];
j--;
}
}
ret[0] = nums[i] * nums[i];
return ret;
}
將偶數的index放偶數,把奇數的index放奇數。
一樣使用兩個pointers來排列。一個指向偶數index,一個指向奇數index,如果偶數index不是偶數,奇數index不是奇數就進行交換。
vector<int> sortArrayByParityII(vector<int>& nums) {
auto n = nums.size();
int i = 0, j = 1;
while(i < n && j < n) {
for(;i < n && nums[i] % 2 == 0; i+= 2);
for(;j < n && nums[j] % 2 == 1; j+= 2);
if(i >= n || j >= n) break;
else swap(nums[i], nums[j]);
}
return nums;
}
這是一題經典題。給你一個int的array nums找出,nums[i]+nums[j]+nums[k]的和為零。其中i<j<k。也就是i,j,和k不相等。
解題思路:
- 先把array排序。因為這樣使用two pointer來尋找會比較有效率。
- 固定一個數字nums[i],則題目變為two sum,nums[j] + nums[k] = -nums[i]
- 最後使用two pointer來找解答。
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
if(nums.empty() || nums.front() > 0 || nums.back() < 0) return {};
auto nsize = nums.size();
vector<vector<int>> res;
for(int k = 0; k < nsize; ++k) {
if(nums[k] > 0) break; // 因為排序過,後面數值皆為正
int target = 0 - nums[k];
int i = k + 1; // 從k的下一個
int j = nsize - 1;// 和從最後面找起
while(i < j) {
if(nums[i] + nums[j] == target) {
res.push_back({nums[k], nums[i], nums[j]});
// 跳過重複的數值
while (i < j && nums[i] == nums[i + 1]) ++i;
while (i < j && nums[j] == nums[j - 1]) --j;
++i;
--j;
} else if(nums[i] + nums[j] > target) {
// 因為和比較大,所以最大值減少
--j;
} else {
// 因為和比較小,所以最小值增加
++i;
}
}
}
return res;
}
和3sum類似,這次改成4sum,並且不可以有重複的答案。
- 一樣用左右pointer分別來逼近答案。
- 遇到一樣的數字則跳過.
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> rtn;
auto n = nums.size();
if(n < 3) return {};
sort(nums.begin(), nums.end());
for(int i = 0; i < n - 3; ++i) {
// i 為第二個以後,遇到一樣的則跳過
if(i > 0 && nums[i] == nums[i - 1]) continue;
for(int j = i + 1; j < n - 2; ++j) {
// j為i + 2以後,遇到一樣的則跳過
if(j > i + 1 && nums[j] == nums[j - 1]) continue;
int left = j + 1, right = n - 1;
while(left < right) {
long sum = (long)nums[i] + nums[j] + nums[left] + nums[right];
if(sum == target) {
rtn.push_back({nums[i], nums[j], nums[left], nums[right]});
// 跳過下一個一樣的數字
while(left < right && nums[left] == nums[left + 1]) left++;
while(left < right && nums[right] == nums[right - 1]) right--;
left++;
right--;
} else if(sum < target)
left++;
else
right--;
}
}
}
return rtn;
}
給定兩個數列vector<vector<int>> firstList和secondList,求出兩個數列的交集。
例如:
Input:
Output:
解題思路
- 列出所有的關係。
- case 1 : secondList[j][start] 都在 firstList[i][start] 前面。
- case 2 : secondList[j][start] 都在 firstList[i]中間。
- case 3 : secondList[j][start] 都在 firstList[i][end] 後面。
- 交集 = {max(firstList[i][start], secondList[j][start]),
min(firstList[i][end], secondList[j][end])}
case 1 :
case 2 :
case 3 :
class Solution {
int start = 0;
int end = 1;
public:
vector<vector<int>> intervalIntersection(vector<vector<int>>& firstList,
vector<vector<int>>& secondList) {
auto L1 = firstList.size();
auto L2 = secondList.size();
// special case
if(L1 == 0 || L2 == 0) return {};
vector<vector<int>> rtn;
int i{0}, j{0};
while(i < L1 && j < L2) {
// case 1-1
if(firstList[i][start] > secondList[j][end])
j++;
// case 3-1
else if(secondList[j][start] > firstList[i][end])
i++;
else {
rtn.push_back({max(firstList[i][start], secondList[j][start])
,min(firstList[i][end], secondList[j][end])});
// case 1-2, 1-3, 2-1
if(firstList[i][end] > secondList[j][end])
j++;
// case 1-5, 2-3
else if(secondList[j][end] > firstList[i][end])
i++;
// case 1-4, 2-2
else {
i++;
j++;
}
}
}
return rtn;
}
};
給你一個數列,求出任兩個值中間可以儲存的水是最大值。
- 因為最大的寬(right - left)是最左和最右的點{height[0], height[n - 1]}。
- 下一個寬(right - left -1)有兩個可能,{height[0], height[n - 2]}和{height[1], height[n - 1]}。
- 短邊往中間前進,可以得到最好的結果。
int maxArea(vector<int>& height) {
int rtn = -1;
int left = 0, right = height.size() - 1;
do{
rtn = max(rtn, min(height[left], height[right]) * (right - left));
height[left] < height[right] ? left++ : right--;
} while(left < right);
return rtn;
}
給你一個vector<int> nums,其中數值只有0, 1, 2。照由小大到大排序。
- 這一題可以有很多解法。最簡單就是統計0, 1, 2出現的次數,然後依序寫回nums。
- 另一種使用two pointers,一個指向red(0)應該出現的位置,一個指向blue(2)應該出現的位置。另外使用另一個指標i來看是否需要交換位置。
void sortColors(vector<int>& nums) {
auto n = nums.size();
if(n == 1) return;
int red = 0, blue = n - 1;
for(int i = 0; i <= blue;) {
if(nums[i] == 0)
swap(nums[i++], nums[red++]);
// i的位置不動,因為可能交換了0/1過來,需要重新判斷
else if(nums[i] == 2)
swap(nums[i], nums[blue--]);
else
i++;
}
}
給你一個vector<int>,試問這個是不是moutain array。
moutain array的定義是指存在一個top(i),使得左邊的數和右邊的數都小於top。
- 使用two pointer從左右邊前進。
- 如果兩個pointer相遇在非開始結尾的地方,就是motain array。
bool validMountainArray(vector<int>& arr) {
auto n = arr.size();
if(n <= 2) return false;
int left = 0, right = n - 1;
while(left < n && arr[left] < arr[left + 1]) left++;
while(right > 0 && arr[right] < arr[right - 1]) right--;
if(left == 0 || left == n - 1)
return false;
else
return left == right;
}
找出比n還大的下一個最小的排列數。
class Solution {
// 沒有答案的數字為 54321 依序遞減的數字,因為這樣最大
// 例如 : (3)54321, 前面多了一個3
// 從後面尋找比3還大的數,就會找到4
// 把兩個數對調後, (4)53321
// 因為後面是遞增數列,必須改成遞減才是最小的數
// 所以變成 (4)12335
bool nextGreaterElement(string& s) {
int sz = s.length();
// 從後面開始尋找突然變小的數
int i = sz - 2;
while(i >= 0 && s[i] >= s[i + 1]) --i;
if(i < 0) return false; // 找不到變小的數
// 從後面找比這個數還大的數
int j = sz - 1;
while(j > i && s[j] <= s[i]) --j;
swap(s[i], s[j]); // 對調
reverse(s.begin() + i + 1, s.end()); // 後面的數改成遞增,這樣才是最小的
return true;
}
public:
int nextGreaterElement(int n) {
string s = to_string(n);
if(s.length() == 1) return -1;
if(nextGreaterElement(s)) {
long long val = stoll(s);
return val > INT_MAX || val == n ? -1 : val;
} else
return -1;
}
};
找出subarray的和為最大,並且subarray中的element不重複。
- 因為subarray為連續的所以使用two-pointer。
- 使用一個vector來記錄是否有重複的element。
- 當有重複的element(val[nums[right]] > 1),則把left往前推進直到條件式消失。
int maximumUniqueSubarray(vector<int>& nums) {
int sz = nums.size();
if(sz == 1) return nums[0];
vector<int> val(1e4 + 1);
int maxans = 0, sum = 0, left = 0, right = 0;
for(; right < sz; ++right) {
sum += nums[right];
val[nums[right]]++;
while(val[nums[right]] > 1) {
sum -= nums[left];
val[nums[left++]]--;
}
maxans = max(maxans, sum);
}
return maxans;
}
給你一個dominoes,和同時會從幾個地方往左或是右邊推,求最後的狀態。
一開始想要用queue來統計狀態轉移,不過好像很麻煩
當沒什麼頭緒的時候,應該先分析有幾種狀況。
- L…L 或是 R…R 也就是兩邊都是一樣,這樣中間也一定是一樣。
- L…R 往兩邊倒,中間不會影響。
- R…L 往中間倒,則需知道中間'.'的個數是否為奇數,因為奇數的話中間還是'.',兩邊各為R和L。
前後加上'L' 和 'R'可以讓程式不用判斷邊界條件。
string pushDominoes(string dominoes) {
string rtn{""};
string d = 'L' + dominoes + 'R';
for(int i = 0, j = 1; j < d.size(); ++j) {
if(d[j] == '.') continue;
if(i > 0) rtn += d[i];
int len = j - i - 1;
// case 1
if(d[i] == d[j]) rtn += string(len, d[i]);
// case 2
else if(d[i] == 'L' && d[j] == 'R') rtn += string(len, '.');
// case 3
else rtn += string(len / 2, 'R') + string(len % 2, '.') + string(len / 2, 'L');
i = j;
}
return oss.str();
}
class Solution {
// 0, 1, 2, 3, 4, 5, 6, 7 index
// [1, 2, 3, 10], 4, [2, 3, 5], sz = 8
// r step1 : 找出r的位置,如果r == 0 就是不需要刪任何element
// L --> step2 : L從0開始找,每找到一個就檢查是否可以和r接起來
// step3 : 接不起來就把r倒退,直到條件成立,儲存L~R之間長度的最小值
public:
int findLengthOfShortestSubarray(vector<int>& nums) {
int sz = nums.size(), right = sz - 1;
// step1 : find out the right position
while(right > 0 && nums[right] >= nums[right - 1]) --right;
if(right == 0) return 0;
int ans = right;
// step2 : search left from index 0 to sz - 1
for(int left = 0; left < sz; ++left) {
if(left > 0 && nums[left] < nums[left - 1]) break;
// step3 : find a left then check if nums[left] <= nums[right]
// if not back the right
while(right < sz && nums[left] > nums[right]) ++right;
ans = min(ans, right - left - 1);
}
return ans;
}
};
找出所有subsequences的個數,符合最大值+最小值 <= target。
- 因為是subsequence,所以可以先對nums做排序,不影響結果。
- 因為只需知道最大值+最小值<=target,所以使用two pointer
- 最大最小值決定了,subsequence的個數就是中間值選或不選的個數。pow(2, right - left);
class Solution {
// two sum, [1, 2, 3, 4, 5]
// 2, 3, 4, 5, [6], 7, 8, 9, 10
// 1 + 5就是中間值,right--就是往下,left++就是往上
public:
int numSubseq(vector<int>& A, int target) {
sort(A.begin(), A.end());
int res = 0, n = A.size(), l = 0, r = n - 1, mod = 1e9 + 7;
// 因為pow(2, N) 會有int overflow
// 所以先存在vector中
vector<int> pows(n, 1);
for (int i = 1 ; i < n ; ++i)
pows[i] = pows[i - 1] * 2 % mod;
while (l <= r) {
if (A[l] + A[r] > target) {
r--;
} else {
res = (res + pows[r - l]) % mod;
l++;
}
}
return res;
}
};
leetcode
刷題