LintCode簡單 10題

1. A + B 問題

Note
不使用傳統的“+”運算符,即十進制下的加法,那就從最基礎的二進制加法原理出發。
單獨位相加:0 + 0 = 0, 0 + 1 = 1, 1 + 0 = 0, 1 + 1 = 0。
進位:0 + 0 = 0; 0 + 1 = 0; 1 + 0 = 0; 1 + 1 = 1。
可以發現,單獨位相加符合異或運算的規則,進位符合與運算的規則。加法的結果 = 單獨位 + 進位,因此可以模擬加法運算。由於進位是進到更高位,所以在進位後,要進行一次左移操作,以使進位給到前一位。

class Solution {
public:
    /**
     * @param a: An integer
     * @param b: An integer
     * @return: The sum of a and b 
     */
    int aplusb(int a, int b) {
        return (a ^ b) + ((a & b) << 1);
    }
};

2. 尾部的零

Note
可以這麼理解:比如2456810000=245681×10×10×10×102456810000 = 245681\times 10\times 10\times 10\times 10,可以知道,任何一個n的階乘,其末尾0的個數取決於因數10的個數。而10=2×510 = 2\times 5,由於2出現的次數要遠遠大於5,所以我們只需計算n的階乘的因數中5的個數。這裏便能得出一個結論:

n的階乘的尾部爲0的個數主要取決於其中5的個數。

因爲n!=1×2×3××(n1)×nn! = 1\times 2\times 3\times···\times(n - 1)\times n,所以我們最基礎的想法就是,枚舉1到n,去整除5,能整除計數加1。同時要注意,不是單純地能整除就行,需要對每個枚舉的數都要除5到0爲止。比如25是含兩個5,所以計數是加2。又比如125的因數5個數爲3。

其實更高級一點,我們換個想法。如果我們直接將n除以5,得到的就是n中所有能整除5的數的個數,爲什麼呢?把5理解爲步長就好理解,5,10,15…,是不是呢?將n/5再除以5,得到的就是n中所有能整除25的數的個數;同樣用步長理解,25,50,75…,是不是呢?直到n爲0退出循環。這算法的時間複雜度就降低到了log(N/5)log(N/5)

class Solution {
public:
    /*
     * @param n: A long integer
     * @return: An integer, denote the number of trailing zeros in n!
     */
    long long trailingZeros(long long n) {
        long long count = 0;
        while (n)
        {
            n /= 5;
            count += n;
        }
        return count;
    }
};

6. 合併排序數組 II

Note
我的想法是將B中的元素逐個的插入A中。當B最後一個元素的值大於A中最後一個元素的值時,剛好可以在 i 到達A.size()前插入完;反之,可在下一個while中將剩餘的B中的元素插入A的末尾。

class Solution {
public:
    /**
     * @param A: sorted integer array A
     * @param B: sorted integer array B
     * @return: A new sorted integer array
     */
    vector<int> mergeSortedArray(vector<int> &A, vector<int> &B) {
        vector<int> C;
        int i = 0, j = 0;
        while (i < A.size() && j < B.size())
        {
            if (B[j] < A[i])
                A.insert(A.begin() + i, B[j++]);
            else
                ++i;
        }
        while (j < B.size())
        {
            A.insert(A.begin() + i, B[j++]);
            ++i;
        }
        return A;
    }
};

8. 旋轉字符串

Note
題目的意思就是把字符串末尾的offset位字符移到開頭,當然offset要先做處理,大於字符串長度時要減小。我的想法就是將前面的str.length() - offset位字符串給一個新的字符串,然後刪除這一部分,再將新字符串連接到舊字符串末尾。

class Solution {
public:
    /**
     * @param str: An array of char
     * @param offset: An integer
     * @return: nothing
     */
    void rotateString(string &str, int offset) {
        if (str.length() == 0)
            return;
        string temp;
        int end = str.length() - offset % str.length();
        temp.insert(temp.begin(), str.begin(), str.begin() + end);
        str.erase(str.begin(), str.begin() + end);
        str += temp;
    }
};

9. Fizz Buzz 問題

Note
兩段代碼都能達到13ms以上,網速夠好的情況下。只有一點要記住,必須先判斷能否同時被3和5整除。

class Solution {
public:
    /**
     * @param n: An integer
     * @return: A list of strings.
     */
    vector<string> fizzBuzz(int n) {
        vector<string> vecstr;
        for (int i = 1; i <= n; ++i)
        {
            int a = i % 10;
            if (i % 3 == 0)
            {
                if (a == 0 || a == 5)
                    vecstr.push_back("fizz buzz");
                else
                    vecstr.push_back("fizz");
            }
            else if (a == 0 || a == 5)
                vecstr.push_back("buzz");
            else
                vecstr.push_back(to_string(i));
        }
        return vecstr;
    }
};
class Solution {
public:
    /**
     * @param n: An integer
     * @return: A list of strings.
     */
    vector<string> fizzBuzz(int n) {
        vector<string> vecstr;
        for (int i = 1; i <= n; ++i)
        {
            if (i % 3 == 0 && i % 5 == 0)
                vecstr.push_back("fizz buzz");
            else if (i % 3 == 0)
                vecstr.push_back("fizz");
            else if (i % 5 == 0)
                vecstr.push_back("buzz");
            else
                vecstr.push_back(to_string(i));
        }
        return vecstr;
    }
};

14. 二分查找

Note
對二分查找進行了一點改進,因爲這是一個存在重複元素的數組,所以一旦發現了同樣的元素,就不再進行查找而是一直往左減小mid直到不再等於target。這點小小的改進竟然使我的耗時成了月榜第一。。。

class Solution {
public:
    /**
     * @param nums: The integer array.
     * @param target: Target to find.
     * @return: The first position of target. Position starts from 0.
     */
    int binarySearch(vector<int> &nums, int target) {
        int mid, left = 0, right = nums.size() - 1;
        while (left <= right)
        {
            mid = (left + right) / 2;
            if (nums[mid] == target)
            {
                while (nums[mid - 1] == target)
                    --mid;
                return mid;
            }
            else if (nums[mid] < target)
                left = mid + 1;
            else
                right = mid - 1;
        }
        return -1;
    }
};

28. 搜索二維矩陣

Note
兩次二分查找,第一次二分是爲了確定target可能在哪一行,第二次二分是爲了確定target是否存在二維矩陣中。其中要注意,第一次二分後的right,就是它可能存在的行號。其他的,就注意vector<vector> vec的使用方法。vec.size()是一維長度,vec[n].size()是第n維長度。正常表示其中的元素就和普通的二維數組一樣即可,即vec[m][n]。

class Solution {
public:
    /**
     * @param matrix: matrix, a list of lists of integers
     * @param target: An integer
     * @return: a boolean, indicate whether matrix contains target
     */
    bool searchMatrix(vector<vector<int>> &matrix, int target) {
        int len = matrix.size();
        if (target > matrix[len - 1][matrix[len - 1].size() - 1] || target < matrix[0][0])
            return false;
        int mid, left = 0, right = len - 1;
        while (left <= right)
        {
            mid = (left + right) / 2;
            if (matrix[mid][0] == target)
                return true;
            else if (matrix[mid][0] < target)
                left = mid + 1;
            else
                right = mid - 1;
        }
        int t = right;
        left = 0, right = matrix[t].size() - 1;
        while (left <= right)
        {
            mid = (left + right) / 2;
            if (matrix[t][mid] == target)
                return true;
            else if (matrix[t][mid] < target)
                left = mid + 1;
            else
                right = mid - 1;
        }
        return false;
    }
};

35. 翻轉鏈表

Note
正常寫法如第一段代碼所示,用指針p遍歷原鏈表,然後利用頭插法新建一個鏈表並返回頭結點的下一個結點即可。看了討論之後發現了一個更棒的方法,如果我們把鏈表反向指一下,能更快實現鏈表的翻轉。即將 0->1->2->3->4->NULL 變爲 NULL<-0<-1<-2<-3<-4。實現起來很簡單,從頭一個一個往左指(表現爲指向自己的前一個結點)即可,但千萬不要在往左指的過程中丟失了右邊的鏈表。

注意此題沒有頭結點,頭結點是不含任何信息的。

/**
 * Definition of singly-linked-list:
 *
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param head: n
     * @return: The new head of reversed linked list.
     */
    ListNode * reverse(ListNode * head) {
        if (head == NULL)
            return head;
        ListNode *nhead = new ListNode;
        nhead->val = 0;
        nhead->next = NULL;
        while (head != NULL)
        {
            ListNode *q = new ListNode;
            q->val = head->val;
            q->next = nhead->next;
            nhead->next = q;
            head = head->next;
        }
        return nhead->next;
    }
};
/**
 * Definition of singly-linked-list:
 *
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param head: n
     * @return: The new head of reversed linked list.
     */
    ListNode * reverse(ListNode * head) {
    	if (head == NULL)
            return head;
        ListNode *nhead = NULL;
        ListNode *curr = head;
        ListNode *p;
        while (curr != NULL)
        {
            p = curr->next;
            curr->next = nhead;
            nhead = curr;
            curr = p;
        }
        return nhead;
    }
};

一定要自己寫一遍哦~~~

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