算法笔记
算法笔记
leetcode
美丽塔 2865
给你一个长度为
n下标从 0 开始的整数数组maxHeights。你的任务是在坐标轴上建
n座塔。第i座塔的下标为i,高度为heights[i]。如果以下条件满足,我们称这些塔是 美丽 的:
1 <= heights[i] <= maxHeights[i]heights是一个 山脉 数组。如果存在下标
i满足以下条件,那么我们称数组heights是一个 山脉 数组:
- 对于所有
0 < j <= i,都有heights[j - 1] <= heights[j]- 对于所有
i <= k < n - 1,都有heights[k + 1] <= heights[k]请你返回满足 美丽塔 要求的方案中,高度和的最大值 。
保证数组的左侧构成非递减,右侧构成非递增
如何使得数组成为递增或递减,此时我们想到「单调栈」,「单调栈」可以保证栈中数据的单调性,利用单调栈将连续子数组变为非递减或非递减。
1 | class Solution { |
滑动窗口最大值 239
给你一个整数数组
nums,有一个大小为k的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的k个数字。滑动窗口每次只向右移动一位。返回 滑动窗口中的最大值 。
单调队列,具体请看单调队列 滑动窗口最大值【基础算法精讲 27】
维护一个双向队列,从队尾插入新的数据时保证队列的递增性质或递减性质,如果出现矛盾,则不断从队尾剔除数据,直到满足条件,最终从队尾插入新的数据。
1 | class Solution { |
计算二进制的1的数量
给你一个下标从 0 开始的整数数组
nums和一个整数k。请你用整数形式返回
nums中的特定元素之 和 ,这些特定元素满足:其对应下标的二进制表示中恰存在k个置位。整数的二进制表示中的 1 就是这个整数的 置位 。
例如,
21的二进制表示为10101,其中有3个置位。
API:Integer.bitCount()
1 | private int position(int num){ |
最大合金数 2861
单调性二分查找
假设你是一家合金制造公司的老板,你的公司使用多种金属来制造合金。现在共有
n种不同类型的金属可以使用,并且你可以使用k台机器来制造合金。每台机器都需要特定数量的每种金属来创建合金。对于第
i台机器而言,创建合金需要composition[i][j]份j类型金属。最初,你拥有stock[i]份i类型金属,而每购入一份i类型金属需要花费cost[i]的金钱。给你整数
n、k、budget,下标从 1 开始的二维数组composition,两个下标从 1 开始的数组stock和cost,请你在预算不超过budget金钱的前提下,最大化 公司制造合金的数量。
所有合金都需要由同一台机器制造。
返回公司可以制造的最大合金数。
示例 1:
1 | 输入:n = 3, k = 2, budget = 15, composition = [[1,1,1],[1,1,10]], stock = [0,0,0], cost = [1,2,3] |
题解
假设要制造 num 份合金,由于 num 越小,花费的钱越少,num 越多,花费的钱越多,有单调性,可以二分。
对于第 j 类金属:
- 如果
composition[i][j] * num <= stock[j],那么无需购买额外的金属。 - 如果
composition[i][j] * num > stock[j],那么需要购买额外的金属,花费为(composition[i][j] * num - stock[j]) * cost[j]。
遍历每类金属,计算总花费。如果总花费超过 budget,则无法制造 num 份合金,否则可以制造。
最后讨论下二分的上下界:
- 二分上界:粗略计算一下,假设
composition[i][j]和cost[j]都是 1,此时可以制造最多的合金,个数为min(stock) + budget。 - 二分下界:可以设为 1。更巧妙的做法是,设当前答案为
ans,那么可以初始化二分下界为ans + 1,因为算出小于等于ans的值是没有意义的,不会让ans变大。如果这台机器无法制造出至少ans + 1份合金,那么二分循环结束后的结果为ans,不影响答案。
答案:引用自灵茶山艾府
1 | // 全部转成 int[] 数组,效率比 List<Integer> 更高 |
分割回文串 131
给你一个字符串
s,请你将s分割成一些子串,使每个子串都是 回文串 。返回s所有可能的分割方案。回文串 是正着读和反着读都一样的字符串。
示例 1:
1 | 输入:s = "aab" |
Code
1 | class Solution { |
水壶问题 365
有两个水壶,容量分别为
jug1Capacity和jug2Capacity升。水的供应是无限的。确定是否有可能使用这两个壶准确得到targetCapacity升。如果可以得到
targetCapacity升水,最后请用以上水壶中的一或两个来盛放取得的targetCapacity升水。你可以:
- 装满任意一个水壶
- 清空任意一个水壶
- 从一个水壶向另外一个水壶倒水,直到装满或者倒空
题解
状态队列/dfs
1 | class Solution { |
数学解法
贝祖定理
只要满足 z ≤ x+y 且这样的 a, b 存在,那么我们的目标就是可以达成的。这是因为:
若 a ≥ 0, b ≥ 0,那么显然可以达成目标。
若 a < 0,那么可以进行以下操作:
- 往 y 壶倒水;
- 把 y 壶的水倒入 x 壶;
- 如果 y 壶不为空,那么 x 壶肯定是满的,把 x 壶倒空,然后再把 y 壶的水倒入 x 壶。
重复以上操作直至某一步时 x 壶进行了 a 次倒空操作,y 壶进行了 b 次倒水操作。
若 b < 0,方法同上,x 与 y 互换。
而贝祖定理告诉我们,ax+by=z 有解当且仅当 z 是 x, y 的最大公约数的倍数。因此我们只需要找到 x, y 的最大公约数并判断 z 是否是它的倍数即可。
求最大公约数即可
自由之路 514
动态规划
给定一个字符串 ring ,表示刻在外环上的编码;给定另一个字符串 key ,表示需要拼写的关键词。您需要算出能够拼写关键词中所有字符的最少步数。
- 您可以将 ring 顺时针或逆时针旋转 一个位置 ,计为1步。旋转的最终目的是将字符串
ring的一个字符与12:00方向对齐,并且这个字符必须等于字符key[i]。 - 如果字符
key[i]已经对齐到12:00方向,您需要按下中心按钮进行拼写,这也将算作 1 步。按完之后,您可以开始拼写 key 的下一个字符(下一阶段), 直至完成所有拼写。
题解
记忆化搜索,memo数组存储
dp设置为,dp(i,j)为此时位于第i位置上,去搜索第j个字符所在位置
使循环数组所有元素相等的最少秒数 2808
给你一个下标从 0 开始长度为
n的数组nums。每一秒,你可以对数组执行以下操作:
- 对于范围在
[0, n - 1]内的每一个下标i,将nums[i]替换成nums[i],nums[(i - 1 + n) % n]或者nums[(i + 1) % n]三者之一。注意,所有元素会被同时替换。
请你返回将数组
nums中所有元素变成相等元素所需要的 最少 秒数。
API
computeIfAbsent 是 Java 中 Map 接口的一个方法,它用于在 Map 中根据指定的键(key)来获取对应的值(value)。如果该键不存在于 Map 中,computeIfAbsent 方法会使用指定的函数来计算一个默认值,并将该默认值存入 Map 中,然后返回该默认值。如果该键已经存在于 Map 中,则不会计算默认值,而是直接返回已存在的值。
数字游戏 LCP 24
小扣在秋日市集入口处发现了一个数字游戏。主办方共有
N个计数器,计数器编号为0 ~ N-1。每个计数器上分别显示了一个数字,小扣按计数器编号升序将所显示的数字记于数组nums。每个计数器上有两个按钮,分别可以实现将显示数字加一或减一。小扣每一次操作可以选择一个计数器,按下加一或减一按钮。主办方请小扣回答出一个长度为
N的数组,第i个元素(0 <= i < N)表示将0~i号计数器 初始 所示数字操作成满足所有条件nums[a]+1 == nums[a+1],(0 <= a < i)的最小操作数。回答正确方可进入秋日市集。由于答案可能很大,请将每个最小操作数对
1,000,000,007取余。
动态维护中位数
当处理前缀数组 nums 的每个前缀时,我们需要按照提示 2 中的公式计算最小距离和。这个问题的解法类似于 295. 数据流的中位数,我们可以使用两个堆来维护前 k 个数中,nums[i] - i 较小的一半和较大的一半,从而动态维护提示 2 中的「左半之和」以及「右半之和」。
具体来说,我们使用一个大根堆 left 维护较小的一半,其元素和为 leftSum;用一个小根堆 right 维护较大的一半,其元素和为 rightSum。在遍历 nums[i] 时,设 b = nums[i] - i,我们可以按以下方式进行分类讨论:
- 如果前缀长度是奇数,此时
left和right的大小相等,我们首先将b插入left,然后弹出left的堆顶元素,将其加到right中。这个操作可以保证,无论b是大是小,此时right的堆顶元素就是中位数x。此时,最小距离和为rightSum - x - leftSum。 - 如果前缀长度是偶数,此时
left比right少一个元素,我们首先将b插入right,然后弹出right的堆顶元素,将其加到left中。此时,最小距离和为rightSum - leftSum。
这种方法能够动态地维护中位数以及左半之和和右半之和,并根据前缀长度的奇偶来进行不同的操作,以计算最小距离和。
107 二叉树层序遍历
自底向上的层序遍历
1997 访问完所有房间的第一天
简单dp
你需要访问
n个房间,房间从0到n - 1编号。同时,每一天都有一个日期编号,从0开始,依天数递增。你每天都会访问一个房间。最开始的第
0天,你访问0号房间。给你一个长度为n且 下标从 0 开始 的数组nextVisit。在接下来的几天中,你访问房间的 次序 将根据下面的 规则 决定:
- 假设某一天,你访问
i号房间。- 如果算上本次访问,访问
i号房间的次数为 奇数 ,那么 第二天 需要访问nextVisit[i]所指定的房间,其中0 <= nextVisit[i] <= i。- 如果算上本次访问,访问
i号房间的次数为 偶数 ,那么 第二天 需要访问(i + 1) mod n号房间。请返回你访问完所有房间的第一天的日期编号。题目数据保证总是存在这样的一天。由于答案可能很大,返回对
109 + 7取余后的结果。
1 | class Solution { |
luogu
P1044 栈
栈是计算机中经典的数据结构,简单的说,栈就是限制在一端进行插入删除操作的线性表。
栈有两种最重要的操作,即 pop(从栈顶弹出一个元素)和 push(将一个元素进栈)。
栈的重要性不言自明,任何一门数据结构的课程都会介绍栈。宁宁同学在复习栈的基本概念时,想到了一个书上没有讲过的问题,而他自己无法给出答案,所以需要你的帮忙。
卡特兰数
