-
删除有序數組中的重複項
題目描述:給你一個 升序排列 的數組 nums ,請你 原地 删除重複出現的元素,使每個元素 隻出現一次 ,傳回删除後數組的新長度。元素的 相對順序 應該保持 一緻 。
由于在某些語言中不能改變數組的長度,是以必須将結果放在數組nums的第一部分。更規範地說,如果在删除重複項之後有 k 個元素,那麼 nums 的前 k 個元素應該儲存最終結果。
将最終結果插入 nums 的前 k 個位置後傳回 k 。
不要使用額外的空間,你必須在 原地 修改輸入數組 并在使用 O(1) 額外空間的條件下完成。
解題思路:開始記錄數組開始的第一個元素值,使用一個變量i不停去周遊數組,直到目前周遊的值與記錄的值不一樣時,則把該元素值插入到頭部的頭部,插入的位置則是由另一個變量j來記錄确定。最後j的值便是數組中不重複元素的長度,傳回j+1即可完成題目要求。(因為j是從0開始的,是以傳回的值應該是j+1)
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int i=1,j=0;
int temp=nums[0];
while(i<nums.size())
{
if(nums[i]!=temp)
{
nums[++j]=nums[i];
temp=nums[i];
}
i++;
}
return j+1;
}
};
該方法的時間複雜度為O(n),空間複雜度為O(1),實作了題目要就的“就地”。
思路一的代碼改進
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int n=nums.size();
int j=0;
for(int i=0;i<n;i++)
{
if(nums[i]!=nums[j])
{
nums[++j]=nums[i];
}
}
return j+1;
}
官方提供的代碼
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int n=nums.size();
int j=0;
for(int i=0;i<n;i++)
{
if(i==0||nums[i]!=nums[i-1])
{
nums[j]=nums[i];
j++;
}
}
return j;
}
};
-
兩數之和
題目描述:給定一個整數數組 nums 和一個整數目标值 target,請你在該數組中找出 和為目标值 target 的那 兩個 整數,并傳回它們的數組下标。
你可以假設每種輸入隻會對應一個答案。但是,數組中同一個元素在答案裡不能重複出現。
你可以按任意順序傳回答案。
解題思路一:該思路比較直接,也比較笨拙。沒周遊一個元素時,便記錄該值與目标值的差,接下來便在該值後面的所有元素中尋找是否存在該元素(為什麼隻需要周遊該元素的後面元素,是因為當周遊到該元素時,已經說明該元素與前面的元素相加的值不等于目标值),若存在,傳回兩個元素的下标。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> find;
for(int i=0;i<nums.size();i++)
{
int temp=target-nums[i];
for(int j=i+1;j<nums.size();j++)
{
if(nums[j]==temp)
{ return{i,j};
}
}
}
return {};
}
};
官方解法:建構一個映射表來存放資料元素以及索引值,其中key是數組中的元素值,value是數組中的元素索引。接下來周遊整個數組,将每個數組元素與目标值求差,将內插補點拿到映射表中去查找,如果存在該key值,則直接傳回目前元素的位置以及該key值對應的value值,否則将該元素當組key值,元素在數組中的位置當做value插入映射表中。如果不存在滿足要求的元素,則傳回空值。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
map<int,int>IntHash;
for(int i=0;i<nums.size();i++)
{
int temp=target-nums[i];
if(IntHash.find(temp)!=IntHash.end())
return {IntHash[temp],i};
else IntHash[nums[i]]=i;
}
return {};
}
};
15.三數之和
題目描述:給你一個整數數組 nums ,判斷是否存在三元組 [nums[i], nums[j], nums[k]] 滿足 i != j、i != k 且 j != k ,同時還滿足 nums[i] + nums[j] + nums[k] == 0 。請
你傳回所有和為 0 且不重複的三元組。
注意:答案中不可以包含重複的三元組。
思路:數組裡面有兩類障礙,一類是數組重複,若不消除重複元素很可能導緻相同結果重複輸出;另一類是元素無序,如果有序的話,那麼可以根據目前三值之和來判斷正确的元素尋找方向。是以,在開始的時候首先對輸入數組進行排序,然後開始周遊排序後的數組,如果目前元素值和上一個元素值相等則直接跳過(導緻重複輸出),接下來去目前元素的右方去找符合要求的元素(為什麼是右方尼?其實跟第二天的題解中原理相同,如果再去查找左方元素會導緻進行重複的過程,毫無意義。)當右方邊界上的兩個元素的值與目前周遊的值得元素和等于0,則這三個元素正式我們要找的正确輸出資訊,此時需要注意,目前正在周遊的元素不止與這一對元素的和為0,再往裡面找很可能還有滿足要求的元素,是以不能停止周遊,但同時我們還是需要去消除重複元素的幹擾,繼續向裡面進行周遊,如果三值元素和大于0,則說明右邊的元素大了,該縮小右域的邊界,同理,若小于0,則應縮小左域的邊界,最後将滿足要求的所有元素輸出。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(),nums.end());
if(nums[0]>0) return result;
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;
while(left<right)
{
int sum=nums[i]+nums[left]+nums[right];
if(sum==0)
{
result.push_back({nums[i],nums[left],nums[right]});
while(left<right&&nums[right]==nums[right-1]) right--;
while(left<right&&nums[left]==nums[left+1]) left++;
right--;
left++;
}
else if(sum>0) right--;
else left++;
}
}
return result;
}
};
18.四數之和
題目描述:給你一個由 n 個整數組成的數組 nums ,和一個目标值 target 。請你找出并傳回滿足下述全部條件且不重複的四元組 [nums[a], nums[b], nums[c], nums[d]] (若兩個四元組元素一一對應,則認為兩個四元組重複):
思路:整體思路與三數之和相同,隻不過三數之和中我們固定一個值i,用雙指針left和right去找其他兩個滿足條件的數,而在四數之和中罵我們可以固定兩個值i和j,然後再用雙指針法去找其他兩個值,使得四值加起來的值等于target。思路很簡單,但值得注意的是,在三指針中,因為提前給數組排過序,我們判斷如何數組最小值大于target時,該輸入值便沒有滿足題目要求的項,那是因為三值之和中target為0,如果數組第一個值比0大,那麼後面的所有值也都比0大,比0大的數再加任何一個比0大的數都不可能變小,而在本題中target可真可負,如果僅用數組最小值大于target來減枝的話,在一個例子中,target=-5,數組為[-4,-1,0,0],此時會發生錯誤。是以在判斷數組最小值大于target時,需得target大于0才行。具體的實作代碼如下所示,更細節的減枝和去重都标注在代碼中。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(),nums.end());
if (nums[0]>0&&nums[0]>target) return result;
for(int i=0;i<nums.size();i++)
{ if(i!=0&&nums[i]==nums[i-1]) continue;//去重,與三值和原理相同
for(int j=i+1;j<nums.size();j++)
{
if(nums[i]+nums[j]>target&&target>0) break;
//target為正時,兩個值之和都大于target,且nums[j]一定是大于0的,所有
//後續的數也一定是大于0的,是以肯定找不到符合題意的集。
if(j>i+1&&nums[j]==nums[j-1]) continue;
long temp=(long)target-nums[i]-nums[j];
int l=j+1;
int r=nums.size()-1;
while(l<r)
{
if(nums[l]+nums[r]>temp) r--;
else if(nums[l]+nums[r]<temp) l++;
else
{
result.push_back( {nums[i],nums[j],nums[l],nums[r]});
while(l<r&&nums[r-1]==nums[r]) r--;
while(l<r&&nums[l+1]==nums[l]) l++;
r--;
l++;
}
}
}
}
return result;
}
};
242.有效的字母異位詞
給定兩個字元串 s 和 t ,編寫一個函數來判斷 t 是否是 s 的字母異位詞。
注意:若 s 和 t 中每個字元出現的次數都相同,則稱 s 和 t 互為字母異位詞
思路:題目的要求就是統計兩個字元串中的字元數,判斷s有的t也要有,且有的個數相同,且所有的字元都是小寫的,那麼久好說了,我們隻需要設定一個大小為26的數組統計s字元每一位出現的次數,再次統計t時,将字元出現的次數相抵消,如果最後數組中還存在非0的項,那麼就說明不符合題意。
class Solution {
public:
bool isAnagram(string s, string t) {
int record[26]={0};
for(int i=0;i<s.size();i++)
{
record[s[i]-'a']++;
}
for(int j=0;j<t.size();j++ )
{
record[t[j]-'a']--;
}
for(int k=0;k<26;k++)
{
if(record[k]!=0) return false;
}
return true;
}
};
注釋:當題目輸入大小受限時,可以用數組替代哈希表。
349.兩個數組的交集
給定兩個數組 nums1 和 nums2 ,傳回 它們的交集 。輸出結果中的每個元素一定是 唯一 的。我們可以 不考慮輸出結果的順序 。
思路:求兩個數組的交集,并要去重,去重在一個無序的數組中很麻煩,且輸出不需要有序,是以我們用到了unordered_set,它對元素不會進行排序,節省了很多時間。
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result;
unordered_set<int> nums1_set(nums1.begin(),nums1.end());
for(int i=0;i<nums2.size();i++)
{
if(nums1_set.find(nums2[i])!=nums1_set.end())
result.insert(nums2[i]);
}
return vector<int>(result.begin(),result.end());
}
};
454四數相加 II
給你四個整數數組 nums1、nums2、nums3 和 nums4 ,數組長度都是 n ,請你計算有多少個元組 (i, j, k, l) 能滿足:
思路:題目的大意是四個長度都為n的的數組,能否在每個數組中找到一個元素,緻使四個元素加起來的和等于0,若存在,有多少個元素組滿足這個要求。也就是說,題目要求輸出個數,且不需要去重(元素相同但下标不同依然是兩個不同的元組)。既然隻要求傳回個數,那我們就可以把前兩個數組的每組元素和放入map中(由于不用去重,是以map的value部分存儲的是和為key值元組的個數),接下來,我們去便利另外兩個數組,把他們的相反值拿到map中去查找,若找到,則統計變量則加上以該值為key的value值。
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
int count=0;
map<int,int> IntMap;
for(int i=0;i<nums1.size();i++)
{
for(int j=0;j<nums2.size();j++)
{
IntMap[nums1[i]+nums2[j]]++;//注意不用去重
}
}
for(int i=0;i<nums3.size();i++)
{
for(int j=0;j<nums4.size();j++)
{
if(IntMap.find(0-(nums3[i]+nums4[j]))!=IntMap.end())
count+=IntMap[0-(nums3[i]+nums4[j])];//注意不用去重
}
}
return count;
}
};
202.快樂數
編寫一個算法來判斷一個數 n 是不是快樂數。
「快樂數」 定義為:
對于一個正整數,每一次将該數替換為它每個位置上的數字的平方和。
然後重複這個過程直到這個數變為 1,也可能是 無限循環 但始終變不到 1。
如果這個過程 結果為 1,那麼這個數就是快樂數。
如果 n 是 快樂數 就傳回 true ;不是,則傳回 false 。
思路:本題的結題關鍵正如題目所說,當一個數的各位的平方相加和出現循環時,就肯定不是快樂數
class Solution {
public:
bool isHappy(int n) {
unordered_set<int> Iset;
while(1)
{ int sum=0;
while(n)
{
sum+=pow(n%10,2);
n /=10;
}
if(sum==1) return true;
if(Iset.find(sum)!=Iset.end())
return false;
else Iset.insert(sum);
n=sum;
}
}
};
383 贖金信
給你兩個字元串:ransomNote 和 magazine ,判斷 ransomNote 能不能由 magazine 裡面的字元構成。
如果可以,傳回 true ;否則傳回 false 。
magazine 中的每個字元隻能在 ransomNote 中使用一次。
思路
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int record[26]={0};
for(int i=0;i<magazine.size();i++)
{
record[magazine[i]-'a']++;
}
for(int i=0;i<ransomNote.size();i++)
{
record[ransomNote[i]-'a']--;
}
for(int i=0;i<26;i++)
{
if(record[i]<0)
return false;
}
return true;
}
};
總結:二數之和和二值相加隻需要将前面周遊過的值儲存下來,後面周遊新值時隻需要去找儲存的元素裡有沒有對應內插補點的。而三值相加和四值相加屬于同一類問題,三值之和和四值之和要注意去重和減枝問題。在三值相加中,由于數組是經過排序的,是以在最外層的元素遇到與前值相等時應跳過避免去重,同理,當找到第二個和第三個值時,也需要進行去重;四值相加時,尤其注意找第一個值和第二個值時(第一層和第二層循環)的去重和減枝條件。