1三數之和
給你一個整數數組?nums
?,判斷是否存在三元組?[nums[i], nums[j], nums[k]]
?滿足?i != j
、i != k
?且?j != k
?,同時還滿足?nums[i] + nums[j] + nums[k] == 0
?。請
你返回所有和為?0
?且不重復的三元組。
注意:答案中不可以包含重復的三元組。
示例 1:
輸入:nums = [-1,0,1,2,-1,-4] 輸出:[[-1,-1,2],[-1,0,1]] 解釋: nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。 nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。 nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。 不同的三元組是 [-1,0,1] 和 [-1,-1,2] 。 注意,輸出的順序和三元組的順序并不重要。
示例 2:
輸入:nums = [0,1,1] 輸出:[] 解釋:唯一可能的三元組和不為 0 。
示例 3:
輸入:nums = [0,0,0] 輸出:[[0,0,0]] 解釋:唯一可能的三元組和為 0 。
思路:
首先將數組排序,然后有一層for循環,i從下標0的地方開始,同時定一個下標left 定義在i+1的位置上,定義下標right 在數組結尾的位置上。
在數組中找到 abc 使得a + b +c =0,我們這里相當于 a = nums[i],b = nums[left],c = nums[right]。
接下來如何移動left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就說明 此時三數之和大了,因為數組是排序后了,所以right下標就應該向左移動,這樣才能讓三數之和小一些。
如果 nums[i] + nums[left] + nums[right] < 0 說明 此時 三數之和小了,left 就向右移動,才能讓三數之和大一些,直到left與right相遇為止。
本題解題思路重點是去重:
- 外層循環中的條件判斷:在每次循環開始時,通過判斷當前數字與前一個數字是否相同來避免重復計算相同的數字。如果當前數字與前一個數字相同,則跳過當前循環,繼續下一個數字的處理。
if(i > 0 && nums[i] == nums[i - 1])continue;
- 內層循環中的去重操作:在找到符合條件的三元組后,通過向右移動左指針和向左移動右指針,并跳過與移動后相同的元素,來避免重復。
while(left < right && nums[left] == nums[left + 1])left++;while(left < right && nums[right] == nums[right - 1])right--;
完整代碼:
class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {vector<vector<int>> ans; // 用于存儲結果的二維向量// 對數組進行排序,方便后續操作sort(nums.begin(), nums.end());// 遍歷數組for(int i = 0; i < nums.size(); i++) {// 避免重復計算相同的數字if(i > 0 && nums[i] == nums[i - 1])continue;int left = i + 1; // 左指針,指向當前數字的下一個位置int right = nums.size() - 1; // 右指針,指向數組末尾int target = 0 - nums[i]; // 目標值為當前數字的相反數// 在左右指針沒有相遇的情況下進行查找while(left < right) {// 如果找到了三個數的和等于目標值if(nums[left] + nums[right] == target) {// 將結果加入到答案中ans.push_back({nums[i], 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(nums[left] + nums[right] < target) {left++;} // 如果三數之和大于目標值,則右指針向左移動else {right--;}}}return ans; // 返回結果}
};
2四數之和
給你一個由?n
?個整數組成的數組?nums
?,和一個目標值?target
?。請你找出并返回滿足下述全部條件且不重復的四元組?[nums[a], nums[b], nums[c], nums[d]]
?(若兩個四元組元素一一對應,則認為兩個四元組重復):
0 <= a, b, c, d?< n
a
、b
、c
?和?d
?互不相同nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按?任意順序?返回答案 。
示例 1:
輸入:nums = [1,0,-1,0,-2,2], target = 0 輸出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:
輸入:nums = [2,2,2,2,2], target = 8 輸出:[[2,2,2,2]]
提示:
1 <= nums.length <= 200
-109 <= nums[i] <= 109
-109 <= target <= 109
思路:
-
剪枝處理
:在這里,剪枝指的是通過判斷當前數值是否已經大于目標值(或者非負且大于等于目標值),如果是的話就可以跳出循環,因為后面的數值只會更大,不會再符合條件了。這里使用break
語句跳出循環,統一通過return
返回最終結果。 -
對nums[k]去重
:這里的去重處理是指如果當前數字與前一個數字相同(除了第一個數字外),則跳過當前循環,繼續下一個數字的處理,以避免重復計算相同的數字。 -
2級剪枝處理
:類似于剪枝處理,這里也是對兩個數字的和進行判斷,如果已經大于目標值(或者非負且大于等于目標值),則跳出循環,因為后面的數值只會更大,不會再符合條件了。 -
對nums[i]去重
:與對nums[k]去重類似,這里是對內層循環中的第二個數字進行去重處理,避免重復計算相同的數字。 -
對nums[left]和nums[right]去重
:在找到符合條件的四元組后,通過向右移動左指針和向左移動右指針,并跳過與移動后相同的元素,來避免重復。
代碼:
class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {// 存儲結果的二維向量vector<vector<int>> result;// 對數組進行排序,方便后續處理sort(nums.begin(), nums.end());// 外層循環遍歷數組for (int k = 0; k < nums.size(); k++) {// 剪枝處理:如果當前數字已經大于目標值且為非負數,跳出循環if (nums[k] > target && nums[k] >= 0) {break; // 這里使用break,統一通過最后的return返回}// 對當前數字進行去重處理if (k > 0 && nums[k] == nums[k - 1]) {continue;}// 內層循環遍歷數組for (int i = k + 1; i < nums.size(); i++) {// 2級剪枝處理:如果當前兩個數字的和已經大于目標值且為非負數,跳出循環if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0) {break;}// 對第二個數字進行去重處理if (i > k + 1 && nums[i] == nums[i - 1]) {continue;}// 定義左右指針int left = i + 1;int right = nums.size() - 1;// 在左右指針未相遇之前進行循環while (right > left) {// 避免溢出,使用 long 類型進行判斷if ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) {right--;} else if ((long) nums[k] + nums[i] + nums[left] + nums[right] < target) {left++;} else {// 找到符合條件的四元組,加入結果向量result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});// 對左右指針所指的數字進行去重處理while (right > left && nums[right] == nums[right - 1]) right--;while (right > left && nums[left] == nums[left + 1]) left++;// 找到答案時,雙指針同時收縮right--;left++;}}}}// 返回最終結果return result;}
};
3移除元素
給你一個數組?nums
?和一個值?val
,你需要?原地?移除所有數值等于?val
?的元素。元素的順序可能發生改變。然后返回?nums
?中與?val
?不同的元素的數量。
假設?nums
?中不等于?val
?的元素數量為?k
,要通過此題,您需要執行以下操作:
- 更改?
nums
?數組,使?nums
?的前?k
?個元素包含不等于?val
?的元素。nums
?的其余元素和?nums
?的大小并不重要。 - 返回?
k
。
用戶評測:
評測機將使用以下代碼測試您的解決方案:
int[] nums = [...]; // 輸入數組 int val = ...; // 要移除的值 int[] expectedNums = [...]; // 長度正確的預期答案。// 它以不等于 val 的值排序。int k = removeElement(nums, val); // 調用你的實現assert k == expectedNums.length; sort(nums, 0, k); // 排序 nums 的前 k 個元素 for (int i = 0; i < actualLength; i++) {assert nums[i] == expectedNums[i]; }
如果所有的斷言都通過,你的解決方案將會?通過。
示例 1:
輸入:nums = [3,2,2,3], val = 3 輸出:2, nums = [2,2,_,_] 解釋:你的函數函數應該返回 k = 2, 并且 nums 中的前兩個元素均為 2。 你在返回的 k 個元素之外留下了什么并不重要(因此它們并不計入評測)。
示例 2:
輸入:nums = [0,1,2,2,3,0,4,2], val = 2 輸出:5, nums = [0,1,4,0,3,_,_,_] 解釋:你的函數應該返回 k = 5,并且 nums 中的前五個元素為 0,0,1,3,4。 注意這五個元素可以任意順序返回。 你在返回的 k 個元素之外留下了什么并不重要(因此它們并不計入評測)。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
思路:
慢指針?slowIndex
?指向下一個待賦值的位置,快指針?fastIndex
?遍歷數組。當快指針指向的元素不等于目標值時,將其賦值給慢指針指向的位置,并將慢指針向后移動一位。這樣就實現了移除數組中指定值的元素,并且返回新數組的長度
代碼:
class Solution {
public:// 移除元素函數int removeElement(vector<int>& nums, int val) {// 慢指針初始位置int slowIndex = 0;// 快指針遍歷數組for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {// 如果當前值不等于目標值if (val != nums[fastIndex]) {// 將當前值移到慢指針位置,并將慢指針向后移動一位nums[slowIndex++] = nums[fastIndex];}}// 返回新數組的長度return slowIndex;}
};
4反轉字符串
編寫一個函數,其作用是將輸入的字符串反轉過來。輸入字符串以字符數組?s
?的形式給出。
不要給另外的數組分配額外的空間,你必須原地修改輸入數組、使用 O(1) 的額外空間解決這一問題。
示例 1:
輸入:s = ["h","e","l","l","o"] 輸出:["o","l","l","e","h"]
示例 2:
輸入:s = ["H","a","n","n","a","h"] 輸出:["h","a","n","n","a","H"]
提示:
1 <= s.length <= 105
s[i]
?都是?ASCII?碼表中的可打印字符
代碼:
class Solution {
public:void reverseString(vector<char>& s) {for (int i = 0, j = s.size() - 1; i < s.size()/2; i++, j--) {swap(s[i],s[j]);}}
};