给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
n == height.length
2 <= n <= 105
0 <= height[i] <= 104
双指针法:目标容积是由两个坐标下不同高度线决定的
关键:高的线往内部移动一格时,容积必然不变或者减小;而矮的线往内部移动一格时,容积可能增大
这个和贪心算法有什么关系呢?
每一步都是 曾经步骤到当前步骤的 局部最优解,迭代到最后一步时,得到的是全局最优解。
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例2:
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
提示:
1 <= nums.length <= 3 * 104
0 <= nums[i] <= 105
动态规划: 最优解存在子结构中,(i)的解可能比(i+1)的更优,综合所有的(i)的解,能得到最优解。所以可以看成DP问题。
我没有看出是DP问题,使用了逆向思维。如果判断是否能从0到重点n可以从0开始,那也可以从n开始判断最终能否回到0。为什么这样思考,因为最笨的方法是对每个(i)都试一遍,而(i)中的最多又需要遍历n次,复杂度为(n * n)并且存在重复的尝试。如果我从n开始,回头算呢?还是存在重复的尝试,所以维护一个哈希表存储已经尝试过的位置。所以其实,从0开始还是从n开始,时间复杂度都是(n * n), 甚至还有空间复杂度n;
而答案的时间复杂度只有n, 空间复杂度为1。它维护了一个index,代表目前所有遍历过的位置出发能到达的最远位置,当遍历所有位置,得到的必然是最优解,即判断能不能走到终点n。如果在遍历过程中发现当前位置下标>之前遍历过的位置出发所能到达的最远位置,那么就立刻停止遍历并得出无法到达n的结论。
假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。
请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。
示例 1:
输入:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
输出:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
解释:
编号为 0 的人身高为 5 ,没有身高更高或者相同的人排在他前面。
编号为 1 的人身高为 7 ,没有身高更高或者相同的人排在他前面。
编号为 2 的人身高为 5 ,有 2 个身高更高或者相同的人排在他前面,即编号为 0 和 1 的人。
编号为 3 的人身高为 6 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
编号为 4 的人身高为 4 ,有 4 个身高更高或者相同的人排在他前面,即编号为 0、1、2、3 的人。
编号为 5 的人身高为 7 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
因此 [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 是重新构造后的队列。
示例 2:
输入:people = [[6,0],[5,0],[4,0],[3,2],[2,2],[1,4]]
输出:[[4,0],[5,0],[2,2],[3,2],[1,4],[6,0]]
提示:
1 <= people.length <= 2000
0 <= hi <= 106
0 <= ki < people.length
题目数据确保队列可以被重建
排序和找规律
我没想出来;答案的解法如下。这道题的关键是排序和找规律;标签说和贪心、线段树有关,为啥?和贪心有关的原因是这道题可以通过每次寻找局部最优解,即寻找当前元素顺序时,只需要关心当前元素的顺序,这样依次后得到全局最优解,即所有元素的顺序都是正确的。那和线段树什么关系?
规律是,矮的人不会对高的人产生影响。换句话说,先把高的排好顺序,再排矮的。对于这种排序和找规律的题目,影响大的先操作,影响小的后操作,因为,当影响大的元素确定了顺序后,影响小的元素插入到影响大的元素之中时不会影响到影响大的元素的顺序,这样,就能保证所有的顺序是正确的。
给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
请你找出符合题意的 最短 子数组,并输出它的长度。
示例 1:
输入:nums = [2,6,4,8,10,9,15]
输出:5
解释:你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
示例 2:
输入:nums = [1,2,3,4]
输出:0
示例 3:
输入:nums = [1]
输出:0
双指针和贪心
(1)最笨的方法是排序后,进行比较;
(2)第二笨的方法是我第二次提交的,思想是贪心,在快速排序的过程中,寻找左边界和右边界。当进行快速排序的互换元素操作时,局部左边界必定是min(当前左指针,历史最小的左指针), 局部右指针必定是max(当前右指针,历史最大右指针),如此局部最优,最后得到了一个全局最优,即真正的右边界。时间复杂度为nlogn,空间复杂度为1
(3)最好的方法是我第一次提交的,无需排序,使用双指针,规律如下:从左往右遍历,如果当前元素小于历史最大元素,更新右边界为当前元素下标;从右往左遍历,如果当前元素大于历史最小元素,更新左边界为当前元素下标。时间复杂度为n,空间复杂度为1
标签是:贪心、双指针和单调栈。单调栈一开始看不出,后来看到一个评论后想到,当前元素小于历史最大即为右边界,不就是单调栈里面的只存放历史最大元素吗?
给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间,CPU 可以完成一个任务,或者处于待命状态。
然而,两个 相同种类 的任务之间必须有长度为整数 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。
你需要计算完成所有任务所需要的 最短时间 。
示例 1:
输入:tasks = [“A”,“A”,“A”,“B”,“B”,“B”], n = 2
输出:8
解释:A -> B -> (待命) -> A -> B -> (待命) -> A -> B
在本示例中,两个相同类型任务之间必须间隔长度为 n = 2 的冷却时间,而执行一个任务只需要一个单位时间,所以中间出现了(待命)状态。
示例 2:
输入:tasks = [“A”,“A”,“A”,“B”,“B”,“B”], n = 0
输出:6
解释:在这种情况下,任何大小为 6 的排列都可以满足要求,因为 n = 0
[“A”,“A”,“A”,“B”,“B”,“B”]
[“A”,“B”,“A”,“B”,“A”,“B”]
[“B”,“B”,“B”,“A”,“A”,“A”]
…
诸如此类
示例 3:
输入:tasks = [“A”,“A”,“A”,“A”,“A”,“A”,“B”,“C”,“D”,“E”,“F”,“G”], n = 2
输出:16
解释:一种可能的解决方案是:
A -> B -> C -> A -> D -> E -> A -> F -> G -> A -> (待命) -> (待命) -> A -> (待命) -> (待命) -> A
提示:
1 <= task.length <= 104
tasks[i] 是大写英文字母
n 的取值范围为 [0, 100]
找规律+分类讨论。但是这个和贪心什么关系,我没理解。
(1)我的:最笨的方法。
思想:动态地从大到小排序,然后以n+1为一轮按顺序遍历,如此重复直到全部走完
数据结构:二维数组中,list[i][0]
表示第i+1个字母的任务个数,list[i][1]
表示排序后的下一个字母的index。超时了。
(2)答案:找规律+分类讨论。把时间问题看成一个桶,桶的高度由重复次数最多的那类任务决定,而桶的宽度则由时间片n决定。然后桶里具体时间的多少就看任务怎么摆进去,无非就是两种情况,一是桶没装满,前h-1行是必须经历的时间,最后一行的宽就看最长种类任务有几类;另一种是桶溢出了,多出来的部分放在桶后面。而我们不知道桶是哪一种情况(也没必要知道),只需取两种情况中数量最大的情况。因为如果桶没溢出,前者>后者;如果桶溢出,后者>前者。规律就是,二者中的最大值总能会是问题中的所求时间的值。时间复杂度为1。