跳转: 42. 接雨水
问题:
给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
思路:
比较容易想到的是用快慢指针(其实和两层for循环遍历一样)一个坑一个坑的找,只要大于等于当前的柱子就跳转,但最坏的情况时间为 O ( n 2 ) O(n^2) O(n2);
可以用左右指针,以低的一边为界(因为左右都在最外侧,所以更低的情况可以视作坑内,故不存在更低边界),让边界低的向边界高的靠近,可以一遍遍历完成计算
复杂度:
代码(左右指针):
class Solution {
public int trap(int[] height) {
int l = 0;
int r = height.length - 1;
int lMax = 0;
int rMax = 0;
int res = 0;
while (l != r) {
if (height[l] > lMax) {
lMax = height[l];
}
if (height[r] > rMax) {
rMax = height[r];
}
if (height[l] < height[r]) {
res += lMax - height[l];
l++;
} else {
res += rMax - height[r];
r--;
}
}
return res;
}
}
复杂度:
代码(快慢指针):
class Solution {
public int trap(int[] height) {
int slow = 0;
int fast = 1;
int max = 0;
int maxIndex = 0;
int res = 0;
while(slow<height.length-1){
int H = height[slow];
while(fast<height.length&&height[fast]<H){
if(max<height[fast]){
max = height[fast];
maxIndex = fast;
}
fast++;
}
if(fast<height.length&&height[fast] >= H){
for(int i = slow+1;i<fast;i++){
res+=H - height[i];
}
slow = fast;
}
else if(maxIndex>0){
H = max>H?H:max;
for(int i = slow+1;i<maxIndex;i++){
res+=H - height[i];
}
slow = maxIndex;
}
else{
slow++;
}
fast = slow+1;
maxIndex = 0;
max = 0;
}
return res;
}
}
跳转: 2109. 向字符串添加空格
问题:
给你一个下标从 0 开始的字符串 s
,以及一个下标从 0 开始的整数数组 spaces
。
数组 spaces
描述原字符串中需要添加空格的下标。每个空格都应该插入到给定索引处的字符值 之前 。
s = "EnjoyYourCoffee"
且 spaces = [5, 9]
,那么我们需要在 'Y'
和 'C'
之前添加空格,这两个字符分别位于下标 5
和下标 9
。因此,最终得到 “Enjoy Your Coffee” 。请你添加空格,并返回修改后的字符串。
思路:
StringBuilder 比 StringBuffer 少了同步机制,更快
初始化内存避免扩容开销
避免使用insert这种O(n)方法,选择使用append
批量append代替单字符,减少循环的开销
直接用append方法添加子串,避免substring临时对象的开销
复杂度:
代码:
class Solution {
public String addSpaces(String s, int[] spaces) {
StringBuilder ans = new StringBuilder(s.length() + spaces.length);
int last = 0;
for (int i = 0; i < spaces.length; i++) {
ans.append(s, last, spaces[i]);
last = spaces[i];
ans.append(" ");
}
ans.append(s,last,s.length());
return ans.toString();
}
}
跳转: 121. 买卖股票的最佳时机
问题:
给定一个数组 prices
,它的第 i
个元素 prices[i]
表示一支给定股票第 i
天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0
。
思路:
每次用当前历史最低价卖出,根据价格更新最大利润
复杂度:
代码:
class Solution {
public int maxProfit(int[] prices) {
int min = prices[0];
int max = 0;
for(int i = 1;i<prices.length;i++){
if(min>prices[i]){
min = prices[i];
}
else if(max<prices[i]-min){
max = prices[i] -min;
}
}
return max;
}
}
跳转: 55. 跳跃游戏
问题:
给你一个非负整数数组 nums
,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标,如果可以,返回 true
;否则,返回 false
。
思路:
首先想到的是用哈希,走一步看看当前点能不能到达,然后更新一遍可到达点
如果遇到不可到达的地方就说明不能到最后
可以直接记录最远可到达点
如果遇到最大无法到达就说明不能走到最后
复杂度:
代码(最远点贪心):
class Solution {
public boolean canJump(int[] nums) {
int max = 0;
int n = nums.length-1;
for(int i = 0;i<=n;i++){
if(max<i) return false;
int tmp = i+nums[i];
max = max>tmp?max:tmp;
if(max>=n) return true;
}
return false; // 没用
}
}
复杂度:
代码(哈希):
class Solution {
public boolean canJump(int[] nums) {
int n = nums.length;
int[] hash = new int[n];
hash[0] =1;
for(int i=0;i<n;i++){
if(hash[i]==1){
int far = i+nums[i];
for(int j = i+1;j<n&&j<=far;j++){
hash[j] = 1;
}
}
}
// System.out.println(Arrays.toString(hash));
return hash[n-1]==1;
}
}
跳转: 45. 跳跃游戏 II
问题:
给定一个长度为 n
的 0 索引整数数组 nums
。初始位置为 nums[0]
。
每个元素 nums[i]
表示从索引 i
向后跳转的最大长度。换句话说,如果你在 nums[i]
处,你可以跳转到任意 nums[i + j]
处:
0 <= j <= nums[i]
i + j < n
返回到达 nums[n - 1]
的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]
。
思路:
贪心*2,每步走最远,记最远可到达点
复杂度:
代码:
class Solution {
public int jump(int[] nums) {
if(nums.length==1) return 0;
int n = nums.length;
int maxStep = 0;
int nextMax = 0;
int steps = 0;
for (int i = 0; i < n; i++) {
if(maxStep<i){
maxStep=nextMax;
steps++;
nextMax = i;
}
nextMax = Math.max(nextMax,i+nums[i]);
if(nextMax>=n-1) return steps+1;
}
return -1;
}
}
跳转: 763. 划分字母区间
问题:
给你一个字符串 s
。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。例如,字符串 "ababcc"
能够被分为 ["abab", "cc"]
,但类似 ["aba", "bcc"]
或 ["ab", "ab", "cc"]
的划分是非法的。
注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s
。
返回一个表示每个字符串片段的长度的列表。
思路:
一开始想的是记录所有字母的起点和终点的索引,遍历起点和终点,一段一段找
如果更新终点就重找(因为可能会有些起点在新的区间里)
不过实现起来相对复杂
可以记录终点,然后遍历字符串
到达当前段终点时记录位置,更新起点
复杂度:
代码(遍历字符串):
class Solution {
public List<Integer> partitionLabels(String s) {
int n = s.length();
int[] last = new int[26];
int length = s.length();
for (int i = n - 1; i >= 0; i--) {
int tmp = s.charAt(i) - 'a';
if (last[tmp] == 0) {
last[tmp] = i;
}
}
List<Integer> res = new ArrayList<>();
int start = 0;
int end = 0;
for (int i = 0; i < n; i++) {
end = Math.max(end, last[s.charAt(i) - 'a']);
if (i == end) {
res.add(end - start + 1);
start = end + 1;
}
}
return res;
}
}
复杂度:
代码(遍历起点和终点):
class Solution {
public List<Integer> partitionLabels(String s) {
int n = s.length();
int[] start = new int[26];
int[] end = new int[26];
char[] arr = s.toCharArray();
for (int i = 0; i < n; i++) {
int tmp = arr[i] - 'a';
if (start[tmp] == 0) {
start[tmp] = i+1;
}
}
for (int i = n - 1; i >= 0; i--) {
int tmp = arr[i] - 'a';
if (end[tmp] == 0) {
end[tmp] = i+1;
}
}
// System.out.println(Arrays.toString(start));
// System.out.println(Arrays.toString(end));
List<Integer> list = new ArrayList<>();
for (int i = 0; i < n;) {
int tmp = arr[i] - 'a';
int st = start[tmp];
int e = end[tmp];
int[] hash = new int[26];
int pre;
do {
pre = e;
for (int j = 0; j < 26; j++) {
if (start[j] > st && start[j] < e) {
hash[j] = 1;
}
}
for (int j = 0; j < 26; j++) {
if (hash[j] == 1) {
if (end[j] > e)
e = end[j];
}
}
} while (e != pre);
// System.out.println(st);
// System.out.println(e);
list.add(e - st + 1);
i = e;
}
return list;
}
}
今天主要是练习了几道贪心
用贪心求解感觉在于找到那个使当前最优的关键状态变量,像当前段终点,最远可到达点,历史最低价
都是基于当前状态逐步决策,在每步决策中求局部最优解
这种思维后续还需要继续练习
代码随想录算法训练营第一天
代码随想录算法训练营第二天
代码随想录算法训练营第三天
代码随想录算法训练营第四天