原文作者:aircraft
原文連結:https://www.cnblogs.com/DOMLX/p/11089327.html
已經刷了很多篇leetcode題了,不過最近在找c++的實習工作(大佬們有推薦的嗎QAQ),現在慢慢補上吧
雖然有點煩,但是還是要提一句,刷leetcode題目的時候,最好還是自己先思考然後寫出一個通過的程式碼,再去看其他人的程式碼參考比較好,當然了,寫不出來就當我沒說。盡力而為即可。
一、反轉字串
class Solution {
public:
void reverseString(vector<char>& s) {
int len = s.size();
int i = 0,j = len - 1;
while(i < j){
s[i] = s[i]^s[j];
s[j] = s[i]^s[j];
s[i] = s[i]^s[j];
i++;
j--;
}
}
};
我的思路就是很簡單的用兩個i,j 一個由前向後移動,另外一個反之。這樣就可以不斷的把頭尾字串交換了,當i<j時就說明已經交換完畢了結束迴圈。
看看大佬們的程式碼:44ms左右
class Solution
{
public:
void reverseString(vector<char>& s)
{
for (int i = 0; i < s.size() / 2; i++)
swap(s[i], s[s.size() - i - 1]);
}
};
好吧,看來跟我的差別不是很大,就是呼叫了庫函式交換,以及省略些判斷過程。
二、整數反轉
給出一個 32 位的有符號整數,你需要將這個整數中每位上的數字進行反轉。
示例 1:
輸入: 123
輸出: 321
示例 2:
輸入: -123
輸出: -321
示例 3:
輸入: 120
輸出: 21
注意:
假設我們的環境只能儲存得下 32 位的有符號整數,則其數值範圍為 [−231, 231 − 1]。請根據這個假設,如果反轉後整數溢位那麼就返回 0。
我的程式碼:8ms左右
class Solution {
public:
int reverse(int x) {
int tem = 0;
if(x > 0){
while(x != 0){
if(tem > (INT_MAX - (x % 10)) / 10 )return 0;
tem = tem * 10 + x % 10;
x /= 10;
}
}
else{
while(x != 0){
if(tem < (INT_MIN - (x % 10)) / 10)return 0;
tem = tem * 10 + x % 10;
x /= 10;
}
}
return tem;
}
};
這道題的思路很簡單,在大一學c語言的時候就遇到過將數字反轉的測試題目,只要對X%10就能將其個位數字保留下來,然後tem = tem * 10 + x % 10; 就相當於將原來的末尾數保留下來,x /= 10; 已經拿到個位數了,那麼就將原來的數除以10,將原來的十位數字移動到個位,這樣組合起來就是每加入一個數就將上一個數值乘10加上後來的個位數字即可,
比如21----%10,就保留1,然後將21/10,並且因為是整型資料所以剩下2,然後2%10,還是2,tem = tem * 10 + x % 10;這樣就是把1乘10+2,就將數字反轉了。
不過這個跟大一的題目比較多了一個環境只能儲存32 位的有符號整數,則其數值範圍為 [−231, 231 − 1]。這其實就是有符號的Int型的儲存範圍,最大可以用內建的INT_MAX 表示,最小則是INT_MIN。
也就是說我們的環境是不予許出現超出這兩個範圍的數字的,那麼我們一開始想的肯定是加一個判斷--比如:tem * 10 + x % 10>INT_MAX就結束迴圈,或者tem * 10 + x % 10<INT_MIN,這個思路是沒有問題的,不過在最後如果因為判斷條件成立要結束這個迴圈的話,tem * 10 + x % 10 有些值必然會超過環境允許的範圍,而報錯,那麼我們就換一個寫法,數學的在大於小於符號左右移動數值的方法大家都學過吧。
我們只要將tem * 10 + x % 10>INT_MAX 變成 tem < (INT_MIN - (x % 10)) / 10 就不會在某一次判斷的時候出現超過環境允許範圍的數值了。
看看大佬們的程式碼:8ms左右
class Solution {
public:
int reverse(int x) {
int sum = 0;
for (; x != 0; x /= 10) {
int yu = x % 10;
if (sum > (INT_MAX - yu) / 10) return 0;
if (sum < (INT_MAX - yu) / 10) return 0;
sum = sum * 10 + yu;
}
return sum;
}
};
這個思路跟我一樣,不過我一開始寫的時候覺得需要對於正數負數需要分開判斷,其實不需要,這樣寫就簡化美觀了很多。
大佬的程式碼:1ms左右
class Solution {
public:
int reverse(int x) {
if(x == 0) return 0;
else
{
int flag = 1;
if(x > 0) flag = 1;
else flag = -1;
long long l = abs((long long)(x));
long long res = 0;
int ans;
while(l > 0)
{
int temp = l % 10;
res = res*10 + temp;
l = l / 10;
}
if(flag == 1) ans = res > INT_MAX ? 0:res;
else ans = res*flag < INT_MIN ? 0:res*flag;
return ans;
}
}
};
這個程式碼就是直接用64位的環境變數來儲存,最後加一個判斷,不過題目好像說只能在32的環境下吧,這個應該是為了刷速度寫的。
三、字串中的第一個唯一字元
給定一個字串,找到它的第一個不重複的字元,並返回它的索引。如果不存在,則返回 -1。
案例:
s = "leetcode"
返回 0.
s = "loveleetcode",
返回 2.
注意事項:您可以假定該字串只包含小寫字母。
我的程式碼:80ms
class Solution {
public:
int firstUniqChar(string s) {
unordered_map<char,int> umap;
int len = s.size();
if(len == 1) return 0;
for(auto s1:s){
umap[s1]++;
}
for(int i = 0; i < len; i++){
if(umap[s[i]] == 1) return i;
}
return -1;
}
};
我的思路就是將每個數字都儲存起來然後計數,計數為1就是那一個字元了,不過呢,這樣簡單的東西我用了需要做很多特殊處理的unorder_map容器就比較花時間了,這個思路的還是用陣列來儲存會快上很多,比如下面的程式碼
20ms左右
class Solution {
public:
int firstUniqChar(string s) {
static const auto io_sync = []{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
return nullptr;
}();
int times[26]={0};
for(int i=0;i<s.size();i++){
times[s[i]-'a']++;
}
for(int i=0;i<s.size();i++){
if(times[s[i]-'a']==1)
return i;
}
return -1;
}
};
還有12ms大佬的程式碼:
class Solution {
public:
int firstUniqChar(string s) {
static const auto io_sync = []{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
return nullptr;
}();
int num = 0, len = s.length();
int a[26] = {0}, pos[26] = {0};
char c[26];
for(int i = 0; i < len; i++){
if(a[s[i] - 'a'] == 0){
c[num++] = s[i];
pos[s[i] - 'a'] = i;
}
a[s[i] - 'a']++;
}
for(int i = 0; i < num; i++){
if(a[c[i] - 'a'] == 1){
return pos[c[i] - 'a'];
}
}
return -1;
}
};
這個就是將前面那個20ms的程式碼優化了一下,我們從string型別中拿資料的話要比從一個char陣列裡拿資料要做的工作會多很多。
四、有效的字母異位詞
給定兩個字串 s 和 t ,編寫一個函式來判斷 t 是否是 s 的字母異位詞。
示例 1:
輸入: s = "anagram", t = "nagaram"
輸出: true
示例 2:
輸入: s = "rat", t = "car"
輸出: false
說明:
你可以假設字串只包含小寫字母。
進階:
如果輸入字串包含 unicode 字元怎麼辦?你能否調整你的解法來應對這種情況?
我的程式碼:16ms左右
class Solution {
public:
bool isAnagram(string s, string t) {
unordered_map<char,int> umap_s,umap_t;
int len_s = s.size();
int len_t = t.size();
if(len_s != len_t) return false;
for(int i = 0; i < len_s; i++){
umap_s[s[i]]++;
umap_t[t[i]]++;
}
for(auto ch:umap_s){
if(umap_s[ch.first] != umap_t[ch.first]) return false;
}
return true;
}
};
就是像unordered_map以及map類的容器真的是非常的好用,在一些區域性程式碼或者是小型專案,對速度的需要沒有確切需求的地方都可以很好的使用。
我的思路就是用兩個容器分別儲存,然後計數,如果雙方某一個字元的計數值不同,那麼肯定不是異位字串了。
但是我們其實用char或者int型別的陣列來操作就可以做到了,並且速度優化很多。
大佬們的程式碼:1ms左右
class Solution {
public:
bool isAnagram(string s, string t) {
//字母異位詞,指的是字母相同,排列不同的單詞
if (s.size() != t.size()) return false;
int m[26] = {0};
for (int i = 0; i < s.size(); ++i) ++m[s[i] - 'a'];
for (int i = 0; i < t.size(); ++i){
if (--m[t[i]-'a'] < 0) return false;
}
return true;
}
};
這個程式碼就簡化了很多,並且提高的速度,直接用一個int型的陣列儲存第一個字串的-‘a'之後的ASCII的值,其他沒出現的都置為0,然後第二個for迴圈就是把第二個字串的數值都拿去跟第一個通過相減的方式比對,因為你有一個,我也有 一個那麼減後就是為0,而如果我有你沒有,那麼減後就會小於0,這樣判斷是有大前提的s.size() != t.size(),兩個的size是相同的。
五、驗證迴文字串
給定一個字串,驗證它是否是迴文串,只考慮字母和數字字元,可以忽略字母的大小寫。
說明:本題中,我們將空字串定義為有效的迴文串。
示例 1:
輸入: "A man, a plan, a canal: Panama"
輸出: true
示例 2:
輸入: "race a car"
輸出: false
我的程式碼:20ms
class Solution {
public:
bool isPalindrome(string s) {
int len = s.size();
if(len == 1) return true;
int i = 0,j = len - 1;
while(i < j){
if(s[i] >= '0' && s[i] <= '9' || s[i] >= 'A' && s[i] <= 'Z' || s[i] >= 'a' && s[i] <= 'z');
else{
i++;
continue;
}
if(s[j] >= '0' && s[j] <= '9' || s[j] >= 'A' && s[j] <= 'Z' || s[j] >= 'a' && s[j] <= 'z');
else{
j--;
continue;
}
if(s[i] >= 'a' || s[j] >= 'a'){
if(abs(s[i] - s[j]) == 0 || abs(s[i] - s[j]) == 32){
i++;
j--;
}
else return false;
}
else if(abs(s[i] - s[j]) == 0){
i++;
j--;
}
else return false;
}
return true;
}
};
我的思路就是忽略掉大小寫的影響,以及不屬於數字或者字元的影響,剩下的用兩個指標來移動對比,一個頭指標,一個尾,如果有一個比對不成立,那麼就不是迴文字串,這個程式碼有很多可以優化的地方,比如判斷是字元或者數字,都可以直接使用庫函式,然後題目說忽略大小寫的影響,那麼幹脆把所有字串都換成大寫或者小寫在來比對好了。。。。反正當時我是沒有想到用庫函式,都是自己寫了。
大佬們的程式碼:1ms左右
class Solution {
public:
bool isPalindrome(string s) {
int lefts=0;
int rights=s.size()-1;
while(lefts<rights)
{
while(!isalnum(s[lefts]))
{
lefts++;
if(lefts>=rights)
return true;
}
while(!isalnum(s[rights]))
{
rights--;
if(lefts>rights)
return false;
}
if(tolower(s[lefts])!=tolower(s[rights]))
return false;
lefts++;
rights--;
}
return true;
}
};
這個程式碼就是一個外迴圈來遍歷,而裡面的兩個while則是用來分別跳過左邊不是數字或者字元的數,然後在將左邊的值與右邊的值都轉為小寫比對,若是不相同那麼就不是迴文字串了。
isalnum()用來判斷字元或者數字,tolower()轉為小寫。
六、字串轉整數
請你來實現一個 atoi
函式,使其能將字串轉換成整數。
首先,該函式會根據需要丟棄無用的開頭空格字元,直到尋找到第一個非空格的字元為止。
當我們尋找到的第一個非空字元為正或者負號時,則將該符號與之後面儘可能多的連續數字組合起來,作為該整數的正負號;假如第一個非空字元是數字,則直接將其與之後連續的數字字元組合起來,形成整數。
該字串除了有效的整數部分之後也可能會存在多餘的字元,這些字元可以被忽略,它們對於函式不應該造成影響。
注意:假如該字串中的第一個非空格字元不是一個有效整數字符、字串為空或字串僅包含空白字元時,則你的函式不需要進行轉換。
在任何情況下,若函式不能進行有效的轉換時,請返回 0。
說明:
假設我們的環境只能儲存 32 位大小的有符號整數,那麼其數值範圍為 [−231, 231 − 1]。如果數值超過這個範圍,qing返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
示例 1:
輸入: "42"
輸出: 42
示例 2:
輸入: " -42"
輸出: -42
解釋: 第一個非空白字元為 '-', 它是一個負號。
我們儘可能將負號與後面所有連續出現的數字組合起來,最後得到 -42 。
示例 3:
輸入: "4193 with words"
輸出: 4193
解釋: 轉換截止於數字 '3' ,因為它的下一個字元不為數字。
示例 4:
輸入: "words and 987"
輸出: 0
解釋: 第一個非空字元是 'w', 但它不是數字或正、負號。
因此無法執行有效的轉換。
示例 5:
輸入: "-91283472332"
輸出: -2147483648
解釋: 數字 "-91283472332" 超過 32 位有符號整數範圍。
因此返回 INT_MIN (−2
31
) 。
我的程式碼:8ms左右
class Solution {
public:
int myAtoi(string str) {
int sign = 0,res = 0,i = -1,len = str.size();
if(len < 1) return 0;
while(i < len && str[++i] == ' ');
if(i >= len) return 0;
if(str[i] == '-'){
sign = -1;
i++;
}
else if(str[i] == '+') i++;
if(sign != -1){
while(str[i] >= '0' && str[i] <= '9'){
if(res > ((INT_MAX - (str[i] - '0')) / 10)) return INT_MAX;
res = res * 10 + (str[i]-'0');
i++;
}
return res;
}
while(str[i] >= '0' && str[i] <= '9'){
if(-res < ((INT_MIN + (str[i] - '0') ) / 10) ) return INT_MIN;
if(res * 10 > (INT_MAX -(str[i] - '0')) ){
return -res * 10 - (str[i]-'0');
}
res = res * 10 + (str[i]-'0');
i++;
}
return -res;
}
};
字串轉數字這個程式碼應該初學c語言的時候大家都學過,而這個題目就是加一個限制,環境只能容納32位有符號整型的值。所以要多加點判斷。
字串轉數字無非就是res * 10 + (str[i]-'0'); 將字元的值-‘0’的值,然後如果剛轉的這個數前面已經有數字存在了,那麼就把前面的那個高位數乘10提高一個位數加上去就行了。
而要加判斷的話,大於0的值比較,一般是這樣的想法:res * 10 + (str[i]-'0');>INT_MAX就不成立,然而環境不允許存在比INT_MAX大的值,那麼我們就移動一下比較符號左右的運算,變成這樣:
res > ((INT_MAX - (str[i] - '0')) / 10);這樣的預判式子就不會突破環境變數的限制了。
小於0的話就這樣改:-res < ((INT_MIN + (str[i] - '0') ) / 10);
大佬們的程式碼:1ms左右
class Solution {
public:
int myAtoi(string str) {
int cur = 0;
int len = str.length();
int flag = 1;
int ret = 0;
while(cur < len && str[cur]==' ')
{
cur ++;
}
if(cur ==len)
return 0;
else if(str[cur]=='-')
flag = -1;
else if(str[cur]=='+')
flag = 1;
else if(isdigit(str[cur]))
ret = str[cur] -'0';
else
return 0;
cur +=1;
while(cur < len && isdigit(str[cur]))
{
int temp = (str[cur] -'0') * flag;
if (ret > INT_MAX / 10 || ret == INT_MAX / 10 && temp > 7)
return INT_MAX;
if (ret < INT_MIN / 10 || ret == INT_MIN / 10 && temp < -8)
return INT_MIN;
ret =ret *10 + temp;
cur ++;
}
return ret;
}
};
這個程式碼就比我的優化很多了,我的分別把正負值分開算,多了很多程式碼和判斷,而這裡直接放在一個迴圈裡,通過flag來確定符號就挺好的。
七、實現 strStr() 函式。
給定一個 haystack 字串和一個 needle 字串,在 haystack 字串中找出 needle 字串出現的第一個位置 (從0開始)。如果不存在,則返回 -1。
示例 1:
輸入: haystack = "hello", needle = "ll"
輸出: 2
示例 2:
輸入: haystack = "aaaaa", needle = "bba"
輸出: -1
說明:
當 needle
是空字串時,我們應當返回什麼值呢?這是一個在面試中很好的問題。
對於本題而言,當 needle
是空字串時我們應當返回 0 。這與C語言的 strstr() 以及 Java的 indexOf() 定義相符。
我的程式碼:4ms
class Solution {
public:
int strStr(string haystack, string needle) {
int ht_len = haystack.size();
int nl_len = needle.size();
int len = ht_len - nl_len;
int i, j = 0;
if(nl_len == 0) return 0;
for(i = 0; i <=len; i++){
if(haystack[i] == needle[j]){
while(j < nl_len && haystack[i + j] == needle[j]) j++;
if(j == nl_len) return i;
j = 0;
}
}
return -1;
}
};
我的思路就是:從第一個字串中找第二個子串出現的位置,那麼只要從第一個串裡面找到第二個子串相匹配的字元,然後記住位置,遍歷一個子串長度的資料對比,都相同的話就代表找到了。
同樣的,假如第二個字串長度為3,第一個為6,那麼第一個字串到第四個位置之後就沒有必要再去比較判斷了,所以真正可以比較長度就是len1 - len2的長度。
大佬們的程式碼:1ms左右
class Solution {
int issame(string haystack, string needle,int k)
{
if(haystack.length()-k<needle.length())
return -1;
for(int i=0;i<needle.length();i++)
{
if(haystack[i+k]!=needle[i])
return issame(haystack,needle,k+1);
}
return k;
}
public:
int strStr(string haystack, string needle) {
if(needle=="")
return 0;
return issame(haystack,needle,0);
}
};
這個大佬用遞迴的方法不斷的遞迴尋找到第二串的第一個字元在第一個串出現的位置,找到後就遍歷比對,若不成立則繼續遞迴併且增加k值,直到k值不滿足haystack.length()-k<needle.length()這個條件就說明不存在,返回-1.利用了空間換時間的方法,增加了速度,而且程式設計之美里面說過,“迭代是人,遞迴為神”,能用遞迴的方法會使得程式碼更加簡潔和快速,不過要注意堆疊的溢位問題就是了。
八、報數
報數序列是一個整數序列,按照其中的整數的順序進行報數,得到下一個數。其前五項如下:
1. 1
2. 11
3. 21
4. 1211
5. 111221
1
被讀作 "one 1"
("一個一"
) , 即 11
。
11
被讀作 "two 1s"
("兩個一"
), 即 21
。
21
被讀作 "one 2"
, "one 1"
("一個二"
, "一個一"
) , 即 1211
。
給定一個正整數 n(1 ≤ n ≤ 30),輸出報數序列的第 n 項。
注意:整數順序將表示為一個字串。
示例 1:
輸入: 1
輸出: "1"
示例 2:
輸入: 4
輸出: "1211"
我的程式碼:8ms左右
class Solution {
public:
string countAndSay(int n) {
vector<string> list;
int len;
int j = 0;
int count = 1;
string temp = "";
string ch = "";
list.emplace_back("1");
for (int i = 1; i < n; i++) {
len = list[i - 1].size();
while (j < len) {
ch = list[i - 1][j];
if (j == (len - 1)) {
temp += "1" + ch;
break;
}
while (j < len - 1 && list[i - 1][j] == list[i - 1][j + 1]) {
count += 1;
j++;
}
temp += to_string(count) + ch;
count = 1;
j++;
}
list.emplace_back(temp);
temp = "";
j = 0;
}
return list[n - 1];
}
};
這題理解題目的意思就比較重要了,一開始我沒理解寫了很多都通過不了,自己也找不到原因。後來是刪除了所有程式碼重新思考了一遍才寫出來。
反正就是下一個數就是把上一個數字按照一定語法來解讀,比如1211後面的111221,因為1211是先一個1 所以為11,然後是一個2 所以為 12,然後兩個1 為21,全部組合起來就是111221,是這麼個意思,所以我們就用上一個不斷的去推出下一個,這就是一個迭代過程。
那我們只需要紀錄下第一個字元,然後計算有幾個一樣的寫上就好,遇到不一樣的在替換紀錄的字元繼續計數,比如1112,三個1=31,替換為2,有一個2=12 組合起來就是3112
看看大佬們的程式碼:1ms左右
class Solution {
public:
string countAndSay(int n) {
string str = "1";
if(n == 1) return str;
for(int i = 2; i <= n; ++i)
{
string tmp = "";
char cur = str[0];
int cnt = 1;
for(int j = 1; j < str.length(); ++j)
{
if(str[j] != cur)
{
tmp.push_back(char(cnt+'0'));
tmp.push_back(cur);
cur = str[j];
cnt = 1;
}
else ++cnt;
}
tmp.push_back(char(cnt+'0'));
tmp.push_back(cur);
str = tmp;
}
return str;
}
};
大佬的思路跟我差不多,不過寫法比我優化很多。
九、最長公共字首
編寫一個函式來查詢字串陣列中的最長公共字首。
如果不存在公共字首,返回空字串 ""
。
示例 1:
輸入: ["flower","flow","flight"]
輸出: "fl"
示例 2:
輸入: ["dog","racecar","car"]
輸出: ""
解釋: 輸入不存在公共字首。
說明:
所有輸入只包含小寫字母 a-z
。
我的程式碼:20ms
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if(strs.empty()) return "";
int len = INT_MAX;
string temp;
for(auto str:strs){
if(str.size() < len) len = str.size();
}
for(int i = 0; i < len; i++){
temp = strs[0].substr(0,i + 1);
for(auto str:strs){
if(str.substr(0,i + 1).find(temp) == str.npos) return strs[0].substr(0,i);
}
}
return strs[0].substr(0,len);
}
};
題目的意思很清楚,也不需要什麼思路就是比較就是了,我的是一次把多個字元切割好去比較,直到最後一次比較不成功,之前的就是最長公共字首,這樣看起來程式碼很清楚,不過效率特別慢,因為中間做了很多很多事。
大佬們的程式碼:1ms左右
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
string res;
int n=strs.size();
if(n==0)
return res;
else if(n==1)
return strs[0];
char c;
for(int i=0;;i++){
c=strs[0][i];
for(int j=1;j<n;j++){
if(c=='\0'||strs[j][i]!=c)
return res;
}
res+=c;
}
return res;
}
};
一個個字元去遍歷比較,都有的話就加到結果裡,最後返回的就是最長公共字首了。
後面還是會陸續更新leetcode演算法篇,也有其他面試教程篇或者網路程式設計篇之類的。想要的話就關注我把!!!!感謝各位。