• 【剑指offer】刷题记录11~18(持更)


    注:点击题目可直接跳转至leetcode相应的题目代码提交处

    11. 旋转数组的最小数字

    题目

    把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。

    示例 1:

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

    思路

    直接判断后一个数是否比第一个数小,如果小的话就是最小值;如果遍历整个数组都没有这样的值,那么第一个数就是最小的。

    代码

    class Solution {
    public:
        int minArray(vector<int>& numbers) {
            if(numbers.size()<=0)
            {
                return -1;
            }
            int num=numbers.size();
            int i;
            for( i=1;i<num;i++)
            {
                if(numbers[i]<numbers[i-1])
                {
                    return numbers[i];
                }
            }
            if(i==num)
            {
                return numbers[0];
            }
            return -1;
    
        }
    };
    

    12. 矩阵中的路径

    题目

    请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。

    [["a","b","c","e"],
    ["s","f","c","s"],
    ["a","d","e","e"]]

    但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。

    示例 1:

    输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
    输出:true

    思路

    这道题非常经典常见,可当作模板记住,很多类似的问题都可以使用同一方法。

    这道题使用回溯法:首先这道题并没有规定是左上角开始,所以可以判断每一个点适不适合作为匹配的开始,然后再依次判断她的四周,因为路径中不能有重复的点,所以每一步要标记,如果此条路径不通,就回溯,回溯时记得撤销标记;

    代码

    class Solution {
    public:
        bool exist(vector<vector<char>>& board, string word) {
            if(board.empty()||board[0].empty())
                return word.empty();
            int row=board.size();
            int col=board[0].size();
            for(int i=0;i<row;i++)
            {
                for(int j=0;j<col;j++)
                {
                    if(isExist(board,word,0,i,j))
                        return true;
                }
            }
            return false;
        }
        bool isExist(vector<vector<char>>& board,const string &word,int s,int a,int b)
        {
            if(s==word.size())    return true;
            if(a<0||a>=board.size()||b<0||b>=board[0].size())   return false;
            if(word[s]!=board[a][b])   return false;
            
            board[a][b]='*';
            if(isExist(board,word,s+1,a-1,b)||isExist(board,word,s+1,a+1,b)||
               isExist(board,word,s+1,a,b+1)||isExist(board,word,s+1,a,b-1))
                    return true;
            
            board[a][b]=word[s];
            return false;
        }
    };
    

    13. 机器人的运动范围

    题目

    地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

    示例 1:

    输入:m = 2, n = 3, k = 1
    输出:3

    思路

    这个题和第12题很相似,但是是计算可以走的格子的位置,可以用dfs,遍历可以走的位置,然后每到一个点,判断这个点是否符合规则,如果符合,则可以计入总数,但要注意不能重复计数。因此可以设置一个标记数组,走过的位置不再走,(因为走过的位置下一步dfs永远一样,如果不标记会无限循环);注意:与上题还有个不同点是,这一题的机器人只能从(0,0)开始;

    代码

    class Solution {
    public:
        int movingCount(int m, int n, int k) {
            if(k==0)
                return 1;
            vector<vector<int>> flag(m,vector<int>(n,0));
            isMoving(flag,0,0,k);  	
            int sum=0;
            for(int i=0;i<m;i++)
            {
                for(int j=0;j<n;j++)
                {
                    sum+=flag[i][j];
                }
            }
            return sum;
        }
        void isMoving(vector<vector<int>> &flag,int i,int j,int k)
        {
            if(i<0||i>=flag.size()||j<0||j>=flag[0].size())
                return;
            if((i % 10 + i / 10 + j % 10 + j / 10)>k)
                return;
            if(flag[i][j]==1)
                return;
    
            flag[i][j]=1;
            isMoving(flag,i,j+1,k);
            isMoving(flag,i,j-1,k);
            isMoving(flag,i+1,j,k);
            isMoving(flag,i-1,j,k);
        }
    };
    

    我采用的是计算标记的数量来计算总数,其他题解里是边走边计数,速度会更快一些。


    14- I. 剪绳子

    题目

    给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]k[1]...*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

    示例 1:

    输入: 2
    输出: 1
    解释: 2 = 1 + 1, 1 × 1 = 1

    思路

    显然是一个动态规划,和前面的上楼梯是一个类型;

    首先,n>=2,m>1表示绳子必须切,不能不减断,例如绳长为2时,输出值为1,而不是2;绳长为3时输出2;由于4开始有多种分法,所以我们以4开始进行动态规划式子讨论。可以对一根绳子从1开始遍历切割,切割的两部分分别取其最大值,然后相乘,如果超过了原来的切割最大值,则进行更新,否则不变;

    dp[i]=max(dp[i],dp[i-j]*dp[j]);
    

    对于初始值,dp[1];dp[2];dp[3]的定义十分重要,虽然它们直接返回时为1或2,但是在切割中(切割就证明最少已经有两段了,切下来的1、2、3长的绳子可以不必再分)他们的初值分别为1,2,3;

    代码

    class Solution {
    public:
        int cuttingRope(int n) {
            vector<int> dp(n+1,0);
            if(n==2)
                return 1;
            if(n==3)
                return 2;
    
            dp[1]=1;
            dp[2]=2;
            dp[3]=3;
            for(int i=4;i<=n;i++)
            {
                for(int j=1;j<=i/2;j++)
                {
                    dp[i]=max(dp[i],dp[i-j]*dp[j]);
                }
            }
            return dp[n];
        }
    };
    

    14- II. 剪绳子 II

    题目

    给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m - 1] 。请问 k[0]k[1]...*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

    答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

    示例 1:

    输入: 2

    输出: 1
    解释: 2 = 1 + 1, 1 × 1 = 1

    • 2 <= n <= 1000

    思路

    这一题与上一题的不同点在于数据更大,当需要取余的时候无法进行大小比较,所以上一题的方法无法使用。

    参考了评论区的解法:

    数论中的一些结论:

    1. 任何大于1的数都可由2和3相加组成(根据奇偶证明)
    2. 因为2*2=1*4,2*3>1*5, 所以将数字拆成2和3,能得到的积最大
    3. 因为2*2*2<3*3, 所以3越多积越大 时间复杂度O(n/3),用幂函数可以达到O(log(n/3)), 因为n不大,所以提升意义不大。 空间复杂度常数复杂度O(1)

    代码

    class Solution {
    public:
        int cuttingRope(int n) {
            vector<int> dp(n+1,0);
            if(n==2)
                return 1;
            if(n==3)
                return 2;
    
            dp[1]=1;
            dp[2]=2;
            dp[3]=3;
            for(int i=4;i<=n;i++)
            {
                for(int j=1;j<=i/2;j++)
                {
                    dp[i]=max(dp[i],dp[i-j]*dp[j]);
                }
            }
            return dp[n];
        }
    };
    

    15. 二进制中1的个数

    题目

    请实现一个函数,输入一个整数(以二进制串形式),输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。

    示例 1:

    输入:00000000000000000000000000001011
    输出:3
    解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。
    

    思路

    这一题很简单

    代码

    class Solution {
    public:
        int hammingWeight(uint32_t n) {
            int sum=0;
            for(int i=0;i<32;i++)
            {
                sum+=n%2;
                n=n>>1;
            }
            return sum;
        }
    };
    

    16. 数值的整数次方

    题目

    实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。

    示例 1:

    输入:x = 2.00000, n = 10
    输出:1024.00000
    

    示例 2:

    输入:x = 2.10000, n = 3
    输出:9.26100
    

    思路

    将n化为二进制(指计算过程中看作二进制),转化成x的平方运算,这样可以避免正负数分开运算,复数即使移位仍然是复数;递归法

    代码

    class Solution {
    public:
        double myPow(double x, int n) {
            if(n==0)
            {
                return 1;
            }
            if(n==-1)
            {
                return 1/x;
            }
            if(n&1) return myPow(x*x,n>>1)*x;
            else return myPow(x*x,n>>1);
    
        }
    };
    

    17. 打印从1到最大的n位数

    题目

    输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。

    示例 1:

    输入: n = 1
    输出: [1,2,3,4,5,6,7,8,9]

    说明:

    用返回一个整数列表来代替打印
    n 为正整数

    思路

    直接将数据放进去就可

    代码

    class Solution {
    public:
        vector<int> printNumbers(int n) {
            int num=10;
            for(int i=0;i<(n-1);i++)
            {
                num=num*10;
            }
            vector<int> a(num-1,0);
            for(int i=1;i<num;i++)
            {
                a[i-1]=i;
            }
            return a;
        }
    };
    

    18. 删除链表的节点

    题目

    给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

    返回删除后的链表的头节点。

    注意:此题对比原题有改动

    示例 1:

    输入: head = [4,5,1,9], val = 5
    输出: [4,1,9]
    解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
    

    示例 2:

    输入: head = [4,5,1,9], val = 1
    输出: [4,5,9]
    解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
    

    说明:

    题目保证链表中节点的值互不相同
    若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点

    思路

    如果是第一个节点就直接返回head->next,然后设置两个指针,一个指针遍历链表,一个指针标记正在遍历节点的上一个节点,当遇到需要删除的目标节点时,就使用标记指针和遍历指针删除。

    代码

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        ListNode* deleteNode(ListNode* head, int val) {
            ListNode* newhead=head;
            ListNode* l=head;
            ListNode* n=l->next;
            if(l->val==val)
            {
                newhead=n;
                return newhead;
            }
            while(n->val!=val&&n->next!=NULL){
                l=n;
                n=n->next;
            }
            if(n->next==NULL&&n->val!=val)
                return newhead;
            else if(n->next==NULL&&n->val==val)
            {
                n=NULL;
                l->next=NULL;
                return newhead;
            }
            else
            {
                n=n->next;
                l->next=n;
                return newhead;
            }
        }
    };
    

  • 相关阅读:
    小程序swiper组件实现间距轮播
    小程序form静态页面跳转
    批量添加Iconfont图标库图标
    Vant Weapp 有赞小程序UI库 ICON 组件的本地图标路径支持
    $rootScope、$apply、$watch
    EF code first 数据模型创建数据库
    angularjs directive2
    angularjs directive
    angularjs service
    angular repeat
  • 原文地址:https://www.cnblogs.com/wwj321/p/14526105.html
Copyright © 2020-2023  润新知