1-10

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;
        }
    }
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章