15. 三数之和——双指针+遍历
自己写的思路总体正确,但是去重时出现了一些问题
分为几步:
先判断数组是否为空或者个数是否小于3,如果是,直接返回;
排序数组;
排序后遍历:
先判断开头是否>0,如果大于0,则一定无法满足三者相加为0,跳出循坏;
再判断该开头是否与前一个数相同,若相同,则跳过此次遍历,去重;
设左右指针,l=i+1,r=n-1,进行以下判断:
*若nums[i]+nums[l]+nums[r]=0,将该组合添加到list,然后判断l的右边是否与l相等,若相等,则跳过,r的左边亦是,让l、r都跳到不相等的下一个数
*若nums[i]+nums[l]+nums[r]<0,l++
*若nums[i]+nums[l]+nums[r]>0,r--
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> list=new ArrayList<List<Integer>>();
if(nums==null||nums.length<3){
return list;
}
Arrays.sort(nums);
for(int i=0;i<nums.length-1;i++){
if(nums[i]>0){
break;
}
//去重
if(i!=0&&nums[i]==nums[i-1]){
continue;
}
int l=i+1,r=nums.length-1;
int ans=-nums[i];
while(l<r){
if(nums[l]+nums[r]==ans){
List<Integer> list_add=new ArrayList<>();
list_add.add(nums[i]);
list_add.add(nums[l]);
list_add.add(nums[r]);
list.add(list_add);
//去重
while(l<r&&nums[l]==nums[l+1]){
l++;
}
while(l<r&&nums[r]==nums[r-1]){
r--;
}
//保证不漏
l++;
r--;
}else if(nums[l]+nums[r]<ans){
l++;
}else{
r--;
}
}
}
return list;
}
}
class Solution {
private long ans;
public long minimumFuelCost(int[][] roads, int seats) {
//思路为计算每一条路的贡献值,然后向上取整,再把贡献值相加
int n=roads.length+1;
List<Integer>[] g=new ArrayList[n];
Arrays.setAll(g,e->new ArrayList<>());
for(int[] e:roads){
int x=e[0],y=e[1];
g[x].add(y);
g[y].add(x);
}
dfs(0,-1,g,seats);
return ans;
}
private int dfs(int x,int fa,List<Integer>[] g,int seats){
int size=1;
for(int y:g[x]){
if(y!=fa){
size+=dfs(y,x,g,seats);
}
}
if(x>0){
ans+=(size-1)/seats+1;
}
return size;
}
}
1094. 拼车——差分数组
count[k]=count[k-1]+diff[k]
diff数组维护如下:
根据trips:
*k<from,不管
*k=from,diff[k]要加上当前上车乘客数
*from<k<to,不管
*k=to,diff[k]要减去下车乘客数
*k>to,不管
class Solution {
public boolean carPooling(int[][] trips, int capacity) {
int toMax=0;
for(int[] trip:trips){
toMax=Math.max(toMax,trip[2]);
}
int[] diff=new int[toMax+1];
for(int[] trip:trips){
diff[trip[1]]+=trip[0];
diff[trip[2]]-=trip[0];
}
int count=0;
for(int i=0;i<=toMax;i++){
count+=diff[i];
if(count>capacity){
return false;
}
}
return true;
}
}
自己的方法,使用了双指针,但是代码不优美,效率也不高,但话锋一转,虽然效率不高,但是自己做出困难题还是有成就感的
class Solution {
public int trap(int[] height) {
//若没有遇到大于等于它的数,选取小于等于它的数中最大的,构成容器,容器高度为较小值
//遇到大于它的数,构成容器,容器高度为较小值
//容器的体积为,较小值*长度-中间每一个的高度
int l=0;
while(height[l]==0&&l<height.length-1){
l++;
}
if(l>=height.length){
return 0;
}
//从有柱子的地方开始
int r=l+1;
int ans=0;
while(r<height.length){
int maxright=0;
int maxnum=0;
int h=0;
for(int i=r;i<height.length;i++){
if(height[i]>=height[l]){
r=i;
h=height[l];
break;
}
if(height[i]>=maxright){
maxright=height[i];
maxnum=i;
}
if(i==height.length-1){
r=maxnum;
h=maxright;
}
}
ans+=volume(l,r,h,height);
l=r;
r++;
}
return ans;
}
private int volume(int l,int r,int h,int[] height){
int v=h*(r-l-1);
for(int i=l+1;i<r;i++){
v-=height[i];
}
return v;
}
}
题解:
暂且不看动态规划(很好理解)和单调栈,只关注双指针
明显更简洁,思维更合理
class Solution {
public int trap(int[] height) {
//假设两柱子分别为 i,j。那么就有 iLeftMax,iRightMax,jLeftMx,jRightMax 这个变量。由于 j>i ,故 jLeftMax>=iLeftMax,iRigthMax>=jRightMax.
//那么,如果 iLeftMax>jRightMax,则必有 jLeftMax >= jRightMax,所有我们能接 j 点的水。
//如果 jRightMax>iLeftMax,则必有 iRightMax >= iLeftMax,所以我们能接 i 点的水。
int ans = 0;
int left = 0, right = height.length - 1;
int leftMax = 0, rightMax = 0;
while (left < right) {
leftMax = Math.max(leftMax, height[left]);
rightMax = Math.max(rightMax, height[right]);
//height[left] < height[right],left接水
//height[left]==height[right],right接水
if (height[left] < height[right]) {
ans += leftMax - height[left];
++left;
} else {
ans += rightMax - height[right];
--right;
}
}
return ans;
}
}