《剑指Offer》C++实现-Week2

24. 机器人的运动范围

地上有一个 m 行和 n 列的方格,横纵坐标范围分别是 0∼m−1 和 0∼n−1。

一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格。

但是不能进入行坐标和列坐标的数位之和大于 k 的格子。

请问该机器人能够达到多少个格子?

样例1
输入:k=7, m=4, n=5

输出:20

样例2
输入:k=18, m=40, n=40

输出:1484

解释:当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。
但是,它不能进入方格(35,38),因为3+5+3+8 = 19。
注意:

0<=m<=50
0<=n<=50
0<=k<=100

class Solution {
public:

    int get_single_sum(int x)
    {
        int s = 0;
        while(x) s += x % 10, x /= 10;
        return s;
    }

    int get_sum(pair<int, int> p)
    {
        return get_single_sum(p.first) + get_single_sum(p.second); //一个点两个数
    }

    int movingCount(int threshold, int rows, int cols)
    {
        int res = 0; 
        if(!rows || !cols) return 0;
        
        vector<vector<bool>> st(rows, vector<bool>(cols));
        queue<pair<int, int>>q;
        
        q.push({0,0});
        
        int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
        
        while(q.size())
        {
            auto t = q.front(); //每次取出队头
            q.pop();
            
            if(get_sum(t) > threshold || st[t.first][t.second]) continue; //判断当前点有没有被遍历过
            res ++;
            st[t.first][t.second] = true;
            
            for(int i = 0; i < 4; i ++)
            {
                int x = t.first + dx[i], y = t.second + dy[i];
                if(x >= 0 && x < rows && y >= 0 && y < cols)
                {
                    q.push({x, y});
                }
            }
        }
        return res;
    }
};

25. 剪绳子

给你一根长度为 n 绳子,请把绳子剪成 m 段(m、n 都是整数,2≤n≤58 并且 m≥2)。

每段的绳子的长度记为k[0]、k[1]、……、k[m]。k[0]k[1] … k[m] 可能的最大乘积是多少?

例如当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到最大的乘积18。

样例
输入:8

输出:18

class Solution {
public:
    int maxProductAfterCutting(int n) {
        //拆成多个3,最多两个2
        if(n <= 3) return 1 * (n - 1);
        
        int res = 1;
        if(n % 3 == 1) res *= 4, n -= 4;
        if(n % 3 == 2) res *= 2, n -= 2;
        while(n) res *= 3, n -= 3;
        
        return res;
    }
};

26. 二进制中1的个数

输入一个32位整数,输出该数二进制表示中1的个数。

注意:

负数在计算机中用其绝对值的补码来表示。

样例1
输入:9
输出:2
解释:9的二进制表示是1001,一共有2个1。

样例2
输入:-2
输出:31
解释:-2在计算机里会被表示成11111111111111111111111111111110,
一共有31个1。

class Solution {
public:
    int NumberOf1(int _n) {
        unsigned int n = _n; //无符号整数右移后高位补上0,有符号整数右移高位补上1
        
        int s = 0;
        while(n) s += n & 1, n >>= 1; //取个位计算1的个数,右移删掉最低位
        
        return s;
    }
};

27. 数值的整数次方

实现函数double Power(double base, int exponent),求base的 exponent次方。

不得使用库函数,同时不需要考虑大数问题。

注意:

不会出现底数和指数同为0的情况

样例1
输入:10 ,2
输出:100

样例2
输入:10 ,-2
输出:0.01

class Solution {
public:
    double Power(double base, int exponent) {
        double res = 1;
        for(int i = 0; i < abs(exponent); i ++) res *= base;
        if(exponent < 0) res = 1 / res;
        
        return res;
    }
};

28. 在O(1)时间删除链表结点

给定单向链表的一个节点指针,定义一个函数在O(1)时间删除该结点。

假设链表一定存在,并且该节点一定不是尾节点。

样例
输入:链表 1->4->6->8
删掉节点:第2个节点即6(头节点为第0个节点)

输出:新链表 1->4->8

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void deleteNode(ListNode* node) {
        node ->val = node->next->val;
        node->next = node->next->next;
    }
};

29. 删除链表中重复的节点

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留。

样例1
输入:1->2->3->3->4->4->5

输出:1->2->5
样例2
输入:1->1->1->2->3

输出:2->3

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplication(ListNode* head) {
        auto dummy = new ListNode(-1);
        dummy->next = head;
        
        auto p = dummy;
        while(p->next)
        {
            auto q = p->next;
            while(q && p->next->val == q->val) q = q->next;
            
            if(p->next->next == q) p = p->next;
            else p->next = q;
        }
        return dummy->next;
    }
};

30. 正则表达式匹配

请实现一个函数用来匹配包括’.‘和’*'的正则表达式。

模式中的字符’.‘表示任意一个字符,而’*'表示它前面的字符可以出现任意次(含0次)。

在本题中,匹配是指字符串的所有字符匹配整个模式。

例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配。

样例
输入:

s=“aa”
p=“a*”

输出:true

/*

动态规划DP

状态表示:f[i][j] 第一串从第i个字母到最后,和第二串从第j个字母到最后 相匹配

状态转移:
1.p[j]是正常字符,f[i][j] == s[i] == p[j] && f[i + 1][j + 1]
2.p[j]是'.', f[i][j] = f[i + 1][j + 1]
3.p[j + 1]是'*', f[i][j] = f[i][j + 2](前面字符出现0次)  || f[i + 1][j] (前面字符是不是能更当前*位匹配)

f[n][m] = true

*/
class Solution {
public:
    int n, m;
    vector<vector<int>> f;
    string s, p;
    bool isMatch(string _s, string _p) {
        s = _s, p = _p;
        n = s.size(), m = p.size();
        f = vector<vector<int>>(n + 1, vector<int>(m + 1, -1));
        return dp(0, 0);
    }
    
    bool dp(int x, int y)
    {
        if(f[x][y] != -1) return f[x][y];
        if(y == m) return f[x][y] = x == n;
        bool first_match = x < n && (s[x] == p[y] || p[y] == '.'); //顺序
        if(y + 1 < m && p[y + 1] == '*')
        {
            f[x][y] = dp(x, y + 2) || first_match && dp(x + 1, y); //增加对first_match的判断
        }
        else
        {
            f[x][y] = first_match && dp(x + 1, y + 1);
        }
        return f[x][y];
    }
};

31. 表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。

例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。

但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。

注意:

小数可以没有整数部分,例如.123等于0.123;
小数点后面可以没有数字,例如233.等于233.0;
小数点前面和后面可以有数字,例如233.666;
当e或E前面没有数字时,整个字符串不能表示数字,例如.e1、e1;
当e或E后面没有整数时,整个字符串不能表示数字,例如12e、12e+5.4;

样例:
输入: “0”
输出: true

class Solution {
public:
    bool isNumber(string s) {
        //消除空格
        int i = 0, j = s.size();
        while(i <= j && s[i] == ' ') i ++;
        while(i <= j && s[j] == ' ') j --;
        if(i > j) return false;
        
        s = s.substr(i, j - i + 1);
        if(s[0] == '+' || s[0] == '-') s = s.substr(1); //从下标1开始一直到字符串结束
        if(s.empty() || (s[0] == '.' && s.size() == 1)) return false; // +, . , -
        
        int dot = 0, e = 0;
        for(int i = 0; i < s.size(); i ++)
        {
            if(s[i] >= '0' && s[i] <= '9') ;//是数字
            else if(s[i] == '.')
            {
                dot ++;
                if(dot > 1 || e) return false; //12.12.12,  112e.121
            }
            else if(s[i] == 'e' || s[i] == 'E')
            {
                e ++;
                if(!i || i + 1 == s.size() || e > 1 || s[i - 1] == '.' && i == 1) return false; //e125, 123e, .e1
                if(s[i + 1] == '+' || s[i + 1] == '-')
                {
                    if(i + 2 == s.size()) return false; //122e+
                    i ++;
                }
            }
            else return false;
        }
        return true;
    }
};

32. 调整数组顺序使奇数位于偶数前面

输入一个整数数组,实现一个函数来调整该数组中数字的顺序。

使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分。

样例
输入:[1,2,3,4,5]

输出: [1,3,5,2,4]

class Solution {
public:
    void reOrderArray(vector<int> &array) {
         int i = 0, j = array.size() - 1;
         while(i <= j)
         {
             while(i <= j && array[i] % 2 == 1) i ++;// 奇数
             while(i <= j && array[j] % 2 == 0) j --; //偶数
             if(i < j) swap(array[i], array[j]); //没有相遇就交换
         }
    }
};

33. 链表中倒数第k个节点

输入一个链表,输出该链表中倒数第k个结点。

注意:
k >= 0;
如果k大于链表长度,则返回 NULL;

样例
输入:链表:1->2->3->4->5 ,k=2

输出:4

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* findKthToTail(ListNode* pListHead, int k) {
        int n = 0;
        for(auto p = pListHead; p; p = p->next) n ++;
        if(k > n) return nullptr;
        auto p = pListHead;
        for(int i = 0; i < n - k; i ++) p = p->next;
        return p;
    }
};

34. 链表中环的入口结点

给定一个链表,若其中包含环,则输出环的入口节点。

若其中不包含环,则输出null。

样例
QQ截图20181202023846.png--------------------

给定如上所示的链表:
[1, 2, 3, 4, 5, 6]
2
注意,这里的2表示编号是2的节点,节点编号从0开始。所以编号是2的节点就是val等于3的节点。

则输出环的入口节点3.

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *entryNodeOfLoop(ListNode *head) {
        auto i = head, j = head;
        while(i && j)
        {
            i = i->next;
            j = j->next;
            if(j) j = j->next;
            if(i == j) //第一次相遇 i回到起点
            {
                i = head;
                while(i != j)
                {
                    i = i->next; 
                    j = j->next;
                }
                return i;
            }
        }
        return 0;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值