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 × 10 2456810000 = 245681\times 10\times 10\times 10\times 10 2456810000=245681×10×10×10×10,可以知道,任何一个n的阶乘,其末尾0的个数取决于因数10的个数。而 10 = 2 × 5 10 = 2\times 5 10=2×5,由于2出现的次数要远远大于5,所以我们只需计算n的阶乘的因数中5的个数。这里便能得出一个结论:

n的阶乘的尾部为0的个数主要取决于其中5的个数。

因为 n ! = 1 × 2 × 3 × ⋅ ⋅ ⋅ × ( n − 1 ) × n n! = 1\times 2\times 3\times···\times(n - 1)\times n n!=1×2×3××(n1)×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退出循环。这算法的时间复杂度就降低到了 l o g ( N / 5 ) 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;
    }
};

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值