leetcode (9) | 分治法 && 贪心法 && 图 && 数论

目录

分治法

Pow(x,n)

Sqrt(x)

贪心法

Jump Game(mid)

Jump Game II(hard)

Best Time to Buy and Sell Stock(easy)

Best Time to Buy and Sell Stock II(easy)

Longest Substring Without Repeating Characters(hard)

Container With Most Water(mid)

Patching Array(sooooo hard)

Clone Graph(mid,但是我不会做)

数论

Happy Number(easy)

Ugly Number(easy)

Ugly Number II(mid)

Super Ugly Number

Fraction to Recurring Decimal (分数转小数)(mid)

Factorial Trailing Zeroes (后缀零个数)(easy)

Nim Game(easy)


分治法

Pow(x,n)

Implement pow(xn), which calculates x raised to the power n (xn).

Example 1:

Input: 2.00000, 10
Output: 1024.00000

Example 2:

Input: 2.10000, 3
Output: 9.26100

Example 3:

Input: 2.00000, -2
Output: 0.25000
Explanation: 2-2 = 1/22 = 1/4 = 0.25

Note:

  • -100.0 < x < 100.0
  • n is a 32-bit signed integer, within the range [−231, 231 − 1]

思路:肯定不能暴力做

解法一:递归

用递归来折半计算,每次把n缩小一半,这样n最终会缩小到0,任何数的0次方都为1,这时候我们再往回乘,如果此时n是偶数,直接把上次递归得到的值算个平方返回即可,如果是奇数,则还需要乘上个x的值。

还有一点需要引起我们的注意的是n有可能为负数,对于n是负数的情况,我们可以先用其绝对值计算出一个结果再取其倒数即可,之前是可以的,但是现在test case中加了个负2的31次方后,这就不行了,因为其绝对值超过了整型最大值,会有溢出错误,不过我们可以用另一种写法只用一个函数,在每次递归中处理n的正负,然后做相应的变换即可

class Solution {
public:
    double myPow(double x, int n) {
        if(n == 0) return 1;
        double half = myPow(x,n/2);
        if(!(n % 2)) return half*half;
        if(n > 0) return half*half*x;
        // 负数
        return half*half/x;
    }
};

解法二:非递归

让i初始化为n,然后看i是否是2的倍数,是的话x乘以自己,否则res乘以x,i每次循环缩小一半,直到为0停止循环。最后看n的正负,如果为负,返回其倒数

class Solution {
public:
    double myPow(double x, int n) {
        double res = 1.0;
        for (int i = n; i != 0; i /= 2) {
            if (i % 2 != 0) res *= x;
            x *= x;
        }
        return n < 0 ? 1 / res : res;
    }
};

 

Sqrt(x)

Implement int sqrt(int x).

Compute and return the square root of x, where x is guaranteed to be a non-negative integer.

Since the return type is an integer, the decimal digits are truncated and only the integer part of the result is returned.

Example 1:

Input: 4
Output: 2

Example 2:

Input: 8
Output: 2
Explanation: The square root of 8 is 2.82842..., and since 
             the decimal part is truncated, 2 is returned.

牛顿法:

class Solution {
public:
    int mySqrt(int x) {
        // 0的情况不要忘记
        if(x == 0) return 0;
        // 随便找一个点为起点,这里用了1,注意不要使用x/2作为起点,否则输入1是会出错
        double k = 1;
        double last = x;
        // 使用前后两次结果比较来作为跳出条件,而不是相减>1e-9,会超时
        while(last != k){
            last = k;
            k = (k+x/k)/2;
        }
        return int(k);
    }
};

补充:牛顿法可以求方程式的根(必看,详细可参考:https://www.cnblogs.com/liuyu124/p/7332493.html

但有两个条件,应用牛顿-拉弗森方法,要注意以下问题:

  • 函数在整个定义域内最好是二阶可导
  • 起始点对求根计算影响重大,可以增加一些别的判断手段进行试错

 

贪心法

Jump Game(mid)

Given an array of non-negative integers, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Determine if you are able to reach the last index.

Example 1:

Input: [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.

Example 2:

Input: [3,2,1,0,4]
Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum
             jump length is 0, which makes it impossible to reach the last index.

思路:这题我用暴力递归做超时了,我们并不是很关心每一个位置上的剩余步数,我们只希望知道能否到达末尾,也就是说我们只对最远能到达的位置感兴趣,所以我们维护一个变量reach,表示最远能到达的位置,初始化为0。遍历数组中每一个数字,如果当前坐标大于reach(注意)或者reach已经抵达最后一个位置则跳出循环,否则就更新reach的值为其和i + nums[i]中的较大值,其中i + nums[i]表示当前位置能到达的最大位置

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int reach = 0;
        int n = nums.size();
        for(int i = 0;i < n;i++){
            // 当i大于前面最大能到达的位置,即走到0的后面了,这时退出
            if(reach >= n-1 || i > reach) break;
            reach = max(reach,i+nums[i]);
        }
        return reach >= n-1;
    }
};

 

Jump Game II(hard)

Given an array of non-negative integers, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Your goal is to reach the last index in the minimum number of jumps.

Example:

Input: [2,3,1,1,4]
Output: 2
Explanation: The minimum number of jumps to reach the last index is 2.
    Jump 1 step from index 0 to 1, then 3 steps to the last index.

Note:

You can assume that you can always reach the last index.

思路:贪的是一个能到达的最远范围,我们遍历当前跳跃能到的所有位置,然后根据该位置上的跳力来预测下一步能跳到的最远距离,贪出一个最远的范围,一旦当这个范围到达末尾时,当前所用的步数一定是最小步数。

class Solution {
public:
    int jump(vector<int>& nums) {
        int target = nums.size()-1;
        int min_step = 0,reach = 0,i = 0,pre_reach;
        while(reach < target){
            ++ min_step;
            // 遍历上一步最远到达,看这一步最远能到哪里
            pre_reach = reach;
            for(;i <= pre_reach;i++){
                reach = max(reach,i+nums[i]);
                if(reach >= target) return min_step;
            }
            // 防止到不了终点的情况产生
            if(pre_reach == reach) return -1;
        }
        return min_step;
    }
};

 

Best Time to Buy and Sell Stock(easy)

Say you have an array for which the ith element is the price of a given stock on day i.

If you were only permitted to complete at most one transaction (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit.

Note that you cannot sell a stock before you buy one.

Example 1:

Input: [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
             Not 7-1 = 6, as selling price needs to be larger than buying price.

Example 2:

Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.

思路:我最开始是扫两遍,第一遍记录左边最小,第二遍从右往左计算res

后来发现没必要,只需要遍历一次数组,用一个变量记录遍历过数中的最小值,然后每次计算当前值和这个最小值之间的差值即为利润,然后每次选较大的利润来更新。当遍历完成后当前利润即为所求

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size() < 2) return 0;
        
        int left_min = prices[0],res = 0;
        for(int i = 0;i < prices.size();i++){
            res = max(res,prices[i]-left_min);
            left_min = min(left_min,prices[i]);
        }
        return res;
    }
};

 

Best Time to Buy and Sell Stock II(easy)

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete as many transactions as you like (i.e., buy one and sell one share of the stock multiple times).

Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).

Example 1:

Input: [7,1,5,3,6,4]
Output: 7
Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), profit = 5-1 = 4.
             Then buy on day 4 (price = 3) and sell on day 5 (price = 6), profit = 6-3 = 3.

Example 2:

Input: [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.
             Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are
             engaging multiple transactions at the same time. You must sell before buying again.

解法一:我的思路,每次计算局部最大收益,加入结果中

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size() < 2) return 0;
        
        int res = 0,cur = 0,left_min = prices[0];
        for(int i = 1;i < prices.size();i++){
            if(prices[i] < prices[i-1]){
                res += cur;
                cur = 0;
                left_min = prices[i];
            }
            else cur = max(cur,prices[i]-left_min);
        }
        return res+cur;
    }
};

解法二:更加简洁,炒股想挣钱当然是低价买入高价抛出,那么这里我们只需要从第二天开始,如果当前价格比之前价格高,则把差值加入利润中,因为我们可以昨天买入,今日卖出,若明日价更高的话,还可以今日买入,明日再抛出。以此类推,遍历完整个数组后即可求得最大利润。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size() < 2) return 0;
    
        int res = 0;
        for(int i = 1;i < prices.size();i++){
            if(prices[i] > prices[i-1]){
                res += (prices[i]-prices[i-1]);
            }
        }
        return res;
    }
};

 

Longest Substring Without Repeating Characters(hard)

Given a string, find the length of the longest substring without repeating characters.

Example 1:

Input: "abcabcbb"
Output: 3 
Explanation: The answer is "abc", with the length of 3. 

Example 2:

Input: "bbbbb"
Output: 1
Explanation: The answer is "b", with the length of 1.

Example 3:

Input: "pwwkew"
Output: 3
Explanation: The answer is "wke", with the length of 3. 
             Note that the answer must be a substring, "pwke" is a subsequence and not a substring.

思路:开始没写出来,下面参考了网上的答案

使用HashMap来建立字符和其出现位置之间的映射,窗口在不停向右滑动,所以我们只关心每个字符最后出现的位置,并建立映射。窗口的右边界就是当前遍历到的字符的位置,为了求出窗口的大小,我们需要一个变量new_start来指向滑动窗口的左边界,(1) 如果当前遍历到的字符从未出现过,那么直接扩大右边界

(2) 如果之前出现过,那么就分两种情况,在或不在滑动窗口内,如果不在滑动窗口内,那么就没事,当前字符可以加进来,如果在的话,就需要先在滑动窗口内去掉这个已经出现过的字符了,去掉的方法直接移动new_start指针就可以了。我们维护一个结果res,每次用出现过的窗口大小来更新结果res,就可以得到最终结果啦。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_map<char,int> char_map;
        // new_start记录每次无重复字符串的起点的前一位,这样i-new_start就是字符串长度
        int res = 0,new_start = -1;
        for(int i = 0;i < s.size();i++){
            if(char_map.count(s[i]) && char_map[s[i]] > new_start){
                new_start = char_map[s[i]];
            }
            char_map[s[i]] = i;
            res = max(res,i-new_start);
        }
        return res;
    }
};

 

Container With Most Water(mid)

Given n non-negative integers a1a2, ..., an , where each represents a point at coordinate (iai). n vertical lines are drawn such that the two endpoints of line i is at (iai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.

Note: You may not slant the container and n is at least 2.

 

The above vertical lines are represented by array [1,8,6,2,5,4,8,3,7]. In this case, the max area of water (blue section) the container can contain is 49.

思路:这题其实不难,但是我没想出来。

这道只是让求最大的一个的装水量,我们需要定义i和j两个指针分别指向数组的左右两端,然后两个指针向中间搜索,每移动一次算一个值和结果比较取较大的,容器装水量的算法是找出左右两个边缘中较小的那个乘以两边缘的距离

class Solution {
public:
    int maxArea(vector<int>& height) {
        int i = 0,j = height.size()-1,res = 0,cur = 0;
        while(i < j){
            if(height[i] > height[j]){
                cur = height[j]*(j-i);
                j--;
            }else{
                cur = height[i]*(j-i);
                i++;
            }
            res = max(res,cur);
        }
        return res;
    }
};

 

Patching Array(sooooo hard)

Given a sorted positive integer array nums and an integer n, add/patch elements to the array such that any number in range [1, n] inclusive can be formed by the sum of some elements in the array. Return the minimum number of patches required.

Example 1:

Input: nums = [1,3], n = 6

Output: 1 
Explanation:
Combinations of nums are [1], [3], [1,3], which form possible sums of: 1, 3, 4.Now if we add/patch 2 to nums, the combinations are: [1], [2], [3], [1,3], [2,3], [1,2,3].Possible sums are 1, 2, 3, 4, 5, 6, which now covers the range [1, 6].So we only need 1 patch.

Example 2:

Input: nums = [1,5,10], n = 20

Output: 2
Explanation: The two patches can be [2, 4].

Example 3:

Input: nums = [1,2,2], n = 5

Output: 0

思路:完全没想法,网上别人的思路,非常巧妙。。。我觉得我是想不出来了

定义一个变量miss,用来表示[0,n)之间最小的不能表示的值,那么初始化为1,为啥不为0呢,因为n=0没啥意义,直接返回0了。那么此时我们能表示的范围是[0, miss),表示此时我们能表示0到miss-1的数

(1)如果此时的num <= miss,那么我们可以把我们能表示数的范围扩大到[0, miss+num)

(2)如果num>miss,那么此时我们需要添加一个数,为了能最大限度的增加表示数范围,我们加上miss它本身,以此类推直至遍历完整个数组,我们可以得到结果。下面我们来举个例子说明:

给定nums = [1, 2, 4, 11, 30], n = 50,我们需要让[0, 50]之间所有的数字都能被nums中的数字之和表示出来。

首先使用1, 2, 4可能表示出0到7之间的所有数,表示范围为[0, 8),但我们不能表示8,因为下一个数字11太大了,所以我们要在数组里加上一个8,此时能表示的范围是[0, 16),那么我们需要插入16吗,答案是不需要,因为我们数组有1和4,可以组成5,而下一个数字11,加一起能组成16,所以有了数组中的11,我们此时能表示的范围扩大到[0, 27),但我们没法表示27,因为30太大了,所以此时我们给数组中加入一个27,那么现在能表示的范围是[0, 54),已经满足要求了,我们总共添加了两个数8和27,所以返回2即可。

class Solution {
public:
    int minPatches(vector<int>& nums, int n) {
        long miss = 1,cnt = 0,i = 0;
        // 注意等于号不能少,miss代表能表示[1,miss),不包括miss范围内
        while(miss <= n){
            // 此时可以表示[1,miss)
            if(i < nums.size() && nums[i] <= miss){
                miss += nums[i++];
            }else{
                cnt++;
                miss += miss;
            }
        }
        return cnt;
    }
};

 

Clone Graph(mid,但是我不会做)

Given a reference of a node in a connected undirected graph, return a deep copy (clone) of the graph. Each node in the graph contains a val (int) and a list (List[Node]) of its neighbors.

Example:

Input:
{"$id":"1","neighbors":[{"$id":"2","neighbors":[{"$ref":"1"},{"$id":"3","neighbors":[{"$ref":"2"},{"$id":"4","neighbors":[{"$ref":"3"},{"$ref":"1"}],"val":4}],"val":3}],"val":2},{"$ref":"4"}],"val":1}

Explanation:
Node 1's value is 1, and it has two neighbors: Node 2 and 4.
Node 2's value is 2, and it has two neighbors: Node 1 and 3.
Node 3's value is 3, and it has two neighbors: Node 2 and 4.
Node 4's value is 4, and it has two neighbors: Node 1 and 3.

思路:这道无向图的复制问题和之前的 Copy List with Random Pointer 有些类似,那道题的难点是如何处理每个结点的随机指针,这道题目的难点在于如何处理每个结点的neighbors,由于在深度拷贝每一个结点后,还要将其所有neighbors放到一个vector中,而如何避免重复拷贝呢?这道题好就好在所有结点值不同,所以我们可以使用HashMap来对应原图中的结点和新生成的克隆图中的结点

解法一:DFS

对于图的遍历的两大基本方法是深度优先搜索DFS和广度优先搜索BFS,这里我们先使用深度优先搜索DFS来解答此题,在递归函数中

(1)首先判空

(2)然后再看当前的结点是否已经被克隆过了,若在HashMap中存在,则直接返回其映射结点。

(3)否则就克隆当前结点,并在HashMap中建立映射,然后遍历当前结点的所有neihbor结点,调用递归函数并且加到克隆结点的neighbors数组中即可

/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> neighbors;

    Node() {}

    Node(int _val, vector<Node*> _neighbors) {
        val = _val;
        neighbors = _neighbors;
    }
};
*/

class Solution {
public:
    Node* cloneGraph(Node* node) {
        unordered_map<Node*,Node*> m;
        return dfs(node,m);
    }
    
    Node* dfs(Node* &node,unordered_map<Node*,Node*> &m){
        if(!node) return NULL;
        // 存在,直接返回
        if(m.count(node)) return m[node];
        // 未创建,递归去创建
        Node* clone = new Node(node->val);
        m[node] = clone;
        for(auto cur:node->neighbors){
            clone->neighbors.push_back(dfs(cur,m));
        }
        return clone;
    }
};

解法二:BFS

使用BFS来遍历图,使用队列queue进行辅助,还是需要一个HashMap来建立原图结点和克隆结点之间的映射。先克隆当前结点,然后建立映射,并加入queue中,进行while循环。在循环中,取出队首结点,遍历其所有neighbor结点,若不在HashMap中,我们根据neigbor结点值克隆一个新neighbor结点,建立映射,并且排入queue中。然后将neighbor结点在HashMap中的映射结点加入到克隆结点的neighbors数组中即可

/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> neighbors;

    Node() {}

    Node(int _val, vector<Node*> _neighbors) {
        val = _val;
        neighbors = _neighbors;
    }
};
*/
class Solution {
public:
    Node* cloneGraph(Node* node) {
        if(!node) return NULL;
        
        unordered_map<Node*,Node*> m;
        queue<Node*> q{{node}};
        Node* cur,* clone = new Node(node->val);
        m[node] = clone;
        while(!q.empty()){
            cur = q.front(); q.pop();
            for(auto neib:cur->neighbors){
                if(!m.count(neib)){
                    // 这里必须新建一个Node节点,不能使用clone,否则会修改m[node]造成错误
                    m[neib] = new Node(neib->val);
                    m[cur]->neighbors.push_back(m[neib]);
                    q.push(neib);
                }else{
                    m[cur]->neighbors.push_back(m[neib]);
                }
            }
        }
        return clone;
    }
};

 

数论

Happy Number(easy)

Write an algorithm to determine if a number is "happy".

A happy number is a number defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those numbers for which this process ends in 1 are happy numbers.

Example: 

Input: 19
Output: true
Explanation: 
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

思路:简单,如果在计算新的数的过程中,出现与之前重复的数,那么这就进入死循环了,直接false

class Solution {
public:
    bool isHappy(int n) {
        unordered_set<int> m;
        int res = 0;
        while(n != 1){
            while(n){
                res += pow(n%10,2);
                n = n/10;
            }
            if(res == 1) return true;
            if(m.count(res)) return false;
            m.insert(res);
            n = res;
            res = 0;
        }
        return true;
    }
};

 

Ugly Number(easy)

Write a program to check whether a given number is an ugly number.

Ugly numbers are positive numbers whose prime factors only include 2, 3, 5.

Example 1:

Input: 6
Output: true
Explanation: 6 = 2 × 3

Example 2:

Input: 8
Output: true
Explanation: 8 = 2 × 2 × 2

Example 3:

Input: 14
Output: false 
Explanation: 14 is not ugly since it includes another prime factor 7.

Note:

  1. 1 is typically treated as an ugly number.
  2. Input is within the 32-bit signed integer range: [−231,  231 − 1].

质因数:从1到N先找出最小的质因数,如果等于本身,那么说明只有一个质因数,如果不是,那么将该质因数打印出来,并将N/该质因数作为新的N值进行运算

class Solution {
public:
    bool isUgly(int num) {
        if(num == 0) return false;
        if(num == 1) return true;
        for(int i = 2;i <= 5;i++){
            if(num % i == 0) return isUgly(num/i);
        }
        return false;
    }
};

 

Ugly Number II(mid)

Write a program to find the n-th ugly number.

Ugly numbers are positive numbers whose prime factors only include 2, 3, 5

Example:

Input: n = 10
Output: 12
Explanation: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 is the sequence of the first 10 ugly numbers.

Note:  

  1. 1 is typically treated as an ugly number.
  2. n does not exceed 1690.

思路:这题看着简单,其实挺复杂的,主要有两个问题需要解决

(1)怎样保证生成的数列是顺序的,不跳过一些数字

(2)如何保证不重复,因2*3和3*2均为6,纯粹计数生成会产生重复

下面的解法很巧妙

我们知道丑陋数序列可以拆分为下面3个子列表:

(1) 1x2,  2x2, 2x2, 3x2, 3x24x2, 5x2...

(2) 1x3,  1x3, 2x3, 2x3, 2x3, 3x3, 3x3...

(3) 1x5,  1x5, 1x5, 1x5, 2x5, 2x5, 2x5...

仔细观察上述三个列表,我们可以发现每个子列表都是一个丑陋数分别乘以2,3,5,而要求的丑陋数就是从已经生成的序列中取出来的,我们每次都从三个列表中取出当前最小的那个加入序列

class Solution {
public:
    int nthUglyNumber(int n) {
        vector<int> res(1,1);
        int c2 = 0,c3 = 0,c5 = 0,n2,n3,n5,num;
        while(res.size() < n){
            // 每次都是已经生成的丑陋数去乘2,3,5,然后比较取哪个
            n2 = res[c2] * 2;
            n3 = res[c3] * 3;
            n5 = res[c5] * 5;
            num = min(n2,min(n3,n5));
            res.push_back(num);
            // 这里不要写成if else,否则会出现重复情况
            if(num == n2) c2++;
            if(num == n3) c3++;
            if(num == n5) c5++;
        }
        return res.back();
    }
};

 

Super Ugly Number

Write a program to find the nth super ugly number.

Super ugly numbers are positive numbers whose all prime factors are in the given prime list primes of size k.

Example:

Input: n = 12, primes = [2,7,13,19]

Output: 32 
Explanation: [1,2,4,7,8,13,14,16,19,26,28,32] is the sequence of the first 12 super ugly numbers given primes = [2,7,13,19] of size 4.

Note:

  • 1 is a super ugly number for any given primes.
  • The given numbers in primes are in ascending order.
  • 0 < k ≤ 100, 0 < n ≤ 106, 0 < primes[i] < 1000.
  • The nth super ugly number is guaranteed to fit in a 32-bit signed integer.

思路:与上一题原理完全相同

class Solution {
public:
    int nthSuperUglyNumber(int n, vector<int>& primes) {
        int min_num = INT_MAX,x = primes.size();
        vector<int> cnt(x,0);
        vector<int> cur_res(x,1);
        vector<int> res(1,1);
        while(res.size() < n){
            for(int i = 0;i < x;i++){
                cur_res[i] = res[cnt[i]]*primes[i];
                min_num = min(min_num,cur_res[i]);
            }
            for(int i = 0;i < x;i++){
                if(min_num == cur_res[i]) cnt[i]++;
            }
            res.push_back(min_num);
            min_num = INT_MAX;
        }
        return res.back();
    }
};

 

Fraction to Recurring Decimal (分数转小数)(mid)

Given two integers representing the numerator and denominator of a fraction, return the fraction in string format.

If the fractional part is repeating, enclose the repeating part in parentheses.

Example 1:

Input: numerator = 1, denominator = 2
Output: "0.5"

Example 2:

Input: numerator = 2, denominator = 1
Output: "2"

Example 3:

Input: numerator = 2, denominator = 3
Output: "0.(6)"

思路:这题不难,但是要注意边界情况的考虑

(1)写成分数的都是有理数,而有理数要么是有限的,要么是无限循环小数,无限不循环的叫无理数,例如圆周率pi或自然数e等

(2)由于还存在正负情况,处理方式是按正数处理,符号最后在判断,那么我们需要把除数和被除数取绝对值,那么问题就来了:由于整型数INT的取值范围是-2147483648~2147483647,而对-2147483648取绝对值就会超出范围,所以我们需要先转为long long型再取绝对值。(这边必须取绝对值进行运算,否则结果会变成-0.-1-2这样)

(3)那么怎么样找循环呢,肯定是再得到一个数字后要看看之前有没有出现这个数。为了节省搜索时间,我们采用哈希表来存数每个小数位上的数字。还有一个小技巧,由于我们要算出小数每一位,采取的方法是每次把余数乘10,再除以除数,得到的商即为小数的下一位数字。等到新算出来的数字在之前出现过,则在循环开始出加左括号,结束处加右括号。

class Solution {
public:
    string fractionToDecimal(int numerator, int denominator) {
        if(numerator == 0) return "0";
        // 符号确定
        int s1 = numerator >= 0? 1:-1;
        int s2 = denominator >= 0? 1:-1;
        unordered_map<long long,long long> m;
        // 负数最大int取abs会溢出,使用long long来存储
        long long num = abs((long long)numerator);
        long long deno = abs((long long)denominator);
        long long first = num / deno;
        long long second = num % deno;
        string res = to_string(first);
        // 第一次整除完,如果存在小数,就加入小数点
        if(second) res += ".";
        // i记录每个余数对于的商的位置,用于后续( 位置的确定,比如22/7 = 3.(142857)
        int i = res.size();
        while(second){
            // cout << res << endl;
            m[second] = i++;
            first = second*10 / deno;
            second = second*10 % deno;
            if(m.count(second)){
                res.insert(m[second],"(");
                res += to_string(first);
                res += ")";
                break;
            }
            res += to_string(first);
        }
        // 加入符号
        if(s1*s2 < 0) res.insert(0,"-");
        return res;
    }
};

 

Factorial Trailing Zeroes (后缀零个数)(easy)

Given an integer n, return the number of trailing zeroes in n!.

Example 1:

Input: 3
Output: 0
Explanation: 3! = 6, no trailing zero.

Example 2:

Input: 5
Output: 1
Explanation: 5! = 120, one trailing zero.

思路:后缀0是由2,5相乘得来,因此只需看有多少个2,5即可 

class Solution {
public:
    int trailingZeroes(int n) {
        int res = 0;
        while(n >= 5){
            res += n/5;
            n = n/5;
        }
        return res;
    }
};

 

Nim Game(easy)

You are playing the following Nim Game with your friend: There is a heap of stones on the table, each time one of you take turns to remove 1 to 3 stones. The one who removes the last stone will be the winner. You will take the first turn to remove the stones.

Both of you are very clever and have optimal strategies for the game. Write a function to determine whether you can win the game given the number of stones in the heap.

Example:

Input: 4

Output: false 
Explanation: If there are 4 stones in the heap, then you will never win the game;
             No matter 1, 2, or 3 stones you remove, the last stone will always be 
             removed by your friend.

讨论:我们来generalize一下这道题,当可以拿1~n个石子时,那么个数为(n+1)的整数倍时一定会输,我们试着证明一下这个结论,若当前共有m*(n+1)个石子,那么:

当m=1时,即剩n+1个的时候,肯定会输,因为不管你取1~n中的任何一个数字,另一个人都可以取完。

当m>1时,即有m*(n+1)的时候,不管你先取1~n中的任何一个数字x,另外一个人一定会取n+1-x个,这样总数就变成了(m-1)*(n+1),第二个人就一直按这个策略取,那么直到剩n+1个的时候,就便变成m=1的情况,一定会输。

class Solution {
public:
    bool canWinNim(int n) {
        return n % 4;
    }
};

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值