2022-01-24每日刷题打卡

2022-01-24每日刷题打卡

飞书——每日一题

232. 用栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):

实现 MyQueue 类:

void push(int x) 将元素 x 推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek() 返回队列开头的元素
boolean empty() 如果队列为空,返回 true ;否则,返回 false

说明:

你只能使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

进阶:

你能否实现每个操作均摊时间复杂度为 O(1) 的队列?换句话说,执行 n 个操作的总时间复杂度为 O(n) ,即使其中一个操作可能花费较长时间。

示例:

输入:
[“MyQueue”, “push”, “push”, “peek”, “pop”, “empty”]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]

解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false

提示:

1 <= x <= 9
最多调用 100 次 push、pop、peek 和 empty
假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)

准备两个栈,一个入栈insta,一个出栈outsta,当有数插入时,就把数插入到insta中,当要将数出栈时,先判断outsta是否为空,如果为空,就把insta的所有元素都出栈并同属插入到ousta中,比如说:insta的元素顺序现在是1 2 3 4 5,此时5是栈顶元素,1是最先进入的元素,我们把这些元素按照顺序出栈并入到outsta后,顺序是:5 4 3 2 1,此时1就是栈顶元素,我们把1出栈即可。而且1出栈后,下一个栈顶元素就是2,正好对应队列的顺序,返回队列头元素时,也可以直接返回outsta的栈顶元素。但要注意,outsta为空时才要把insta出栈,不为空时outsta直接出栈即可。至于判断队列是否为空,可以直接计算两个栈的元素之和看是否为0,也可以一开始就准备一个全局变量ans=0,进行入栈操作时,ans++,出栈操作时,ans–。那么,只要ans不为0,就说明还有元素在队列里,队列不为空,反之为空。

class MyQueue {
public:
    stack<int>insta,outsta;
    int ans=0;
    MyQueue() {

    }
    
    void push(int x) {
        insta.push(x);
        ans++;
    }
    
    int pop() {
        if(outsta.size()==0)
        {
            while(insta.size())
            {
                outsta.push(insta.top());
                insta.pop();
            }
        }
        ans--;
        int a=outsta.top();
        outsta.pop();
        return a;
    }
    
    int peek() {
        if(outsta.size()==0)
        {
            while(insta.size())
            {
                outsta.push(insta.top());
                insta.pop();
            }
        }
        return outsta.top();
    }
    
    bool empty() {
        return ans==0;
    }
};

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue* obj = new MyQueue();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->peek();
 * bool param_4 = obj->empty();
 */
279. 完全平方数

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

给你一个整数 n ,返回和为 n 的完全平方数的 最少数量 。

完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。

示例 1:

输入:n = 12
输出:3
解释:12 = 4 + 4 + 4

原本写的是广度搜索的专题,但好像动态规划写这题更简洁一点。

准备一个大小为n+1的数组dp,状态定义为,dp[i]代表数字i的完全平方数最少数量为dp[i],比如说,dp[1],就代表数字1的完全平方数最少可以是dp[1],因为1就是完全平方数,所以知道了是1,所以数字1的完全平方数最少就是1。

那么该怎么算呢,我们可以由题意得出一个规律:如果一个数和另一个数的差值正好是一个完全平方数,那么这两个数的完全平方数最少个数相差为1,比如数字1和5,差值是4,是一个完全平方数,所有数字5所需要的最少完全平方数为1所需要的完全平方数+1,即dp[5]=d[1]+1=2;这就是我们的解法,遍历n个数,每遍历一个数,找在这个数范围内的完全平方数,比如5,范围内最小的完全平方数就是4,然后计算dp[5-4]+1,就得到结果2了,当然这也是一个遍历的过程,在寻找完全平方数的过程中维护下最小值。比如12,最大的完全平方数是9,即dp[12]=dp[12-9]+1=4,但测试例已经告诉我们了最少应该是3,所以这个不是最小值,然后我们找下一个完全平方数,4 ,即dp[12]=dp[12-4]+1=dp[8]+1,(8是两个4相加,即最少数为2),这时dp[12]=3,得到了最小值,所以我们在找完全平方数的过程中记得要维护最小值,最后再记录在dp[i]上。

class Solution {
public:
    int numSquares(int n) {
        vector<int>dp(n+1);
        for(int i=1;i<=n;i++)
        {
            int min_num=INT_MAX;
            for(int j=1;j*j<=i;j++)
            {
                min_num=min(min_num,dp[i-j*j]+1);
            }
            dp[i]=min_num;
        }
        return dp[n];
    }
};
322. 零钱兑换

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1

方法和上题差不多,区别就是上题的完全平方数是自己算出来的,这题的“完全平方数”是存在数组里给你的。

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int>dp(amount+1,amount+1);
        int n=coins.size();
        dp[0]=0;
        for(int i=1;i<=amount;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(coins[j]-i<=0)
                {
                    dp[i]=min(dp[i],dp[i-coins[j]]+1);
                }
            }
        }
        return dp[amount]>amount?-1:dp[amount];
    }
};
130. 被围绕的区域

给你一个 m x n 的矩阵 board ,由若干字符 ‘X’ 和 ‘O’ ,找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。

示例 1:

xogrid.jpg (773×333) (leetcode.com)

输入:board = [[“X”,“X”,“X”,“X”],[“X”,“O”,“O”,“X”],[“X”,“X”,“O”,“X”],[“X”,“O”,“X”,“X”]]
输出:[[“X”,“X”,“X”,“X”],[“X”,“X”,“X”,“X”],[“X”,“X”,“X”,“X”],[“X”,“O”,“X”,“X”]]
解释:被围绕的区间不会存在于边界上,换句话说,任何边界上的 ‘O’ 都不会被填充为 ‘X’。 任何不在边界上,或不与边界上的 ‘O’ 相连的 ‘O’ 最终都会被填充为 ‘X’。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。

先遍历周围最外围的四个边,看有没有‘O’,如果有,就广度搜索把它和它联通的所有’O’字符都改为字符‘A’,然后遍历所有的点,看哪个点是’O’,以那个点为起点把它和它相邻的所有‘O’字符都改为’X’。最后,把之前改为‘A’的字符都改回’O’。

class Solution {
public:
    int n,m;
    typedef pair<int,int>PII;
    PII que[100000];
    int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
    void solve(vector<vector<char>>& board) {
        n=board.size(),m=board[0].size();
        for(int i=0;i<n;i++)
        {
            if(board[i][0]=='O')
            {
                bfs(i,0,'O','A',board);
            }
            if(board[i][m-1]=='O')
            {
                bfs(i,m-1,'O','A',board);
            }
        }
        for (int i = 0; i < m; i++)
        {
            if (board[0][i] == 'O')
            {
                bfs(0, i, 'O', 'A', board);
            }
            if (board[n-1][i] == 'O')
            {
                bfs(n-1, i, 'O', 'A', board);
            }
        }
        for(int i=1;i<n;i++)
            for(int j=1;j<m;j++)
                if(board[i][j]=='O')
                {
                    bfs(i,j,'O','X',board);
                }
        for(int i=0;i<n;i++)
        {
            if(board[i][0]=='A')
            {
                bfs(i,0,'A','O',board);
            }
            if(board[i][m-1]=='A')
            {
                bfs(i,m-1,'A','O',board);
            }
        }
        for (int i = 0; i < m; i++)
        {
            if (board[0][i] == 'A')
            {
                bfs(0, i, 'A', 'O', board);
            }
            if (board[n-1][i] == 'A')
            {
                bfs(n-1, i, 'A', 'O', board);
            }
        }
    }
    void bfs(int x,int y,char s,char c,vector<vector<char>>& board)
    {
        board[x][y]=c;
        que[0]={x,y};
        int hh=0,tt=0;
        while(hh<=tt)
        {
            auto t=que[hh++];
            for(int i=0;i<4;i++)
            {
                int a=t.first+dx[i],b=t.second+dy[i];
                if(a>=0&&a<n&&b>=0&&b<m&&board[a][b]==s)
                {
                    board[a][b]=c;
                    que[++tt]={a,b};
                }
            }
        }
    }
};
剑指 Offer 67. 把字符串转换成整数

写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。

首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。

当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。

该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。

注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。

在任何情况下,若函数不能进行有效的转换时,请返回 0。

说明:

假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。

示例 1:

输入: “42”
输出: 42

此题注意点就是:首位非空格非符号字符是不是数字字符,不是就返回0;符号字符后面的字符是不是数字字符,不是返回0;数转化后是否会超出int类型的范围,如果小于INT_MIN就返回-2147483648,大于INT_MAX就返回2147483647。

先判断一下字符串是否为空,空就返回0,然后预先遍历一下str,把前面的空格都跳过,然后判断第一个非空字符是不是符号,准备一个数ans=1,如果符号为符号,ans=-1,为正号不变,为字母返回0。然后把后面连着的的数字字符保存下来(注意,如果是“42 212”这样的字符,只要42,后面的212不要),然后准备一个long long 类型变量num=0,把字符串一步步转化成数字保存在num上,每次判断一下num是否超出区间,如果超出了就返回相应的最大值。等转换结束后,返回ans*num。

class Solution {
public:
    int strToInt(string str) {
        int n=str.size();
        if(n==0)return 0;
        string s;
        int i=0,ans=1;
        while(i<n&&str[i]==' ')
            i++;
        if(i>=n)return 0;
        else if(str[i]=='+')
        {
            ans=1;
            i++;
        }
        else if(str[i]=='-')
        {
            ans=-1;
            i++;
        }
        else if((str[i]>='a'&&str[i]<='z')||(str[i]>='A'&&str[i]<='Z'))return 0;
        while(i<n)
        {
            if(str[i]>='0'&&str[i]<='9')s+=str[i];
            else break;
            i++;
        }
        long long num=0;
        for(int j=0;j<s.size();j++)
        {
            num*=10;
            num+=s[j]-'0';
            if(num>2147483647&&ans==1)return 2147483647;
            else if(num>2147483648&&ans==-1)return -2147483648;
        }
          
        return ans*num;
    }

};
剑指 Offer 14- I. 剪绳子343. 整数拆分

给你一根长度为 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

由于两题一样就放一起讲了。

数论的解法,证明也不知道,反正就是尽量把数分成2和3,特别是3,在只有2和3的情况下3越多这个数越大,比如数字6可以分成2+2+2或者3+3,3*3>2 *2 *2。但注意是只有2和3的情况,如果是4你分成3+1那反而比2+2小。还有就是2和3可以组成任何大于1的数,因为2是最小的偶数,3是除1最小的奇数了。

class Solution {
public:
    int integerBreak(int n) {
        while(n<4)
            return n-1;
        int ans=1;
        while(n>4)
        {
            ans*=3;
            n-=3;
        }
        return n*ans;
    }
};
剑指 Offer 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

铜上题,只不过要取模,对应的,数要开到long long 。

class Solution {
public:
    int cuttingRope(int n) {
        while(n<4)
            return n-1;
        long long ans=1;
        while(n>4)
        {
            ans*=3;
            ans%=1000000007;
            n-=3;
        }
        return ans*n%1000000007;
    }
};
404. 左叶子之和

计算给定二叉树的所有左叶子之和。

示例:

	3
   / \
  9  20
    /  \
   15   7

在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24

注意,他说的是左叶子不是左节点。

深搜,准备一个全局变量ans=0,每次判断当前节点是否为空,为空就直接return结束程序。再判断当前节点是否有左节点,而且它的左节点是否是叶子节点(无左右子节点)如果是,就把值左节点的值加到ans上。再把当前节点的左右节点送去下一次深搜。最后返回ans即可。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int ans=0;
    int sumOfLeftLeaves(TreeNode* root) {
        dfs(root);
        return ans;
    }
    void dfs(TreeNode *root)
    {
        if(!root)return;
        if(root->left&&!root->left->left&&!root->left->right)ans+=root->left->val;
        dfs(root->left);
        dfs(root->right);
    }
};
463. 岛屿的周长

给定一个 row x col 的二维网格地图 grid ,其中:grid[i][j] = 1 表示陆地, grid[i][j] = 0 表示水域。

网格中的格子 水平和垂直 方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。

岛屿中没有“湖”(“湖” 指水域在岛屿内部且不和岛屿周围的水相连)。格子是边长为 1 的正方形。网格为长方形,且宽度和高度均不超过 100 。计算这个岛屿的周长。

示例 1:

island.png (221×213) (leetcode-cn.com)

输入:grid = [[0,1,0,0],[1,1,1,0],[0,1,0,0],[1,1,0,0]]
输出:16
解释:它的周长是上面图片中的 16 个黄色的边
示例 2:

输入:grid = [[1]]
输出:4

广度搜索,在走迷宫的时候多加一个判断语句。先准备一个计数器ans=0,用来计算一共有几个墙壁(图中黄色线),遍历矩阵,找到值为1的点,以那个点为起点进行广度搜索。在搜索中,把和起点联通的点都由1改为0,然后在我们走的时候,如果遇到墙壁,ans++。那么什么是墙壁,即数组越界、要走的下一个地方数值为0这两个条件。但有一点,我们要记录走过的地方,因为走过的地方会被改成0,我们在一个地块往上下左右四个方向探索时,有可能走到刚刚走过的地块,此时那个地块也变成了0,这样我们计算墙壁数量时会错误的+1,所以我们把走过的地块都标记,当我们的下一步超出矩阵大小、或者是下一步的地块为0且我们没走过时,计数器ans++。最后返回ans即可。

class Solution {
public:
    typedef pair<int,int>PII;
    PII que[20000];
    int ans=0,n,m;
    int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
    int islandPerimeter(vector<vector<int>>& grid) {
        n=grid.size();
        m=grid[0].size();
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
                if(grid[i][j]==1)
                {
                    vector<vector<int>>v(n,vector<int>(m,0));
                    bfs(i,j,grid,v);
                }
        return ans;
    }
    void bfs(int x,int y,vector<vector<int>>& grid,vector<vector<int>>& v)
    {
        grid[x][y]=0;
        v[x][y]=1;
        que[0]={x,y};
        int hh=0,tt=0;
        while(hh<=tt)
        {
            auto t=que[hh++];
            for(int i=0;i<4;i++)
            {
                int a=t.first+dx[i],b=t.second+dy[i];
                if(a>=0&&a<n&&b>=0&&b<m&&grid[a][b]==1)
                {
                    que[++tt]={a,b};
                    grid[a][b]=0;
                    v[a][b]=1;
                }
                else if ((a >= n || a < 0) || (b >= m || b < 0) || (grid[a][b] == 0 && v[a][b] == 0))
                    ans++;
            }
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值