首页 力扣-剑指offer错题集
文章
取消

力扣-剑指offer错题集

数学类型

剑指 Offer 44. 数字序列中某一位的数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
    int findNthDigit(int n) {
        if(n==0)return 0;
        long long count = 9, start = 1;
        int digit = 1;
        while(n>count)
        {
            n-=count;
            digit++;
            start *= 10;
            count = 9 * start * digit;
        }
        long long num = start + (n-1)/digit;
        int pos = (n-1)%digit;
        string s = to_string(num);
        return s[pos]-'0';
    }
};

    这道题目算是数学中的找规律,比较难搞的是边界问题,还有就是变量类型需要时long。其中count指的是第几位,start是每一个数量级开始的数字,digit是在该数量级数字的位数

剑指 Offer 56 - I. 数组中数字出现的次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
vector<int> singleNumbers(vector<int>& nums) {
        int abXOR = 0;
        for(auto &i : nums)
        {
            abXOR ^= i;
        }
        int diffOne = 1;
        while((diffOne & abXOR) == 0)
        {
            diffOne<<=1;
        }
        int a = 0,b = 0;
        for(auto &i: nums)
        {
            if(diffOne & i)
            {
                a ^= i;
            }
            else b ^= i;
        }
        return vector<int>{a,b};
    }

    我觉得这算计算机类型了都。其实这道题适合看题解,不过提一嘴异或,就是如果所有的数字异或一遍,哪些一样的数字就会消失,最终的结果就是那两个不一样的数的异或,其实很好证明,异或满足交换律。那么剩下的难点就是如何把ab分到两组而且还能脱颖而出了

    还有一点需要注意的是一个数左移是diffOne = diffOne<<1,我一开始只是左移了,忘记写等号了,就产生了超时,那么为什么会产生超时呢,我不说,让未来复习的我想一下

排序类型

剑指 Offer 45. 把数组排成最小的数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
    static bool compare(int a, int b)
{
    string sa = to_string(a);
    string sb = to_string(b);
    return sa + sb < sb+sa;
}
public:

    string minNumber(vector<int>& nums) {
        sort(nums.begin(),nums.end(),compare);
        string ret;
        for(auto num:nums)
        {
            ret += to_string(num);
        }
        return ret;
    }
};

    这道题就是个排序,就比两个数作为字符串相加,看谁在前边小,谁就排在前面去,sort()真好用

动态规划类型

剑指 Offer 46. 把数字翻译成字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public:
    int translateNum(int num) {
        string s = to_string(num);
        int p = 0, q = 1, r = 1;//r是当前下标的翻译种类数量
        for(int i = 1; i < s.size(); i++)
        {
            p = q;
            q = r;
            r = 0;
            if(s.substr(i-1,2)>= "10" && s.substr(i-1,2)<="25")
            {
                r = p + q;
            }else r = q;
        }
        return r;
    }
};

    简单的dp,真没啥说的,看题目就明白了,但当时就是没想到这个法子

剑指 Offer 49. 丑数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
    int nthUglyNumber(int n) {
        int dp[1691];
        int p2 = 1, p3 = 1, p5 = 1;
        dp[1] = 1;
        for(int i = 2; i <= n; i ++)
        {
            dp[i] =min(min(dp[p2]*2,dp[p3]*3),dp[p5]*5);
            if(dp[i] == dp[p2]*2)p2++;
            if(dp[i] == dp[p3]*3)p3++;
            if(dp[i] == dp[p5]*5)p5++;
        }
        return dp[n];
    }
};

    第一次做的时候想到了丑数是由2、3、5相互组合相乘得到的,但是没往dp上想,甚至用了素数表,但超时了。使用三个指针+dp就行了,后面的丑数都是由前面的丑数乘以2、3、5得到的,还有需要注意的一点是去重,代码很清楚,不多说

数据结构

剑指 Offer 59 - II. 队列的最大值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class MaxQueue {
public:
        deque<int> d;
        queue<int> q;
    MaxQueue() {

    }

    int max_value() {
        if(d.empty()) return -1;
        return d.front();
    }

    void push_back(int value) {
        q.push(value);
        while(!d.empty() && d.back() < value)
        {
            d.pop_back();
        }
        d.push_back(value);
    }

    int pop_front() {
        if(q.empty()) return -1;
        int ret = q.front();
        q.pop();
        if(ret == d.front())
        {
            d.pop_front();
        }
        return ret;
    }
};

/**
 * Your MaxQueue object will be instantiated and called as such:
 * MaxQueue* obj = new MaxQueue();
 * int param_1 = obj->max_value();
 * obj->push_back(value);
 * int param_3 = obj->pop_front();
 */

    这道题难点在于双端队列取最大值,就是再push的时候判断,因为push时,当前值位于队列的最后一个,那么如果队列前面的值比当前值小,就说明在pop到当前值之前,调用max得到的结果都不会是前面那些比当前值小的数,所以呢,在双端队列里遇到比当前值小的删除就行。这么下来,双端队列的第一位永远也是最大的一位。

剑指 Offer 60. n个骰子的点数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
    vector<double> dicesProbability(int n) {
        vector<double> dp(6,1.0/6.0);
        for(int i = 2; i <= n; i ++)
        {
            vector<double> tmp(5 * i + 1, 0.0);
            for(int m = 0; m < dp.size(); m++)
            {
                for(int k = 0; k < 6; k++)
                {
                    tmp[m + k] += dp[m] / 6.0;
                }
            }
            dp = tmp;
        }
        return dp;
    }
};

    这道题给我cpu干懵了,首先我把它当成找规律做,浪费了大票的时间。其次,得知它需要用dp做后,又开始搞二维数组,因为下标关系紊乱,又浪费了大票时间,所以看答案,使用滚动数组最简单方便了,麻了,关键的是要找对状态转移方程

本文由作者按照 CC BY 4.0 进行授权