算法笔记
算法笔记
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(将一个元素进栈)。
栈的重要性不言自明,任何一门数据结构的课程都会介绍栈。宁宁同学在复习栈的基本概念时,想到了一个书上没有讲过的问题,而他自己无法给出答案,所以需要你的帮忙。
卡特兰数