【题解】—— LeetCode一周小结

news2024/12/24 9:49:51

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;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python爬虫必学数据库:MongoDB

微信公众号&#xff1a;愤怒的it男&#xff0c;超多Python技术干货文章。 MongoDB由C编写而成&#xff0c;是免费开源跨平台的非关系型数据库&#xff0c;与关系型数据库不同&#xff0c;MongoDB将数据存储在类似JSON的文档中&#xff0c;这使得数据库非常灵活和可伸缩。 一、环…

springcloud bus消息总线

简介 Spring Cloud Bus 配合Spring Cloud Config 使用可以实现配置的动态刷新。 Spring Cloud Bus是用来将分布式系统的节点与轻量级消息系统链接起来的框架&#xff0c;它整合了Java的事件处理机制和消息中间件的功能。Spring Clud Bus目前支持RabbitMQ和Kafka。 Spring C…

云计算任务调度仿真01

云计算任务调度的研究大多数以来仿真研究&#xff0c;现梳理一些做过的代码研究 结果无数次的排错&#xff0c;终于finish with code 0 了 这个代码以来的是比较老的TensorFlow版本&#xff0c;我们都知道TensorFlow1.x和TensorFlow2.x之间有很大差别&#xff0c;但其实&#…

vue3中路由的使用(详细讲解)

1、路由的简介 路由(route)&#xff1a;就是根据特定的规则将数据包或请求从源地址传输到目标地址的过程。 在前端或者vue3项目中路由主要用于构建单页面应用程序&#xff08;SPA&#xff09;&#xff0c;其中所有的页面都在同一个HTML文件中加载&#xff0c;通过JavaScript动…

无人驾驶卡尔曼滤波

无人驾驶卡尔曼滤波&#xff08;行人检测&#xff09; x k a x k − 1 w k x_k ax_{k-1} w_k xk​axk−1​wk​ w k w_k wk​&#xff1a;过程噪声 状态估计 估计飞行器状态&#xff08;高度&#xff09; x k z k − v k x_k z_k - v_k xk​zk​−vk​ 卡尔曼滤波通…

OpenAI 是如何一步一步把RAG做到98%的准确性得

参考OpenAI的官方演讲&#xff0c;如何做好RAG。本文整理的内容&#xff0c;均来源于此演讲内容。 【OpenAI演讲-自制中文字幕】干货-如何提升大模型表现&#xff1f;-提示工程、RAG与Fine-Tuning技巧详解_哔哩哔哩_bilibili 45% 的准确性 普通搜索不做任何处理的效果&#x…

人工智能_机器学习091_使用三维瑞士卷数据_KMeans聚类算法进行瑞士卷数据聚类---人工智能工作笔记0131

然后我们首先来构建一下数据 准备瑞士卷数据: import numpy as np 导入数学计算包 import matplotlib.pyplot as plt 导入画图包 #自底向上聚类 from sklearn.cluster import AgglceerativeClustering 导入分层聚类模型 from sklearn.datasets import make_swiss_roll # 瑞士卷…

给自己创建的GPTs添加Action(查天气)

前言 在这篇文章中&#xff0c;我将分享如何利用ChatGPT 4.0辅助论文写作的技巧&#xff0c;并根据网上的资料和最新的研究补充更多好用的咒语技巧。 GPT4的官方售价是每月20美元&#xff0c;很多人并不是天天用GPT&#xff0c;只是偶尔用一下。 如果调用官方的GPT4接口&…

Linux 下查看内存使用情况方法总结

在做Linux系统优化的时候&#xff0c;物理内存是其中最重要的一方面。自然的&#xff0c;Linux也提供了非常多的方法来监控宝贵的内存资源的使用情况。下面的清单详细的列出了Linux系统下通过视图工具或命令行来查看内存使用情况的各种方法。 1./proc/meminfo 查看RAM使用情况…

【C语言】TCP测速程序

一、服务端 下面是一个用 C 语言编写的测试 TCP 传输速度的基本程序示例。 这只是一个简单示例&#xff0c;没有做详细的错误检查和边缘情况处理。在实际应用中&#xff0c;可能需要增加更多的功能和完善的异常处理机制。 TCP 服务器 (server.c): #include <stdio.h> #…

Centos7 手动更改系统时间

文章目录 1.更改系统时间2.写入系统时间3.查看是否写入成功 1.更改系统时间 date -s "2017-12-18 09:40:00"2.写入系统时间 hwclock -w3.查看是否写入成功 timedatectl

vue中鼠标拖动触发滚动条的移动

前言 在做后端管理系统中&#xff0c;像弹窗或大的表单时&#xff0c;经常会有滚动条的出现&#xff0c;但有些时候如流程、图片等操作时&#xff0c;仅仅使用鼠标拖动滚动条操作不太方便&#xff0c;如果使用鼠标拖拽图片或容器来触发滚动条的移动就比较方便了 功能设计 如…

用python提取word中的所有图片

使用word中提取的方式图片会丢失清晰度&#xff0c;使用python写一个脚本&#xff0c;程序运行将弹出对话框选择一个word文件&#xff0c;然后在弹出一个对话框选择一个文件夹保存word中的文件。将该word中的所有图片都保存成png格式&#xff0c;并命名成image_i的样式。 程序…

模型容器与AlexNet构建

一、模型容器——Containers nn.Sequential 是 nn.module的容器&#xff0c;用于按顺序包装一组网络层 Sequential 容器 nn.Sequential 是 nn.module的容器&#xff0c;用于按顺序包装一组网络层 • 顺序性&#xff1a;各网络层之间严格按照顺序构建 • 自带forward()&#xf…

nodejs版本管理工具nvm的安装与使用

提示&#xff1a;nodejs版本管理工具nvm的安装与使用 文章目录 前言一、安装二、淘宝镜像配置三、安装所需版本的nodejs四、切换nodejs版本五、参考文档总结 前言 需求&#xff1a;新建一个vue3项目&#xff0c;&#xff0c;提示写法错误 查原因为node版本过低 随着技术更新迭…

6个提升Python编程能力的PyCharm插件

大家好&#xff0c;PyCharm作为一款强大的集成开发环境&#xff0c;本身已经提供了许多功能&#xff0c;但一些插件将进一步扩展和增强PyCharm的能力。通过使用这些插件&#xff0c;大家能够更快速地编写代码、提高代码质量、进行调试和优化&#xff0c;并将开发体验提升到一个…

Python 面向对象之元类

Python 面向对象之元类 【一】一切皆对象 【1】元类 元类&#xff08;metaclass&#xff09;是Python中用于创建类的类。在Python中&#xff0c;类是对象&#xff0c;而元类就是类的类它们控制类的创建过程&#xff0c;允许你定制类的行为Python中内置的默认元类是type我们用…

SpringMVC概述、SpringMVC 的入门

1.MVC介绍 MVC是一种设计模式&#xff0c;将软件按照模型、视图、控制器来划分&#xff1a; M&#xff1a;Model&#xff0c;模型层&#xff0c;指工程中的JavaBean&#xff0c;作用是处理数据 JavaBean分为两类&#xff1a; 一类称为数据承载Bean&#xff1a;专门存储业务数据…

12、DolphinScheduler

1、DolphinScheduler简介 1.1、 DolphinScheduler概述 Apache DolphinScheduler是一个分布式、易扩展的可视化DAG工作流任务调度平台。致力于解决数据处理流程中错综复杂的依赖关系&#xff0c;使调度系统在数据处理流程中开箱即用。 1.2、 DolphinScheduler核心架构 Dolph…

Unity 踩坑记录 AnyState 切换动画执行两次

AnySate 切换动画 Can Transition To Self 将这个勾选去掉&#xff01;&#xff01;&#xff01;