給定一串字串,找出其中第乙個不重複的字元。
如:輸入」abcddcaeb1~soop」,輸出』e』
思路:定義liststore
和liststoredel
,對輸入字串str進行遍歷,對str的每乙個字元,分別在store和storedel中查詢,如果在store中存在該字元,則把store中的該字元刪除,並存入storedel中(如果storedel中已經存在該字元,則不需要再存入),如果store和storedel都沒有該字元,則將該字元放進store中。
即用store存放字串中只出現了一次的字元,而storedel儲存出現了多次的字元。那麼當遍歷完str後,store的第乙個元素就是我們要找的字元,將之返回即可。
**:
#include
#include
#include
#include
using
namespace
std;
char findfirstch(string str)
}else
}char ch = -1;
list
::iterator it = store.begin();
if(it != store.end())
return -1;//不存在,返回-1
}int main()
分析:
這種方法用vector也可以實現,但是注意到遍歷字串會發生push_back和remove的操作,vector是用陣列實現的,在陣列某個位置刪除元素(除了最後乙個元素),後面的元素都要往前移;而進行push_back操作,當vector預分配的大小不足時,會開闢一塊更大的記憶體,將原來記憶體的元素複製到新的記憶體。
而list的底層是利用乙個雙向環形鍊錶實現,對鍊錶的某個元素進行刪除,不會造成後續元素在記憶體中的移動,也不存在預分配大小不足的問題。
空間複雜度:
定義了兩個list,list的大小隨著字串長度的增加也會相應地增加。
時間複雜度:
list是鍊錶,隨機查詢某個元素的時間複雜度為o(n);同理,對list進行remove操作,也要先找到字元所在的位置,因此這個演算法的時間複雜度為o(n^2)。
總結:時間複雜度和空間複雜度都比較大,必須要遍歷完整個字串才能確定符合要求的字元。
思路:遍歷待查詢字串,對每個字元,分別查詢字串中是否還存在相同字元,如果不存在,則說明已經找到我們要查詢的字元,可以返回;如果存在,說明不符合要求,繼續對後面的字元進行相同操作。
**:
#include
#include
#include
#include
using
namespace
std;
char findfirstch(string str)
}return -1;
}int main()
分析:
這個方法,關鍵在於查詢的時候,要把待查詢字串分成兩部分,即進行兩次find操作,如果對整個字串進行查詢,因為字元本身就屬於字串,返回的結果肯定是存在的。而find操作中兩個迭代器引數,正好是半開半閉區間,即查詢範圍是[a,b),b不在查詢範圍內。所以這種方法中對字串的遍歷要使用迭代器,兩次查詢的分界正好就是指向當前字串的迭代器。
空間複雜度:
只需要乙個char記錄返回值,o(1),與字串長度無關。
時間複雜度:
最壞的情況下,要查詢的字元位於字串的最後一位,那麼查詢時間將是o(n^2)。
總結:相比第一種方法,方法二不需要定義額外的資料結構,而且在迴圈中第乙個符合要求的字元就是我們要查詢的元素,可以馬上返回,不需要對剩下的字元進行遍歷,這也比第一種方法有了改進。當然,像上面所說,最壞的情況下,這種方法的時間複雜度還是達到了o(n^2)。
思路:在遍歷待查詢字串的情況下,對list或者string進行find操作,最壞情況下時間複雜度為o(n^2)。
考慮使用map,查到的資料提到,由於map底層用紅黑樹實現,查詢的時間複雜度為o(logn)。因此可以定義map
,遍歷字串時在map查詢當前字元,如果存在,將second置為0,如果不存在將second置為1。那麼當遍歷完字串時,這個map裡second為1的char就代表只出現了一次的字元。構造完map後,再次遍歷字串,利用字元查詢map中對應second值(value值),第乙個遇到value為1的字元就是要查詢的字元。
**:
#include
#include
#include
#include
using
namespace
std;
char findfirstch(string str)
else
}//迴圈執行完畢後,second為1表示對應key在字串中只出現一次,second為0的key表示在字串中出現了多次
for(unsigned
int i = 0; i < str.size(); ++i)
return -1;
}int main()
分析:
這個方法相比前兩種方法,主要是在map中進行find操作,時間複雜度有所改進。
這裡容易犯乙個錯誤,就是在構造完map後,查詢map的第乙個second為1的key,將其作為結果返回。
其實map的訪問順序是不一致的,即當我們按自己的順序把元素存放進map後,重新讀取,map的元素已經不是我們儲存的順序了,而是按照key進行了排序。
比如把key為b和a的元素先後存入map,那麼讀取到map的第乙個資料將會是key為a那個。所以如果查詢map中第乙個second為1的元素,是無法達到效果的。舉個例子:」aabbcfec」,按照題目要求,應該輸出』f』,如果遍歷查詢map中第乙個second為1的元素,那麼就會輸出』e』。所以建立map後,這裡要遍歷的是待查詢字串,用每乙個字元作為key去獲取map中對應的value(即second)的值,第乙個second為1的字元才是正確結果。
空間複雜度:
定義了map
結構,map占用記憶體和字串包含不同字元個數相關,不同字元個數越多,map越大。
時間複雜度:
第一次遍歷必須遍歷完整個字串,而每次進行查詢時間複雜度為o(logn),因此第乙個迴圈時間複雜度為o(nlogn)。第二個迴圈最壞情況下時間複雜度為o(n)。因此整個演算法時間複雜度為o(nlogn+n)。
總結:如果對map的儲存方式不清楚,會誤以為map中元素的訪問資料是按照使用者寫入順序儲存,那麼每次輸出的將會是字串中所有不重複字元裡字典順序最小的字元。
思路:建立雜湊表char hashtable[256],陣列元素初始化為0,雜湊函式
void hashfunc(char ch)
構造完雜湊表後,表中每個元素的值就代表陣列下標對應的字元在字串中出現的次數,再次遍歷字串,以每個字元對應的ascii碼表作為下標訪問雜湊表對應的元素,第一次遇到元素值為1,即表示當前字元就是要查詢的字元。
**:
#include
#include
#include
#include
using
namespace
std;
char findfirstch(string str);
for(unsigned
int i = 0; i < str.size(); ++i)
for(unsigned
int i = 0; i < str.size(); ++i)
return -1;
}int main()
分析:
其實這個所謂的雜湊表就相當於ascii碼表,只是它還多出了乙個功能,記錄每次字元出現的次數。
這裡面會容易進入誤區,就是建立完雜湊表後,從雜湊表開始遍歷,輸出遇到的第乙個為1的元素對應的字元。因為題目要求是找出第乙個不重複的字元,而雜湊表中第乙個為1的元素對應的字元不一定能就滿足條件,只能保證是不重複的字元裡字典順序最小的字元。
這和第三種方法的易犯錯誤其實是乙個道理。
空間複雜度:
陣列char[256]作為雜湊表,256是ascii碼表的大小,與字串長度無關,即空間複雜度為常量。
時間複雜度:
對字串進行兩次遍歷操作,時間複雜度為o(n)。
總結:這種方法其實和第三種方法大同小異,思路基本一致,只是第三種方法構造雜湊表的過程交給map去做而已。當然,第四種方法在時間複雜度上無疑更有優勢。
寫乙個函式,找出給定字串中第乙個重複的字元
ex abcda return a bcbdaa return b abc return nonedef search first repetition str1 if set str1 len str1 利用set去重的功能,通過len 函式,判斷字串中是否含有重複元素 return none 如...
找出字串中第乙個不重複的字元
leetcode原題位址 將字串轉換成字元陣列 使用兩層迴圈遍歷,最終根據第二層迴圈退出時的下標值來判斷是否是不重複的字元 我的 public intfirstuniqchar string s if temp i temp j if j length if i length return resu...
字串 第乙個不重複的字元
有關這個題目可以有很多的考察方式,但是本質的乙個方法就是利用hash表,來降低時間複雜度。先看第乙個題目,給定乙個字串,找到這個字串的第乙個不重複的字元 在乙個字串 1 字串長度 10000,全部由字母組成 中找到第乙個只出現一次的字元的位置。若為空串,返回 1。位置索引從0開始。所以 實現 int...