【题解】—— LeetCode一周小结1

1.经营摩天轮的最大利润

题目链接: 1599. 经营摩天轮的最大利润

你正在经营一座摩天轮,该摩天轮共有 4 个座舱 ,每个座舱 最多可以容纳 4 位游客 。你可以 逆时针 轮转座舱,但每次轮转都需要支付一定的运行成本 runningCost 。摩天轮每次轮转都恰好转动 1 / 4 周。

给你一个长度为 n 的数组 customerscustomers[i] 是在第 i 次轮转(下标从 0 开始)之前到达的新游客的数量。这也意味着你必须在新游客到来前轮转 i 次。每位游客在登上离地面最近的座舱前都会支付登舱成本 boardingCost ,一旦该座舱再次抵达地面,他们就会离开座舱结束游玩。

你可以随时停下摩天轮,即便是 在服务所有游客之前 。如果你决定停止运营摩天轮,为了保证所有游客安全着陆,将免费进行所有后续轮转 。注意,如果有超过 4 位游客在等摩天轮,那么只有 4 位游客可以登上摩天轮,其余的需要等待 下一次轮转 。

返回最大化利润所需执行的 最小轮转次数 。 如果不存在利润为正的方案,则返回 -1

示例 1:
在这里插入图片描述

输入:customers = [8,3], boardingCost = 5, runningCost = 6
输出:3
解释:座舱上标注的数字是该座舱的当前游客数。

  1. 8 位游客抵达,4 位登舱,4 位等待下一舱,摩天轮轮转。当前利润为 4 * $5 - 1 * $6 = $14 。
  2. 3 位游客抵达,4 位在等待的游客登舱,其他 3 位等待,摩天轮轮转。当前利润为 8 * $5 - 2 * $6 = $28 。
  3. 最后 3 位游客登舱,摩天轮轮转。当前利润为 11 * $5 - 3 * $6 = $37 。 轮转 3 次得到最大利润,最大利润为 $37 。

示例 2:

输入:customers = [10,9,6], boardingCost = 6, runningCost = 4
输出:7
解释:

  1. 10 位游客抵达,4 位登舱,6 位等待下一舱,摩天轮轮转。当前利润为 4 * $6 - 1 * $4 = $20 。
  2. 9 位游客抵达,4 位登舱,11 位等待(2 位是先前就在等待的,9 位新加入等待的),摩天轮轮转。当前利润为 8 * $6 - 2 * $4 = $40 。
  3. 最后 6 位游客抵达,4 位登舱,13 位等待,摩天轮轮转。当前利润为 12 * $6 - 3 * $4 = $60 。
  4. 4 位登舱,9 位等待,摩天轮轮转。当前利润为 * $6 - 4 * $4 = $80 。
  5. 4 位登舱,5 位等待,摩天轮轮转。当前利润为 20 * $6 - 5 * $4 = $100 。
  6. 4 位登舱,1 位等待,摩天轮轮转。当前利润为 24 * $6 - 6 * $4 = $120 。
  7. 1 位登舱,摩天轮轮转。当前利润为 25 * $6 - 7 * $4 = $122 。 轮转 7 次得到最大利润,最大利润为$122 。

示例 3:

输入:customers = [3,4,0,5,1], boardingCost = 1, runningCost = 92
输出:-1
解释:

  1. 3 位游客抵达,3 位登舱,0 位等待,摩天轮轮转。当前利润为 3 * $1 - 1 * $92 = -$89 。
  2. 4 位游客抵达,4 位登舱,0 位等待,摩天轮轮转。当前利润为 7 * $1 - 2 * $92 = -$177 。
  3. 0 位游客抵达,0 位登舱,0 位等待,摩天轮轮转。当前利润为 7 * $1 - 3 * $92 = -$269 。
  4. 5 位游客抵达,4 位登舱,1 位等待,摩天轮轮转。当前利润为 11 * $1 - 4 * $92 = -$357 。
  5. 1 位游客抵达,2 位登舱,0 位等待,摩天轮轮转。当前利润为 13 * $1 - 5 * $92 = -$447 。 利润永不为正,所以返回 -1 。

提示:

n == customers.length

1 <= n <= 105

0 <= customers[i] <= 50

1 <= boardingCost, runningCost <= 100

题解

        新年第一天想到头痛

        看了半天题干里面 随时可以停下,免费进行后续 原来是指游客上摩天轮以后就不用管他们了…

方法1模拟

        我们可以直接模拟摩天轮的轮转过程,每次轮转时,累加等待的游客以及新到达的游客,然后最多 4 个人上船,更新等待的游客数和利润,记录最大利润与其对应的轮转次数。

class Solution {
    public int minOperationsMaxProfit(int[] customers, int boardingCost, int runningCost) {
        int ans = -1; // 初始化答案为-1,表示没有找到最大利润的方案
        int max = 0, t = 0; // 初始化最大利润为0,当前总利润为0
        int wait = 0, i = 0; // 初始化等待人数为0,轮转次数为0
        // 当等待人数大于0或者还有客户需要服务时,继续循环
        while (wait > 0 || i < customers.length) { 
            // 如果还有游客,将等待人数加入等待游客的人数
            wait += i < customers.length ? customers[i] : 0; 
            int up = Math.min(4, wait); // 计算可以上车的人数,最多为4人
            wait -= up; // 更新等待人数
            i++; // 轮转次数
            t += up * boardingCost - runningCost; // 更新当前总利润
            if (t > max) { // 如果当前总利润大于最大利润
                max = t; // 更新最大利润
                ans = i; // 更新答案为当前的轮转次数
            }
        }
        return ans; // 返回答案
    }
}

方法2贪心

        维护变量 time 代表转动的次数,那么成本就是 cost = time * runningCost;

        维护变量 customer 代表当前有多少等在上摩天轮的游客数量;

        枚举 customers[i] 那么当前 下标i 可以看做第几轮转动前的状态,那么更新 customer += customers[i] 得到当前等待的游客数量,由于存在大量游客(大于等于4)的可能,那么我们就可以直接得出,当前的等待游客如果 “尽量凑4人” 需要转多少轮,也就是更新 time += customer // 4 ,假设预计完成的轮次 time 大于当前 下标i 那么很明显,我们可以等后面游客过来补充 “填数”,如此形成循环。这个循环停止,代表当前游客不能再凑齐 4 人了,而且必须启动转一下,那么直接计算转一下把 “剩余游客” 接上。最后外循环判断还有没有下一轮游客过来即可;

        整个过程记录其最大利润并记录这个例如出现时的 time 即为答案;

        ps:计算如果直接用除法、乘法和余法会非常慢,可以直接位运算加速

class Solution {
    public int minOperationsMaxProfit(int[] customers, int boardingCost, int runningCost) {
        // 如果每次摩天轮的载客量乘以4小于等于运行成本,则无法盈利,返回-1
        if (boardingCost * 4 <= runningCost) return -1;
        
        int customer = 0, time = 0, income = 0, bestRes = 0, bestTime = -1;
        
        for (int i = 0; i < customers.length; ) {
            // 计算当前轮次前是否有新的游客加入,如果之前存在游客,尽量凑 4 人
            for (; i < customers.length && i <= time; i++) {
                customer += customers[i]; // 累加游客人数
                income += (customer >> 2 << 2) * boardingCost; // 计算收入
                time += customer >> 2; // 更新轮转次数
                customer &= 3; // 更新剩余游客人数
            }
            // 上轮循环跳出证明当前出现无游客凑够 4 人的情况,所以先核算之前满载 4 人的最优情况下利润是否创新高
            if (bestRes < income - runningCost * time) {
                bestRes = income - runningCost * time; // 更新最佳利润
                bestTime = time; // 更新轮转次数
            }
            // 到这里证明游客少于 4 人,且必须转动,那么就直接计算并判断
            income += boardingCost * customer; // 计算收入
            customer = 0; // 更新剩余游客人数
            time++; // 更新轮转次数
            if (bestRes < income - runningCost * time) {
                bestRes = income - runningCost * time; // 更新最佳利润
                bestTime = time; // 更新轮转次数
            }
        }

        return bestTime; // 返回轮转次数
    }
}

2.统计重复个数

题目链接:466. 统计重复个数

定义 str = [s, n] 表示 strn 个字符串 s 连接构成。

  • 例如,str == ["abc", 3] =="abcabcabc"

如果可以从 s2 中删除某些字符使其变为 s1,则称字符串 s1 可以从字符串 s2 获得。

  • 例如,根据定义,s1 = "abc" 可以从 s2 = "abdbec" 获得,仅需要删除加粗且用斜体标识的字符。

现在给你两个字符串 s1s2 和两个整数 n1n2 。由此构造得到两个字符串,其中 str1 = [s1, n1]str2 = [s2, n2]

请你找出一个最大整数 m ,以满足 str = [str2, m] 可以从 str1 获得。

示例 1:

输入:s1 = “acb”, n1 = 4, s2 = “ab”, n2 = 2
输出:2

示例 2:

输入:s1 = “acb”, n1 = 1, s2 = “acb”, n2 = 1
输出:1

提示:

1 <= s1.length, s2.length <= 100

s1 和 s2 由小写英文字母组成

1 <= n1, n2 <= 106

题解

方法(来自官方题解):找出循环节(个人觉得太麻烦)
思路

        由于题目中的 n1 和 n2 都很大,因此我们无法真正把 S1 = [s1, n1] 和 S2 = [s2, n2] 都显式地表示出来。由于这两个字符串都是不断循环的,因此我们可以考虑找出 S2 在 S1 中出现的循环节,如果我们找到了循环节,那么我们就可以很快算出 S2 在 S1 中出现了多少次了。

        有些读者可能对循环节这个概念会有些陌生,这个概念我们可以类比无限循环小数,如果从小数部分的某一位起向右进行到某一位止的一节数字「循环」出现,首尾衔接,称这种小数为「无限循环小数」,这一节数字称为「无限循环小数」。比如对于 3.56789789789… 这个无限循环小数,它的小数部分就是以 789 为一个「循环节」在无限循环,且开头可能会有部分不循环的部分,这个数字中即为 56。

        那么回到这题,我们可以将不断循环的 s2 组成的字符串类比作上面小数部分,去找是否存在一个子串,即「循环节」,满足不断在 S2 中循环,且这个循环节能对应固定数量的 s1 。如下图所示,在第一次出现后,S2 的子串 bdadc 构成一个循环节:之后 bdadc 的每次出现都需要有相应的两段 s1。
在这里插入图片描述
        当我们找出循环节后,我们即可知道一个循环节内包含 s1 的数量,以及在循环节出现前的 s1 的数量,这样就可以在 O(1)的时间内,通过简单的运算求出 s2 在 S1 中出现的次数了。当然,由于 S1 中 s1 的数量 n1 是有限的,因此可能会存在循环节最后一个部分没有完全匹配,如上图最后会单独剩一个 s1 出来无法完全匹配完循环节,这部分我们需要单独拿出来遍历处理统计。

        有些读者可能会怀疑循环节是否一定存在,这里我们给出的答案是肯定的,根据鸽笼原理,我们最多只要找过 |s2| + 1 个 s1,就一定会出现循环节。

class Solution {
    public int getMaxRepetitions(String s1, int n1, String s2, int n2) {
        if (n1 == 0) {
            return 0;
        }
        int s1cnt = 0, index = 0, s2cnt = 0;
        // recall 是我们用来找循环节的变量,它是一个哈希映射
        // 我们如何找循环节?假设我们遍历了 s1cnt 个 s1,此时匹配到了第 s2cnt 个 s2 中的第 index 个字符
        // 如果我们之前遍历了 s1cnt' 个 s1 时,匹配到的是第 s2cnt' 个 s2 中同样的第 index 个字符,那么就有循环节了
        // 我们用 (s1cnt', s2cnt', index) 和 (s1cnt, s2cnt, index) 表示两次包含相同 index 的匹配结果
        // 那么哈希映射中的键就是 index,值就是 (s1cnt', s2cnt') 这个二元组
        // 循环节就是;
        //    - 前 s1cnt' 个 s1 包含了 s2cnt' 个 s2
        //    - 以后的每 (s1cnt - s1cnt') 个 s1 包含了 (s2cnt - s2cnt') 个 s2
        // 那么还会剩下 (n1 - s1cnt') % (s1cnt - s1cnt') 个 s1, 我们对这些与 s2 进行暴力匹配
        // 注意 s2 要从第 index 个字符开始匹配
        Map<Integer, int[]> recall = new HashMap<Integer, int[]>();
        int[] preLoop = new int[2];
        int[] inLoop = new int[2];
        while (true) {
            // 我们多遍历一个 s1,看看能不能找到循环节
            ++s1cnt;
            for (int i = 0; i < s1.length(); ++i) {
                char ch = s1.charAt(i);
                if (ch == s2.charAt(index)) {
                    index += 1;
                    if (index == s2.length()) {
                        ++s2cnt;
                        index = 0;
                    }
                }
            }
            // 还没有找到循环节,所有的 s1 就用完了
            if (s1cnt == n1) {
                return s2cnt / n2;
            }
            // 出现了之前的 index,表示找到了循环节
            if (recall.containsKey(index)) {
                int[] value = recall.get(index);
                int s1cntPrime = value[0];
                int s2cntPrime = value[1];
                // 前 s1cnt' 个 s1 包含了 s2cnt' 个 s2
                preLoop = new int[]{s1cntPrime, s2cntPrime};
                // 以后的每 (s1cnt - s1cnt') 个 s1 包含了 (s2cnt - s2cnt') 个 s2
                inLoop = new int[]{s1cnt - s1cntPrime, s2cnt - s2cntPrime};
                break;
            } else {
                recall.put(index, new int[]{s1cnt, s2cnt});
            }
        }
        // ans 存储的是 S1 包含的 s2 的数量,考虑的之前的 preLoop 和 inLoop
        int ans = preLoop[1] + (n1 - preLoop[0]) / inLoop[0] * inLoop[1];
        // S1 的末尾还剩下一些 s1,我们暴力进行匹配
        int rest = (n1 - preLoop[0]) % inLoop[0];
        for (int i = 0; i < rest; ++i) {
            for (int j = 0; j < s1.length(); ++j) {
                char ch = s1.charAt(j);
                if (ch == s2.charAt(index)) {
                    ++index;
                    if (index == s2.length()) {
                        ++ans;
                        index = 0;
                    }
                }
            }
        }
        // S1 包含 ans 个 s2,那么就包含 ans / n2 个 S2
        return ans / n2;
    }
}

方法:递归
        我们预处理出以字符串 s2 的每个位置 i 开始匹配一个完整的 s1 后,下一个位置 jjj 以及经过了多少个 s2,即 d[i]=(cnt,j),其中 cnt 表示匹配了多少个 s2,而 j 表示字符串 s2 的下一个位置。

        接下来,我们初始化 j=0,然后循环 n1 次,每一次将 d[j][0] 加到答案中,然后更新 j=d[j][1]。

        最后得到的答案就是 n1 个 s1所能匹配的 s2 的个数,除以 n2 即可得到答案。

class Solution {
    public int getMaxRepetitions(String s1, int n1, String s2, int n2) {
        int len1 = s1.length();
        int len2 = s2.length();
        int[][] DP = new int[len2][0]; // 0 记录s2匹配的个数 1 记录下一个s2匹配的位置
        for (int i = 0; i < len2; i++) {
            int j = i; // 记录下一个s2匹配的位置
            int cnt = 0; // 记录s2匹配的个数: 一个s1中可能含有多个s2串
            for (int k = 0; k < len1; k++) { // 遍历s1字符串进行匹配
                if (s1.charAt(k) == s2.charAt(j)) { // 匹配相同的字符串
                    j++; // 指向下一个位置
                    if (j == len2) { // s2匹配完一段
                        j = 0; // 重置索引
                        cnt++; // 匹配个数加1
                    }
                }
            }
            // 循环结束,j的位置始终对标在s1中的位置
            DP[i] = new int[]{cnt, j}; // 匹配完一次 则将结果添加到数组中
        }
        int ans = 0; // 定义结果变量
        for (int j = 0; n1 > 0; n1--) { // 遍历n1,记录每个循环中s2下标出现的数量
            ans += DP[j][0]; // 累加匹配数
            j = DP[j][1]; // 获取下一个位置
        }
        return ans / n2; // 将n2个s2作为一个整体数量,砍掉n2个就是结果
    }
}

3.从链表中移除节点

题目链接:2487. 从链表中移除节点

给你一个链表的头节点 head 。

移除每个右侧有一个更大数值的节点。

返回修改后链表的头节点 head 。

示例 1:
在这里插入图片描述

输入:head = [5,2,13,3,8]
输出:[13,8]
解释:需要移除的节点是 5 ,2 和 3 。

  • 节点 13 在节点 5 右侧。
  • 节点 13 在节点 2 右侧。
  • 节点 8 在节点 3 右侧。

示例 2:

输入:head = [1,1,1,1]
输出:[1,1,1,1]
解释:每个节点的值都是 1 ,所以没有需要移除的节点。

提示:

给定列表中的节点数目在范围 [1, 105] 内
1 <= Node.val <= 105

题解

方法:递归
        由题意可知,节点对它右侧的所有节点都没有影响,因此我们可以对它的右侧节点递归地进行移除操作:

  • 该节点为空,那么递归函数返回空指针。

  • 该节点不为空,那么先对它的右侧节点进行移除操作,得到一个新的子链表,如果子链表的表头节点值大于该节点的值,那么移除该节点,否则将该节点作为子链表的表头节点,最后返回该子链表。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNodes(ListNode head) {
        if (head == null) {
            return null;
        }
        head.next = removeNodes(head.next);
        if (head.next != null && head.val < head.next.val) {
            return head.next;
        } else {
            return head;
        }
    }
}

4.被列覆盖的最多行数

题目链接:2397. 被列覆盖的最多行数

给你一个下标从 0 开始、大小为 m x n 的二进制矩阵 matrix ;另给你一个整数 numSelect,表示你必须从 matrix 中选择的 不同 列的数量。

如果一行中所有的 1 都被你选中的列所覆盖,则认为这一行被 覆盖 了。

形式上,假设 s = {c1, c2, ...., cnumSelect} 是你选择的列的集合。对于矩阵中的某一行 row ,如果满足下述条件,则认为这一行被集合 s 覆盖:

  • 对于满足 matrix[row][col] == 1 的每个单元格 matrix[row][col]0 <= col <= n - 1),col 均存在于 s 中,或者
  • row不存在 值为 1 的单元格。
    你需要从矩阵中选出 numSelect 个列,使集合覆盖的行数最大化。

返回一个整数,表示可以由 numSelect 列构成的集合 覆盖最大行数

示例 1:
在这里插入图片描述

输入:matrix = [[0,0,0],[1,0,1],[0,1,1],[0,0,1]], numSelect = 2
输出:3
解释:
图示中显示了一种覆盖 3 行的可行办法。 选择 s = {0, 2} 。

  • 第 0 行被覆盖,因为其中没有出现 1 。
  • 第 1 行被覆盖,因为值为 1 的两列(即 0 和 2)均存在于 s 中。
  • 第 2 行未被覆盖,因为 matrix[2][1] == 1 但是 1 未存在于 s 中。
  • 第 3 行被覆盖,因为 matrix[2][2] == 1 且 2 存在于 s 中。 因此,可以覆盖 3 行。 另外 s = {1, 2} 也可以覆盖 3 行,但可以证明无法覆盖更多行。

示例 2:
在这里插入图片描述

输入:matrix = [[1],[0]], numSelect = 1
输出:2
解释:
选择唯一的一列,两行都被覆盖了,因为整个矩阵都被覆盖了。 所以我们返回 2 。

提示:

m == matrix.length
n == matrix[i].length
1 <= m, n <= 12
matrix[i][j]要么是 0 要么是 1
1 <= numSelect <= n

题解
方法:二进制枚举

        我们先将矩阵中的每一行转换成一个二进制数,记录在数组 rows中,其中 rows[i]表示第 i 行对应的二进制数,而 rows[i] 这个二进制数的第 j 位表示矩阵第 i 行第 j 列的值。

        接下来,我们枚举所有的 2n2 种列选择方案,其中 n 为矩阵的列数。对于每一种列选择方案,我们判断是否选中了 numSelect列,如果不是,则跳过。否则,我们统计矩阵中有多少行中的所有 1 都被选中的列覆盖,即统计有多少行的二进制数 rows[i]与列选择方案 mask 按位与的结果等于 rows[i],并更新最大的行数。

class Solution {
    public int maximumRows(int[][] matrix, int numSelect) {
        // 获取矩阵的行数m和列数n
        int m = matrix.length, n = matrix[0].length;
        // 定义一个长度为m的一维数组rows,用于存储每一行的二进制表示
        int[] rows = new int[m];
        // 遍历矩阵的每一行
        for (int i = 0; i < m; ++i) {
            // 遍历矩阵的每一列
            for (int j = 0; j < n; ++j) {
                // 如果当前位置的元素值为1,则将该位置对应的二进制位设置为1
                if (matrix[i][j] == 1) {
                    rows[i] |= 1 << j;
                }
            }
        }
        // 初始化答案ans为0
        int ans = 0;
        // 遍历所有可能的二进制掩码(从1到2^n-1)
        for (int mask = 1; mask < 1 << n; ++mask) {
            // 如果当前掩码中1的个数不等于numSelect,则跳过当前循环
            if (Integer.bitCount(mask) != numSelect) {
                continue;
            }
            // 初始化计数器t为0
            int t = 0;
            // 遍历每一行的二进制表示
            for (int x : rows) {
                // 如果当前行的二进制表示与当前掩码进行按位与运算后的结果等于当前行的二进制表示,则计数器t加1
                if ((x & mask) == x) {
                    ++t;
                }
            }
            // 更新答案ans为ans和t中的较大值
            ans = Math.max(ans, t);
        }
        // 返回答案ans
        return ans;
    }
}


5.队列中可以看到的人数

题目链接:1944. 队列中可以看到的人数

有 n 个人排成一个队列,从左到右 编号为 0 到 n - 1 。给你以一个整数数组 heights ,每个整数 互不相同,heights[i] 表示第 i 个人的高度。

一个人能 看到 他右边另一个人的条件是这两人之间的所有人都比他们两人 。更正式的,第 i 个人能看到第 j 个人的条件是 i < j 且 min(heights[i], heights[j]) > max(heights[i+1], heights[i+2], …, heights[j-1]) 。

请你返回一个长度为 n 的数组 answer ,其中 answer[i] 是第 i 个人在他右侧队列中能 看到人数

示例 1:在这里插入图片描述

输入:heights = [10,6,8,5,11,9]
输出:[3,1,2,1,1,0]
解释:
第 0 个人能看到编号为 1 ,2 和
4 的人。
第 1 个人能看到编号为 2 的人。
第 2 个人能看到编号为 3 和 4 的人。
第 3 个人能看到编号为 4 的人。
第 4个人能看到编号为 5 的人。
第 5 个人谁也看不到因为他右边没人。

示例 2:

输入:heights = [5,1,2,3,10]
输出:[4,1,1,1,0]

提示:

n == heights.length

1 <= n <= 105

1 <= heights[i] <= 105

heights 中所有数 互不相同 。

提示 1

如何在二次复杂度中解决这个问题?

提示 2

对于从索引i开始的每个子数组,继续寻找新的最大值,直到找到一个大于arr[i]的值。

提示 3

因为极限很高,你需要一个线性解。

提示 4

在从末尾到开始迭代数组时,使用堆栈来保持数组值的排序。

提示 5

继续从堆栈中弹出元素,直到找到一个大于arr[i]的值,这些是我可以看到的人。

题解
方法:单调栈
        由题可知,对于第 i 个人来说,他能看到的人一定是按从左到右高度严格单调递增的。

        因此,我们可以倒序遍历数组 heights,用一个从栈顶到栈底单调递增的栈 stk 记录已经遍历过的人的高度。

        对于第 i 个人,如果栈不为空并且栈顶元素小于 heights[i],累加当前第 i 个人能看到的人数,然后将栈顶元素出栈,直到栈为空或者栈顶元素大于等于 heights[i]。如果此时栈不为空,说明栈顶元素大于等于 heights[i],那么第 i 个人能看到的人数还要再加 1。

        接下来,我们将 heights[i]入栈,继续遍历下一个人。

        遍历结束后,返回答案数组 ans。

class Solution {
    public int[] canSeePersonsCount(int[] heights) { 
        int n = heights.length; 
        int[] ans = new int[n]; 
        Deque<Integer> stk = new ArrayDeque<>(); // 双端队列stk,用于存储高度信息
        for (int i = n - 1; i >= 0; --i) { // 倒序遍历数组
            while (!stk.isEmpty() && stk.peek() < heights[i]) { // 如果栈不为空且栈顶元素小于当前元素
                stk.pop(); // 弹出栈顶元素
                ++ans[i]; 
            }
            if (!stk.isEmpty()) { 
                ++ans[i];
            }
            stk.push(heights[i]); // 将当前元素压入栈中
        }
        return ans; 
    }
}

6.在链表中插入最大公约数

题目链接: 2807. 在链表中插入最大公约数

给你一个链表的头 head ,每个结点包含一个整数值。

在相邻结点之间,请你插入一个新的结点,结点值为这两个相邻结点值的 最大公约数

请你返回插入之后的链表。

两个数的 最大公约数 是可以被两个数字整除的最大正整数。

示例 1:
在这里插入图片描述

输入:head = [18,6,10,3]

输出:[18,6,6,2,10,1,3]

解释:第一幅图是一开始的链表,第二幅图是插入新结点后的图(蓝色结点为新插入结点)。

  • 18 和 6 的最大公约数为 6 ,插入第一和第二个结点之间。
  • 6 和 10 的最大公约数为 2 ,插入第二和第三个结点之间。
  • 10 和 3 的最大公约数为 1 ,插入第三和第四个结点之间。

所有相邻结点之间都插入完毕,返回链表。

示例 2:

在这里插入图片描述

输入:head = [7]

输出:[7]

解释:第一幅图是一开始的链表,第二幅图是插入新结点后的图(蓝色结点为新插入结点)。

没有相邻结点,所以返回初始链表。

提示:

链表中结点数目在 [1, 5000] 之间。

1 <= Node.val <= 1000

题解
方法:遍历
        遍历链表,在当前节点node 后面插入gcd节点,同时gcd节点指向下一个node 节点。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    // 插入最大公约数节点的方法
    public ListNode insertGreatestCommonDivisors(ListNode head) {
        ListNode node = head; // 初始化当前节点为头节点
        while (node.next != null) { // 遍历链表,直到最后一个节点
            node.next = new ListNode(gcd(node.val, node.next.val), node.next); // 将当前节点的下一个节点替换为最大公约数节点
            node = node.next.next; // 移动到新插入的最大公约数节点的下一个节点
        }
        return head; // 返回修改后的链表头节点
    }

    // 计算两个整数的最大公约数的方法
    public int gcd(int a, int b) {
        while (b != 0) { // 当b不为0时,继续循环
            int tmp = a % b; // 计算a除以b的余数
            a = b; // 将b的值赋给a
            b = tmp; // 将余数赋给b
        }
        return a; // 返回最大公约数
    }
}

7.赎金信

题目链接:383. 赎金信

给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false 。

magazine 中的每个字符只能在 ransomNote 中使用一次。

示例 1:

输入:ransomNote = “a”, magazine = “b”

输出:false

示例 2:

输入:ransomNote = “aa”, magazine = “ab”

输出:false

示例 3:

输入:ransomNote = “aa”, magazine = “aab”

输出:true

提示:

1 <= ransomNote.length, magazine.length <= 105

ransomNote 和 magazine 由小写英文字母组成

题解

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        int[] memo = new int[26];
        
        // 遍历magazine字符串中的每个字符,将其转换为小写字母后在memo数组中对应的位置加1
        for (char ch : magazine.toCharArray()) memo[ch - 'a']++;
        
        // 遍历ransomNote字符串中的每个字符,将其转换为小写字母后在memo数组中对应的位置减1
        for (char ch : ransomNote.toCharArray()) if (--memo[ch - 'a'] < 0) return false;
        
        // 如果减1后的值小于0,说明ransomNote中的某个字符在magazine中不存在,返回false
        // 如果ransomNote中的所有字符都在magazine中存在,返回true
        return true;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/307446.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用kennycason.kumo.WordCloud For JAVA 制作词云图

官网&#xff1a;https://kennycason.com/posts/2014-07-03-kumo-wordcloud.html 一&#xff1a;添加POM文件 <!-- 词云 --><dependency><groupId>com.kennycason</groupId><artifactId>kumo-core</artifactId><version>1.27<…

c语言-字符串函数(库函数使用)和模拟实现

文章目录 前言一、库函数strcat()介绍1.1 strcat()介绍1.2 模拟实现strcat() 总结 前言 在写c语言基础系列文章时&#xff0c;介绍了字符串函数strlen(),strcpy(),strcmp()的使用和模拟实现。 本篇文章继续探讨其他字符串函数的使用以及模拟实现。 一、库函数strcat()介绍 1.…

螺旋数字矩阵 - 华为OD统一考试

OD统一考试(C卷) 分值: 100分 题解: Java / Python / C++ 题目描述 疫情期间,小明隔离在家,百无聊赖,在纸上写数字玩。他发明了一种写法: 给出数字个数n和行数m (0 < n <= 999,0 < m <= 999),从左上角的1开始,按照顺时针螺旋向内写方式,依次写出2,3……

RabbitMQ(十一)队列的扩展属性(Arguments)

目录 一、简介二、队列扩展属性清单三、代码示例3.1 实现方式一&#xff1a;channel.queueDeclare()3.2 实现方式二&#xff1a;QueueBuilder.build() 一、简介 RabbitMQ 允许用户在声明队列、交换机或绑定时设置 扩展属性&#xff08;Arguments&#xff09;&#xff0c;这些扩…

求阶乘(优化版)

✨欢迎来到脑子不好的小菜鸟的文章✨ &#x1f388;创作不易&#xff0c;麻烦点点赞哦&#x1f388; 所属专栏&#xff1a;刷题 我的主页&#xff1a;脑子不好的小菜鸟 文章特点&#xff1a;关键点和步骤讲解放在 代码相应位置 明天考试&#xff0c;今天复习&#xff0c;复习…

2.2.3机器学习—— 判定梯度下降是否收敛 + α学习率的选择

2.2.3 判定梯度下降是否收敛 α学习率的选择 2.1、 判定梯度下降是否收敛 有两种方法&#xff0c;如下图&#xff1a; 方法一&#xff1a; 如图&#xff0c;随着迭代次数的增加&#xff0c;J(W,b)损失函数不断下降当 iterations 300 之后&#xff0c;下降的就不太明显了 / …

2024年【安全员-B证】试题及解析及安全员-B证模拟试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 安全员-B证试题及解析参考答案及安全员-B证考试试题解析是安全生产模拟考试一点通题库老师及安全员-B证操作证已考过的学员汇总&#xff0c;相对有效帮助安全员-B证模拟试题学员顺利通过考试。 1、【多选题】《北京市…

Angular学习第二天--问题记录

一、问题 1.用脚手架搭建完项目之后&#xff0c;缺少app.modules.ts文件&#xff0c; 2.解决办法&#xff1a; 在终端继续输入命令 ng new 项目名称 --no-standalone --routing --ssrfalse 3.完整目录&#xff1a; 二、问题 1.问题来源&#xff0c;源代码&#xff1a; <fo…

Goby高级食用指南

Goby高级食用指南 1.Goby POC2.自定义字典3.Goby插件生态 - 一些好用的插件分享FOFASubDomainsBruteExportCsvAWVSRedis-cliGoby4waf初级篇参考 - Goby基本使用 1.Goby POC Goby的漏洞模块包含官方自定义的一些初始POC: 红队版的POC会实时更新,普通版则不会 Goby的POC编写…

Helix QAC 2023.4 新版支持C++20语言,带来更多性能提升!

Helix QAC 2023.4 新增功能 Helix QAC 2023.4全面支持MISRA C:2023规则&#xff0c;涵盖100%的指南。此版本还加强了对C20语言的支持&#xff0c;改进了数据流分析性能&#xff0c;并在整个产品中增加了多项用户体验改进。 增强的C20支持 此版本新增了对以下语言特性的支持&a…

LeetCode-杨辉三角公式

杨辉三角公式 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/a225ff66061e4076924e3299b81b98d5.png /*** author wx* description 杨辉三角公式-标准* create 2023/12/26**/ public class Triangle {public static void main(String[] args) {List<List<I…

GPT大模型在生物、地球、农业、气象、生态、环境科学可以应用?

以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮&#xff0c;可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…

麒麟系统安装docker、mysql、clickhouse

1、查看麒麟系统版本信息 cat /etc/os-release 麒麟系统版本V10 64位操作系统 # uname -p x86_64 # uname -p aarch64 内核版本 # uname -r 4.19.90-24.4.v2101.ky10.x86_64 本操作为麒麟系统版本V10&#xff0c;x86_64操作系统 一&#xff0c;安装docker 文件&#xff1a…

Python自带爬虫库urllib使用大全

目录 一、urllib库简介 二、发送HTTP请求 三、处理响应 四、解析URLs 五、设置代理 六、总结 在Python中&#xff0c;urllib是一个用于处理URLs的内置库&#xff0c;它提供了用于构建、解析、发送和接收HTTP、HTTPS和其他URLs的强大工具。这个库是Python标准库的一部分&a…

Open CASCADE学习|创建拓朴

目录 1、创建点gp_Pnt 2、创建向量gp_Vec 3、创建边TopoDS_Edge 4、线网络TopoDS_Wire 5、面TopoDS_Face 6、体TopoDS_Shape OpenCascade中的拓朴实体如下图所示&#xff0c;其中Compound可以包含很多Solid&#xff1b;Solid由Shell包围而成&#xff1b;Shell由相连的Fac…

7 集中式日志和分布式跟踪

文章目录 日志聚合模式日志集中化的简单解决方案使用日志并输出分布式跟踪Spring Cloud Sleuth实现分布式跟踪 小结 前面的文章&#xff1a; 1、 1 一个测试驱动的Spring Boot应用程序开发 2、 2 使用React构造前端应用 3、 3 试驱动的Spring Boot应用程序开发数据层示例 4、…

设计模式-总述

设计模式简介 设计模式代表了最佳的实践&#xff0c;通常被有经验的面向对象的软件开发人员所采用。它是开发人员在软件开发过程中面临的一般问题的解决方案。这些方案是众多软件开发人员经过相当长的一段时间总结出来的。是一套被反复使用的&#xff0c;多数人知晓的&#xf…

在Linux中使用Apache HTTP服务器

Apache HTTP服务器&#xff0c;也被称为Apache&#xff0c;是全球使用最广泛的Web服务器软件之一。它以其稳定性、强大的功能和灵活性而闻名&#xff0c;尤其在Linux操作系统上表现得尤为出色。以下是关于如何在Linux中使用Apache HTTP服务器的详细指南。 1. 安装Apache 首先…

ORACLE RAC DG文件路径错乱解决办法

最近接手了一个客户的RAC-RAC dg环境的维护,登录上去之后发现dg延迟了8天,由于主库的空间非常紧张,归档日志早就删除了,所以准备使用rman基于scn点的备份恢复的方案恢复dg同步 在备份完成之后,使用新的控制文件进行数据恢复的时候报错datafile 43 not found: 检查了一下发现当…

Vue面试之虚拟DOM

Vue面试之虚拟DOM 什么是虚拟dom&#xff1f;虚拟dom是如何产生的&#xff1f;编写模板template模板编译Complie挂载Mounting 如何进行新旧Dom对比&#xff1f; 最近在整理一些前端面试中经常被问到的问题&#xff0c;分为vue相关、react相关、js相关、react相关等等专题&#…