Leetcode遇上C++(一)
前言:
為了鍛鍊自己的程式設計能力,決定嘗試著刷下Leetcode題庫,也為自己畢業時找軟體開發崗位打下基礎,當然咯,作為非科班出身的我,在程式設計上面還有很大的問題,甚至連最基本的知識都未掌握好,但是,我相信功夫不負有心人。通過動手程式設計來學習是我感覺最舒服的一種學習方式,因此,特此記錄,以備將來複習。
一、Two Sum
題目如下:
C++解答(二分查詢法):
class Solution { public: vector<int> twoSum(vector<int> &numbers, int target) { vector<int> num = numbers; // (1)所有的容器都是類模板,要定義某種特殊的容器,必須在容器後加一對尖括號, // 尖括號裡面提供容器中存放的元素的型別。 // 所有的容器型別都定義了預設建構函式,用於建立指定型別的空容器物件,注意,預設建構函式不帶引數。 // (2)容器的賦值操作:例如c1 = c2, 表示刪除容器c1的所有元素,然後將c2的元素賦值給c1。 // c1和c2的型別(包括容器型別和元素型別)必須相同。 std::sort(num.begin(), num.end()); // std::sort()是一個快速排序函式,其函式宣告如下: // void sort( iterator start, iterator end ); // 其中,num.begin()返回一個迭代器,它指向容器num的第一個元素, // num.end()返回一個迭代器,它指向容器num的最後一個元素的下一位置。 int length = (int)numbers.size(); // size操作返回容器內元素的個數,例如,c.size()返回容器c中的元素個數,返回型別為c::size_type。 // size_type 相當於 unsigned int型別, 使用size_type 主要是為了適應不同的平臺, int 型別大小會根據不同平臺而不同 vector<int>::iterator prior = num.begin(); vector<int>::iterator behind = --num.end(); // vector<int>::iterator是定義向量迭代器,下面介紹一些應用於容器迭代器的關係操作符: // 例如:>,>=,<,<=, 即當一個迭代器指向的元素在容器中位於另一個迭代器指向的元素之前,則前一個迭代器小於後一個迭代器。 // 注意: 關係操作符的兩個迭代器必須指向同一個容器中的元素或者超出容器末端的下一位置。 int sum = 0; vector<int> index; while( prior < behind) { sum = *prior + *behind ; // vector迭代器的解引用運算,使用解引用操作符(*操作符)來訪問迭代器所指向的元素,解引用操作符返回迭代器當前所指向的元素的引用 if(sum == target) { for(int i = 0; i < length; ++i) { if(numbers[i] == *prior) { index.push_back(i); // push_back是在容器中新增元素的一種方法,所有的順序容器都支援push_back操作。 // 例如,c.push_back(t),在容器c的尾部新增值為t的元素,返回void型別。 } else if(numbers[i] == *behind) { index.push_back(i); } if(index.size() == 2) { break; } } break; } else if(sum > target) { --behind; } else { ++prior; } } return index; } };
提交結果:
二、Add Two Numbers
題目如下:
C++解答:
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { ListNode header(-1); ListNode* pa = l1; ListNode* pb = l2; ListNode *latter_ListNode = &header; int temp_value =0; int carry_bit =0; for ( ; pa !=NULL || pb !=NULL; ) { if (pa !=NULL && pb !=NULL) { temp_value = (pa->val + pb->val + carry_bit) % 10 ; carry_bit = (pa->val + pb->val + carry_bit) / 10 ; latter_ListNode -> next = new ListNode(temp_value); pa = pa->next; pb = pb->next; } else if (pa ==NULL && pb !=NULL) { temp_value = (0 + pb->val + carry_bit) % 10 ; carry_bit = (0 + pb->val + carry_bit) / 10 ; latter_ListNode -> next = new ListNode(temp_value); pb = pb->next; } else if (pa !=NULL && pb ==NULL) { temp_value = (pa->val + 0 + carry_bit) % 10 ; carry_bit = (pa->val + 0 + carry_bit) / 10 ; latter_ListNode -> next = new ListNode(temp_value); pa = pa->next; } latter_ListNode = latter_ListNode -> next; } if (carry_bit > 0) { latter_ListNode -> next = new ListNode(carry_bit); } return header.next; } };
提交結果:
三、Longest Substring Without Repeating Characters
題目如下:
解題思路如下:
C++ 解答:
class Solution { public: int lengthOfLongestSubstring(string s) { map<char, int> last_appear_index; // map是鍵-值對的集合,map型別通常可以理解為關聯陣列:可使用鍵作為下標來獲取一個值。 // 在定義map物件時必須分別指明鍵和值的型別,上述語句表示定義了名為 last_appear_index // 的map物件,由string型別的鍵索引,關聯的值則為int型。 // string型別的鍵為s中的元素,int型的值為最新出現的位置索引。 int substring_start = 0, longest = 0; map<char, int>::iterator map_it; for (int i = 0; i < s.length(); i++) { map_it = last_appear_index.find(s[i]); // map容器提供了find操作,用於檢查某個鍵是否存在而不會插入該鍵, // 例如,m.find(k)表示:如果m容器中存在按K索引的元素,則返回指向該元素的迭代器; // 如果不存在,則返回超出末端迭代器。 // 如果希望當具有指定鍵的元素存在時,就獲取該元素的引用,否則就不在容器中建立新的元素,那麼應該使用find。 if (map_it != last_appear_index.end() && map_it->second >= substring_start) { substring_start = map_it->second + 1; // map迭代器返回 value_type型別(儲存元素的鍵以及值的pair型別)的值————包含const key_type和mapped_type 型別成員的pair物件, // 對於pair類,可以直接訪問其資料成員,其資料成員都是公有的,分別命名為first和second。 // 因此,對map迭代器進行解引用獲得pair物件中,它的first成員存放鍵,為const,而second成員則存放值。 // 因此 substring_start 指向第一個重複元素的下一元素。 } last_appear_index[s[i]] = i; // 將重複元素的位置索引更新為最新出現的位置索引 longest = max(longest, i-substring_start+1); // 更新不重複子字串的長度 } return longest; } };
提交結果:
四、Longest Palindromic Substring
題目如下:
解法一:
首先說下什麼是迴文串,就是正讀反讀都一樣的字串,比如 "bob", "level", "noon" 等等。那麼最長迴文子串就是在一個字串中的那個最長的迴文子串。我們知道傳統的驗證迴文串的方法就是兩個兩個的對稱驗證是否相等,那麼對於找回文字串的問題,就要以每一個字元為中心,像兩邊擴散來尋找回文串,這個演算法的時間複雜度是O(n*n),就是要注意奇偶情況,由於迴文串的長度可奇可偶,比如"bob"是奇數形式的迴文,"noon"就是偶數形式的迴文,兩種形式的迴文都要搜尋。
C++解答:
class Solution {
public:
string longestPalindrome(string s)
{
if (s.length() <= 1)
return s;
// 當字串只有一個元素的時候,無論如何都是迴文字串。
int start = 0;
int max_len = 0;
// 最長迴文子串的起點 ,長度
int prior_index =0;
int next_index = 0;
for (int i=0; i <s.length()-1; ++i)
{
if (s[i] == s[i+1])
// 相鄰兩個元素相同,說明它們有偶數形式的迴文的可能。
{
prior_index = i;
next_index = i+1;
searchPalindrome(s, prior_index, next_index, max_len, start);
}
// 再檢測該字元有奇數形式的迴文的可能。
prior_index = i;
next_index = i;
searchPalindrome(s, prior_index, next_index, max_len, start);
}
return s.substr(start , max_len);
// substr函式,返回當前string物件的子串,例如 s.substr(pos,n)
// 返回一個string型別的字串,它包含s中從下標pos開始的n個字元。
}
void searchPalindrome(string s, int prior_index, int next_index, int &max_len, int &start)
{
int step = 0;
while( prior_index-step-1 >=0 && next_index+step+1 < s.length())
// 當往左右兩邊擴張而不越界時。
{
if(s[prior_index - step - 1] != s[next_index + step + 1] )
break;
++step;
}
int width = next_index - prior_index + 1 + 2*step;
if (max_len < width)
{
max_len = width;
start = prior_index - step;
}
// 記錄此次檢測的迴文的長度,然後與先前的歷史最長迴文的長度相比較,
// 如果相等我們就不管了,我們只記錄最先出現的等長迴文字串;
// 如果小於,就更新迴文長度以及最長迴文字串的起點下標。
}
};
提交結果:
解法二:
此題還可以用動態規劃(Dynamic Programming)來解,首先我們建立一個二維陣列dp,其中dp[j] [i]表示字串區間[j, i]是否為迴文串,當j = i時,只有一個字元,肯定是迴文串,如果 i= j + 1,說明是相鄰字元,此時需要判斷s[i]是否等於s[j],如果 i 和 j 不相鄰,即 i - j >= 2時,除了判斷s[i] 和 s[j] 相等之外,還要判斷 dp[j + 1] [i - 1],若為真,就是迴文串。
C++ 解答:
class Solution {
public:
string longestPalindrome(string s)
{
if(s.size() <= 1)
return s;
int dp[s.size()][s.size()] = {0};
int max_len = 1, start = 0; // 最長迴文子串的長度,起點
for (int i = 0; i < s.size(); ++i)
{
for (int j = 0; j < i; ++j)
{
dp[j][i] = (s[i] == s[j] && (i - j < 2 || dp[j + 1][i - 1]));
if (dp[j][i] && max_len < i - j + 1)
{
max_len = i - j + 1;
start = j;
}
}
dp[i][i] = 1;
}
return s.substr(start, max_len);
}
};
提交結果:
五、Reverse Integer
題目如下:
C++解答:
class Solution {
public:
int reverse(int x)
{
int res = 0;
int temp = 0;
while(x !=0 )
{
temp = res * 10 + x % 10 ;
if ( temp / 10 != res)
return 0;
// 這個if 語句用來檢查是否發生溢位,
// 如果發生溢位,返回 0 結束迴圈。
res = temp;
x = x / 10;
}
return res;
}
};
// 這個地方為什麼不分開考慮正數和負數的情況呢?這是因為正負號並不影響計算。
// 首先我們來回顧下,C++中的取整和取模運算:
// (1)除法的取整分為三類:向上取整、向下取整、向零取整,而C/C++採用向零取整方式。
// 所謂向零取整,指向0方向取最接近精確值的整數,換言之就是捨去小數部分,因此又稱截斷取整。
// 在這種取整方式下,7/4=1,7/(-4)=-1,6/3=2,6/(-3)=-2。
// (2)負數取模呢? 對C/C++而言,解決負數取模問題的關鍵是公式:餘數=被除數-商×除數。
// 因此由C/C++向零取整的整除方式可知,7%(-4)=3,(-7)%4=-3,(-7)%(-4)=-3。
// 理解了上面的知識以後,我們來演示一個簡單的例子,將負數 -256進行反轉:
// 第一次迴圈: res = 0 * 10 + (-256) % 10 = 0 + (-6)= -6, x = (-256) / 10 = -25;
// 第二次迴圈: res = -6 * 10 + (-25) % 10 = -60 + (-5)= -65, x = (-25) / 10 = -2;
// 第三次迴圈: res = -65 * 10 + (-2) % 10 = -650 + (-2)= -652, x = (-2) / 10 = 0;
// 退出迴圈,返回結果 -652, 顯然負號並不影響計算過程。
提交結果:
六、 Palindrome Number
題目如下:
解法一(反轉全部數字):
C++解答:
class Solution {
public:
bool isPalindrome(int x)
{
if ( x < 0 || (x != 0 && x % 10 ==0))
return false;
// 顯然,整數的最低位也不能為0,例如 10 就不是迴文數,
// 因此除數字 0 以外,所以如果發現某個正數的末尾是 0 了,就可以直接排除了。
// 此外,負數也一定不是迴文數。
int res = 0;
int x_copy = x;
while ( x!=0 )
{
if (res > INT_MAX/10 || (res == INT_MAX/10 && x % 10 >7) )
return false;
// 如果是迴文數,反轉後仍是原數字,就不可能溢位,只要溢位一定不是迴文數
res = res * 10 + x % 10;
x = x / 10 ;
}
return x_copy == res;
}
};
提交結果:
解法二(反轉一半數字):
具體做法是,每次通過對10取餘,取出最低位的數字,然後加到取出數的末尾,就是將 revertNum 乘以10,再加上這個餘數,這樣我們的翻轉也就同時完成了,每取一個最低位數字,x都要自除以10。這樣當 revertNum大於等於x的時候迴圈停止。由於迴文數的位數可奇可偶,如果是偶數的話,那麼revertNum 就應該和x相等了;如果是奇數的話,那麼最中間的數字就在revertNum 的最低位上了,我們除以10以後應該和x是相等的。
C++ 解答:
class Solution {
public:
bool isPalindrome(int x)
{
if (x < 0 || (x % 10 == 0 && x != 0))
return false;
// 顯然,當 x < 0 時,x 不是迴文數。
// 同樣地,除 0 以外,如果數字的最後一位是 0,則可以直接排除
int revertNum = 0;
while (x > revertNum)
{
revertNum = revertNum * 10 + x % 10;
x /= 10;
}
return x == revertNum || x == revertNum / 10;
// 當奇迴文數時,我們可以通過 revertedNumber/10 去除處於中位的數字。
// 例如,當輸入為 12321 時,在 while 迴圈的末尾我們可以得到 x = 12,revertedNumber = 123,
// 由於處於中位的數字不影響迴文(它總是與自己相等),所以我們可以簡單地將其去除。
}
};
提交結果:
七、Remove Duplicates from Sorted Array(26)
題目如下:
C++解題:
class Solution {
public:
int removeDuplicates(vector<int>& nums)
{
// 考慮空向量的情況
if (nums.size() == 0)
return 0;
int i = 0;
for (int j=1; j < nums.size();j++)
{
if (nums[j] != nums[i])
{
i++;
nums[i] = nums[j];
}
}
return i+1;
}
};
提交結果:
八、Power of Two (231)
題目如下:
解題思路:
通過對1,2,8,16這四個數字分析,發現這些數字的二進位制形式分別為:1,10,100,1000。從二進位制的表示可以看出,如果一個數是2的n次方,那麼這個數對應的二進位制表示中只有一位是1,其餘位都為0,因此,判斷一個數是否為2的n次方可以轉換為這個數對應的二進位制表示中是否只有一位為1。如果一個數的二進位制表示只有一個位為1,例如num=00010000,那麼num-1的二進位制表示為:num-1=00001111,由於num與num-1二進位制表示中每一位都不相同,因此num&(num-1)的運算結果為0,可以利用這個方法來判斷一個數是否為2的n次方。
C++解答:
class Solution {
public:
bool isPowerOfTwo(int n)
{
if (n < 1)
return false;
if ( n != 1 && n % 2 !=0)
return false;
return int(n & (n-1))== 0;
}
};
提交結果:
九、 830. Positions of Large Groups
題目如下:
解法(一):
class Solution{
public:
vector<vector<int>> largeGroupPositions(string S)
{
int left = 0;
vector<vector<int>> result;
for (int right=1; right<S.length();++right)
{
if (S[right] !=S[left])
left= right ;
else
{
if ( (right < S.length()-1 && S[right+1] != S[right] && (right-left)>=2) || (right ==S.length()-1 && (right-left)>=2) )
{
result.push_back({left,right});
left = right ;
}
}
}
return result;
}
};
提交結果:
解法(二):
class Solution{
public:
vector<vector<int>> largeGroupPositions(string S)
{
vector<vector<int>> ret;
for(int i=0;i<S.size();)
{
int temp=S[i],j=i+1;
while(S[j]==temp && j<S.size())
j++;
if(j-i>=3)
ret.push_back({i,j-1});
// 這種用法相當於:
// vector<int> temp;
// temp.push_back(i);
// temp.push_back(j);
// ret.push_back(temp);
// 需要掌握。
i=j;
}
return ret;
}
};
提交結果:
十、 Remove Element
題目如下:
解題如下:
class Solution {
public:
int removeElement(vector<int>& nums, int val)
{
int slow_ptr =0;
for (int fast_ptr = 0; fast_ptr < nums.size();fast_ptr++ )
{
if(nums[fast_ptr] != val)
{
nums[slow_ptr] = nums[fast_ptr];
slow_ptr ++;
}
}
return slow_ptr;
}
};
提交結果:
十一、Remove Duplicates from Sorted List
題目如下:
解答如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {} //預設建構函式
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head)
{
if (!head || !head->next)
return head;
ListNode* low_ptr = head;
ListNode* fast_ptr = head->next;
while(fast_ptr != NULL)
{
if (fast_ptr->val != low_ptr->val)
{
low_ptr->next = fast_ptr;
low_ptr = low_ptr->next;
}
else // 刪除重複的節點
{
ListNode* tem_ptr = fast_ptr;
delete tem_ptr;
}
fast_ptr = fast_ptr->next;
}
low_ptr ->next = NULL;
return head;
}
};
提交結果:
十二、Linked List Cycle
題目如下:
解答如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head)
{
ListNode *slow_ptr = head;
ListNode *fast_ptr = head;
while (fast_ptr !=NULL && fast_ptr->next !=NULL)
{
slow_ptr = slow_ptr->next;
fast_ptr = fast_ptr->next->next;
if (slow_ptr == fast_ptr)
return true;
}
return false;
}
};
提交結果:
十三、親密字串
題目如下:
解答如下:
class Solution {
public:
bool buddyStrings(string A, string B)
{
// 首先考慮空字串的情況,只要有空字串,就絕不是親密字串
if(A.empty() || B.empty())
{
return false;
}
// 考慮兩個字串的長度是否相等
if (A.size() != B.size())
return false;
// 考慮兩個字串對應字元全部相同的情況
// (1)例如:A = "abcdfhh", B = "abcdfhh"
// 兩個字串完全相同,因為A中有兩個相同的字元,
// 因此我們可以通過交換這兩個相同字元(hh)來達到字串匹配;
// (2)例如:A = "abcdf", B = "abcdf"
// 即使兩個字串完全相同,但是不管調換,調換兩個字元順序後都無法匹配。
// 結論:相同字串中,如果存在兩個或以上的相同字元,則這兩個字串必是親密字串。
if (A.compare(B) ==0 )
{
sort(A.begin(), A.end());
for (int n = 0; n < A.size() - 1; n++)
{
if (A[n] == A[n + 1])
{
return true;
}
}
return false;
}
// 考慮兩個字串中有對應字元不相同的情況
vector<int> res;
for (int i = 0;i<A.size();++i)
{
if (A[i] != B[i])
res.push_back(A[i] - B[i]) ;
}
if (res.size() != 2)
return false;
else
{
if (res[0] == -res[1])
return true;
else
return false;
}
}
};
提交結果:
十四、檸檬水找零
題目如下:
解法(一):
class Solution {
public:
bool lemonadeChange(vector<int>& bills)
{
int num_5 =0,num_10 =0,num_20=0;
for (int i=0;i<bills.size();++i)
{
switch(bills[i])
{
case 5:num_5++;
break;
case 10:num_10++;
break;
case 20:num_20++;
break;
default:break;
}
if ( num_5 < num_10+num_20 || (num_5-num_10-num_20)*5+num_10*10 < 10 * num_20 )
return false;
// 10元和20元找零都需要一張5元的,因此,當5元的總數少於其餘兩個之和,則必定無法找零。
// 當num_5 >= num_10+num_20時,需要進一步檢查,是否能夠找零所有20元的。
}
return true;
}
};
提交結果:
解法(二)
class Solution {
public:
bool lemonadeChange(vector<int>& bills)
{
int num_5=0,num_10=0;
for (int i=0;i<bills.size();++i)
{
if(bills[i] == 5)
num_5++;
else if (bills[i] == 10)
{
num_5--;
num_10++;
}
else
{
if (num_10 > 0)
{
num_10--;
num_5--;
}
else
{
num_5 = num_5 - 3;
}
}
if (num_5 < 0)
return false;
}
return true;
}
};
提交如下:
十五、轉置矩陣(867)
題目如下:
C++解答:
class Solution {
public:
vector<vector<int>> transpose(vector<vector<int>>& A)
{
vector<vector<int>> res;
int len = A[0].size();
for (int i=0;i<len;i++)
{
vector<int> temp;
for (int j=0;j<A.size();j++)
{
temp.push_back(A[j][i]);
}
res.push_back(temp);
}
return res;
}
};
提交結果:
十六、單調數列(896)
題目如下:
解法(一):
class Solution {
public:
bool isMonotonic(vector<int>& A)
{
int len = A.size();
if (A[0] == A[len-1])
{
for (int i=1;i<len-1;i++)
if (A[i] != A[0])
return false;
return true;
}
else if (A[0] > A[len-1])
{
for (int i=0;i<len-1;i++)
if (A[i] < A[i+1])
return false;
return true;
}
else
{
for (int i=0;i<len-1;i++)
if (A[i] > A[i+1])
return false;
return true;
}
}
};
提交結果:
解法(二):
class Solution {
public:
bool isMonotonic(vector<int>& A)
{
bool inc = true, dec = true;
int n = A.size();
for(int i =0;i<n-1;i++)
{
if (A[i] > A[i+1])
inc = false;
if (A[i] < A[i+1])
dec = false;
}
return inc || dec;
}
};
提交結果:
十七、
未完待續,持續更新...