文章目錄
- 兩數之和
- 面試題01.02.判定是否為字符重排
- 存在重復元素
- 存在重復元素||
- 字母異位詞分組
- 最長公共前綴和
- 最長回文子串
- 二進制求和
- 字符串相乘
兩數之和
這題的思路很簡單,在讀完題目之后,便可以想到暴力枚舉,直接遍歷整個數組兩遍即可,但是時間復雜度高,下面是運行之后的結果
很簡單快速的將這個題目寫完了,但是有沒有更高效,時間復雜度更低的方法呢?
當然!
思路:
- 利用哈希表進行優化已經知道了target,那么遍歷讓x = target-nums[i]如果x在哈希表中存在,那么就存在這組元素——即結果。
class Solution
{
public:vector<int> twoSum(vector<int>& nums, int target) {unordered_map<int, int> hash;for(int i = 0; i < nums.size(); i++){int x = target - nums[i];if(hash.count(x)) return {hash[x], i};hash[nums[i]] = i;}return {};}
};
面試題01.02.判定是否為字符重排
這個題目也是一個很經典的題目,也是一個一眼題
- 方法一:直接將兩個字符串進行排序,相同就是true,不同就是false。時間復雜度也是最優的
class Solution
{
public:bool CheckPermutation(string s1, string s2){sort(s1.begin(),s1.end());sort(s2.begin(),s2.end());if(s1 == s2) return true;else return false;}
};
- 方法二:可以用一個數組替代哈希表,將遍歷一組字符串進入哈希表,然后再遍歷另外一組字符串——從哈希表中依次減少另外一組字符串的個數,最后結果為0便是true,否則剩余數字就是false。
class Solution
{
public:bool CheckPermutation(string s1, string s2) {if(s1.size() != s2.size()) return false;int hash[26] = { 0 };//先統計第一個字符串的信息for(auto ch : s1)hash[ch - 'a']++;//掃描第二個字符串,看看能不能重排for(auto ch : s2){hash[ch - 'a']--;if(hash[ch - 'a'] < 0) return false;} return true;}
};
存在重復元素
- 思路:
將數組一遍遍歷,一邊插入到哈希表當中,如果遍歷到每個元素的時候已經存在該元素,直接返回true即可,如果遍歷完之后沒有元素了返回false。
class Solution
{
public:bool containsDuplicate(vector<int>& nums) {// 創建一個無序集合,用于存儲nums向量中的唯一元素unordered_set <int> hash;// 遍歷nums向量中的每個元素for(auto x : nums){// 如果元素已經在集合中,說明存在重復元素if(hash.count(x)) return true; // 如果發現重復元素,返回trueelse hash.insert(x); // 如果沒有重復元素,將該元素插入集合中}// 如果遍歷完后沒有發現任何重復元素,返回falsereturn false;}
};
存在重復元素||
- 思路: 這題跟上面一題基本一樣只不過增加了一個| i - j | <= k的條件。
class Solution
{
public:bool containsNearbyDuplicate(vector<int>& nums, int k) {// 創建一個哈希表,用于存儲每個元素及其最后出現的位置unordered_map<int, int> hash;// 遍歷nums向量中的每個元素for(int i = 0; i < nums.size(); i++){// 如果當前元素在哈希表中已經存在if(hash.count(nums[i])){// 判斷當前索引與該元素上次出現索引的差是否小于等于kif(i - hash[nums[i]] <= k) return true; // 如果差值小于等于k,返回true,表示找到滿足條件的重復元素}// 更新當前元素在哈希表中的位置hash[nums[i]] = i;}// 如果遍歷完所有元素都沒有找到滿足條件的重復元素,返回falsereturn false;}
};
字母異位詞分組
- 思路:
-
排序法:
對于每個字符串,將其按字母排序。排序后的字符串可以作為它們的“標識符”,即所有字母異位詞排序后得到的字符串是相同的。
例如,“eat”和“tea”排序后都會變成“aet”,這使得我們能夠將它們歸為一組。 -
使用哈希表分組:
使用一個哈希表 unordered_map<string, vector>,其中鍵是排序后的字符串,值是具有相同排序后的字母異位詞的字符串集合。
對于每個字符串,排序并將它放入對應的組中。如果該組已經存在,就將其添加到對應組里;如果該組不存在,就創建新組。 -
提取結果:
最后,從哈希表中提取出所有的字母異位詞組并返回。
class Solution
{
public:vector<vector<string>> groupAnagrams(vector<string>& strs) {// 創建一個哈希表,key是排序后的字符串,value是與該排序字符串對應的字母異位詞列表unordered_map <string, vector<string>> hash;// 1. 將所有字符串排序并分組for(auto& s : strs){// 將字符串復制到tmp中,然后排序string tmp = s;sort(tmp.begin(), tmp.end());// 根據排序后的字符串,將原始字符串加入哈希表的對應組中hash[tmp].push_back(s);}// 2. 從哈希表中提取出結果vector<vector<string>> ret;for(auto&[x, y] : hash){// 將每一組字母異位詞加入到結果列表中ret.push_back(y);}// 返回結果return ret;}
};
最長公共前綴和
- 思路:
這道題的思路是通過逐個字符地檢查所有字符串的字符來找出最長公共前綴。從第一個字符串的第一個字符開始,逐個比較該位置上其他字符串的字符是否相同。如果遇到不同的字符或某個字符串的長度不足,就返回當前找到的公共前綴。如果沒有遇到不匹配的字符,說明第一個字符串本身就是所有字符串的公共前綴,直接返回它——中心擴展算法
class Solution
{
public:string longestCommonPrefix(vector<string>& strs) {// 遍歷第一個字符串的每一個字符for(int i = 0; i < strs[0].size(); i++){// 獲取當前字符char tmp = strs[0][i];// 遍歷后續的字符串,檢查是否在當前字符位置上與第一個字符串相同for(int j = 1; j < strs.size(); j++){// 如果某個字符串的長度小于等于i,或者字符不匹配,返回從0到i的子串作為公共前綴if(i == strs[j].size() || tmp != strs[j][i])return strs[0].substr(0, i);}}// 如果沒有遇到不匹配的情況,說明第一個字符串就是公共前綴return strs[0];}
};
最長回文子串
- 思路:
這道題要求找到一個字符串中最長的回文子串。回文串是指從前往后和從后往前讀都相同的字符串。我們可以通過 中心擴展法 來解決這個問題。每個回文子串都有一個中心,中心可以是一個字符(對于奇數長度回文串)或者兩個字符之間(對于偶數長度回文串)。從每個字符(或字符間隙)向外擴展,檢查左右兩邊的字符是否相等,直到不再相等為止。
class Solution
{
public:string longestPalindrome(string s) {// 獲取字符串長度nint n = s.size(), begin = 0, len = 0;// 遍歷每個字符,嘗試找到以該字符為中心的回文串for(int i = 0; i < n; i++){// 處理奇數長度回文串,以字符s[i]為中心int right = i, left = i;// 擴展左右兩邊,直到不滿足回文條件while(left >= 0 && right < n && s[left] == s[right]){left--;right++;}// 更新最長回文串的起始位置和長度if(right - left - 1 > len){begin = left + 1;len = right - left - 1;}// 處理偶數長度回文串,以字符s[i]和s[i+1]為中心right = i + 1, left = i;while(left >= 0 && right < n && s[left] == s[right]){left--;right++;}// 更新最長回文串的起始位置和長度if(right - left - 1 > len){begin = left + 1;len = right - left - 1;}}// 返回最長回文子串return s.substr(begin, len);}
};
二進制求和
- 思路:
我們從 a 和 b 的末尾(即最低位)開始逐位進行加法運算,類似于手動加法的過程。每一位相加時,判斷是否需要進位。進位的處理通過變量 t 來存儲,當兩位數字加起來大于或等于2時,t 就會向下一位傳遞進位。最終,逐位的結果被累積到字符串 ret 中,并且需要反轉,因為我們從低位到高位計算。整個過程確保處理了兩者的不同長度以及最終的進位。
class Solution
{
public:string addBinary(string a, string b) {string ret; // 用于存儲結果的字符串int cur1 = a.size() - 1, cur2 = b.size() - 1, t = 0; // cur1, cur2分別是a和b的當前字符索引,t是進位// 遍歷兩個字符串直到都遍歷完并且沒有進位while(cur1 >= 0 || cur2 >= 0 || t){// 如果a還有剩余位,取出a對應位置的數字并加到t中if(cur1 >= 0) t += a[cur1--] - '0'; // 如果b還有剩余位,取出b對應位置的數字并加到t中if(cur2 >= 0) t += b[cur2--] - '0'; // 將當前位的結果加到結果字符串中,t % 2是當前位(0或1),進位需要除以2ret += t % 2 + '0';t /= 2; // 更新進位,t // 2}// 由于我們是從低位到高位處理的,最后需要反轉結果字符串reverse(ret.begin(), ret.end());return ret; // 返回加法結果的二進制字符串}
};
字符串相乘
- 思路:
這道題目要求實現兩個大整數的乘法。我們不能直接將兩個大整數轉換為整數進行計算,因為它們的長度可能超出整數類型的表示范圍。我們通過模擬豎式乘法的方法來手動計算乘積。首先,我們逆序遍歷兩個字符串,將每一位的數字相乘并加到臨時結果數組中,處理乘法的每一位。然后,我們處理進位,最后將結果反轉并處理前導零,得到最終的乘積結果。
class Solution
{
public:string multiply(string num1, string num2){// 獲取兩個字符串的長度int m = num1.size(), n = num2.size();// 反轉字符串,以便從低位開始計算reverse(num1.begin(), num1.end());reverse(num2.begin(), num2.end());// 臨時數組用于存儲每一位的乘積結果vector<int> tmp(m + n - 1);// 進行無進位的相乘for(int i = 0; i < m; i++)for(int j = 0; j < n; j++)tmp[i + j] += (num1[i] - '0') * (num2[j] - '0');// 處理進位int cur = 0, t = 0;string ret;while(cur < m + n - 1 || t != 0){// 將當前的乘積加到結果中if(cur < m + n - 1) t += tmp[cur++];// 取當前位的數字,并更新進位ret += t % 10 + '0';t /= 10;}// 處理前導零while(ret.size() > 1 && ret.back() == '0') ret.pop_back();// 反轉最終的結果字符串reverse(ret.begin(), ret.end());return ret; // 返回最終的乘積結果}
};