2. Add Two Numbers
思路:結合示例可以看到其實就是完成鏈表對應位置相加來模擬加法運算。
首先想到要返回頭結點,則頭結點的創建過程單獨拿出來(兩個都非空,則頭結點一定存在)。並設置好pre=head。
在後續中模式pre->next=node,pre=node。在都不爲空的情況下,每次都要檢查上一次計算設置的carryFlag,以及重新設置carryFlag。
當有一個爲空時,不用針對l1和12兩種情況重複寫代碼,只需l1=l1?l1:l2即可。這時也要注意可能的進位,直到兩個都爲空。
兩個都爲空,再考察是否有進位,如果有要新建一個結點。這個其實合併了兩種情況:初始長度相等和初始長度不等。
get:使用初始化列表(4種情況必須用初始化列表)。注意變量的作用域,不能僅在if..else...中完成後續使用變量的定義。
/**
* 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) {
bool carryFlag = 0;
ListNode *head = NULL;
if (l1->val+l2->val < 10) {
head = new ListNode(l1->val+l2->val);
carryFlag = 0;
} else {
head = new ListNode(l1->val+l2->val-10);
carryFlag = 1;
}
ListNode *pre = head;
ListNode *node = NULL;
l1 = l1->next;
l2 = l2->next;
while(l1 && l2) {
if (l1->val+l2->val+carryFlag < 10) {
node = new ListNode(l1->val+l2->val+carryFlag);
carryFlag = 0;
} else {
node = new ListNode(l1->val+l2->val+carryFlag-10);
carryFlag = 1;
}
pre->next = node;
pre = node;
l1 = l1->next;
l2 = l2->next;
}
l1 = l1?l1:l2;
if (l1) {
while(l1) {
if (l1->val+carryFlag < 10) {
node = new ListNode(l1->val+carryFlag);
carryFlag = 0;
} else {
node = new ListNode(l1->val+carryFlag-10);
carryFlag = 1;
}
pre->next = node;
pre = node;
l1 = l1->next;
}
}
if (carryFlag) {
node = new ListNode(1);
pre->next = node;
}
return head;
}
};
3. Longest Substring Without Repeating Characters
思路:該子串一定有開始位置在0-len-1,所以對子串的首字符在0-len-1之間遍歷,在這個過程中不斷後移臨時pos,直到有重複字符出現。在這個過程中維持更新最大長度即可。在程序寫好之後可以看到,如果是空串也是可以正確處理的。
這個問題可以參照kmp做一個優化,當這個過程中出現重複字符時,下一次遍歷index就可以直接從該重複字符上一次出現的下一個位置開始,因爲從前面的index開始的話必然會再次在該位置上看到該重複字符,不可能產生更長的不重複子串。則需要記錄每個字符出現的位置,爲此開一個int型的字符數大小的數組,並在每次index發生更新時進行重新賦值-1。
外層循環index從0-len-1遍歷,內層循環pos從index到len-1做遍歷,因爲有index = arr[s[pos]]+1的更新,外層循環寫成while比for更方便。
另外注意內層循環while(pos<len)跳出時有兩種情況:找到重複字符,此時需要index更新開始下次大迭代;pos=len,此時不需要再繼續了,這已經是可能的最大長度了。對這兩種情況要判斷並分別執行不同處理。
get:ascii字符的範圍是0-127,所以正好可以開128的數組。(如果限定了具體字符範圍,可能提前終止,比如最大可能長度就是26)
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int len = s.length();
int index = 0;
int arr[128];
int maxCnt = 0;
while(index < len) {
for (int i = 0; i < 128; i++) {
arr[i] = -1;
}
int cnt = 0;
int pos = index;
while(pos < len) {
if (arr[s[pos]] != -1) {
break;
} else {
arr[s[pos]] = pos;
cnt++;
}
pos++;
}
if (cnt > maxCnt) {
maxCnt = cnt;
}
if (pos == len) {
break;
} else {
index = arr[s[pos]]+1;
}
}
return maxCnt;
}
};
5. Longest Palindromic Substring
思路:求最長迴文子串,想法用動態規劃來解。首先要明確dp[i][j]的意義爲首尾在i,j的迴文串長度。狀態轉移方程:if(s[i] == s[j]),且中間構成迴文串(這一點很重要),dp[i][j]=dp[i+1][j-1]+2;if(s[i]!=s[j]),dp[i][j]=0。另外,可以採取遍歷迴文子串中間字符的方法來做,複雜度同樣爲O(n^2)。
在寫出大致轉移方程後,一定要認真考慮三個方面。初始狀態:dp[i][i] = 1。循環順序:i遞減,j遞增;而且要滿足i<j,不僅如此在s[i]=s[j]時,若i+1=j,則dp[i][j]=2,若dp[i+1][j-1]=0(即中間不構成迴文串),則dp[i][j]=0。
另外,該題目要求返回最長迴文子串,在長度發生更新時要記錄下標。這裏初始長度的設置:考慮到空串,直接返回空串;對非空串,初始長度設置爲1,並記錄下標爲0,0。這一點很細節,如果不釐清的話容易出各種問題。
在解本題時,出現的問題主要在不自覺地混淆迴文串與迴文序列,導致寫的方程不對。
get:c++中,string.substr(pos, n)可以用於截取子串。
class Solution {
public:
string longestPalindrome(string s) {
int len = s.length();
if (!len) {
return "";
}
int dp[len][len];
for (int i = 0; i < len; i++) {
dp[i][i] = 1;
}
int maxLen = 1;
int index_i = 0;
int index_j = 0;
for (int i = len-2; i >= 0; i--) {
for (int j = i+1; j < len; j++) {
if (s[i] == s[j]) {
if (i+1 == j) {
dp[i][j] = 2;
} else if (!dp[i+1][j-1]){
dp[i][j] = 0;
} else {
dp[i][j] = dp[i+1][j-1]+2;
}
} else {
dp[i][j] = 0;
}
if (dp[i][j] > maxLen) {
maxLen = dp[i][j];
index_i = i;
index_j = j;
}
}
}
return s.substr(index_i,index_j-index_i+1);
}
};
6. ZigZag Conversion
思路:這道題只需要簡單模擬這個Z字形排列即可,不需要去找每一行在原string中出現位置。
具體來說:定義一個numRows維的vector,對string中每個字符依次加入到對應的vector即可。
在這個過程中,用row==0,row==numRows-1來確定下次移動方向的更改。另外需要將第一個字符放入第一個vector中,並設置preRow和direction,避免與前面規則的衝突。還有一些特殊情況需要處理:string爲空,行爲1。
get:vector型的數組:vector<int> vecArr[numRows]。
class Solution {
public:
string convert(string s, int numRows) {
int len = s.length();
if (!len) {
return "";
}
if (numRows == 1)
return s;
vector<int> vecArr[numRows];
vecArr[0].push_back(s[0]);
int direction = 1;
int preRow = 0;
for (int i = 1; i < len; i++) {
vecArr[preRow+direction].push_back(s[i]);
preRow = preRow+direction;
if (preRow == numRows-1) {
direction = -1;
} else if (preRow == 0) {
direction = 1;
}
}
string resultString = "";
for (int i = 0; i < numRows; i++) {
for (int j = 0; j < vecArr[i].size(); j++) {
resultString += vecArr[i][j];
}
}
return resultString;
}
};
8. String to Integer(atoi)
思路:按照string中字符出現順序自左向右做atoi過程。
首先,略過所有‘ ’,在實現時while(index<len && str[index]==' ' && index++);,這裏用到了短路求值的技巧。若index == len,則return 0(不存在不是‘ ’的字符,這裏連帶着將空串處理了)此時str[index]必須是+,-,0-9,否則return 0。此時若str[index]==+,-,設置sign後略過,否則設置sign=1。接下來的必須爲0-9,開始處理數字。這個過程中需要用long long型來記錄數字,並每次檢查是否超過一定範圍(字符串很長,即使long long也是不夠用的)。最後按照要求return即可。
get:(result >1<<32)是不對的,1是int,左移32越界;正確寫法:(result > (long long)1<<32)。
class Solution {
public:
int myAtoi(string str) {
int len = str.length();
int index =0;
while(index<len && str[index]==' ' && ++index);
if (index == len) {
return 0;
}
if ((str[index]<'0' || str[index]>'9') && str[index]!='+' && str[index]!='-') {
return 0;
}
int sign = 1;
if (str[index] == '+') {
index++;
} else if(str[index] == '-') {
sign = -1;
index++;
}
if (str[index]<='9' && str[index]>='0') {
while(index<len && str[index]=='0' && ++index);
long long result = 0;
while(index<len && str[index]<='9' && str[index]>='0') {
result = result*10+str[index]-'0';
if (result > (long long)1<<32) {
break;
}
index++;
}
int int_max = ~(1<<31);
int int_min = 1<<31;
result = result*sign;
if (result < int_min) {
result = int_min;
} else if (result > int_max) {
result = int_max;
}
return result;
} else {
return 0;
}
}
};