总结
总结

寻找重复数 leetcode hot100 最后一题

思路:当成链表找环处理。每到一个位置i 那下一次就到arr[i]对应的位置。显然入环节点就是重复的数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public int findDuplicate(int[] nums) {
int kuai = 0;
int man = 0;
while(true){
kuai = nums[nums[kuai]];
man = nums[man];
if(kuai == man){
kuai = 0;
while(kuai != man){
kuai = nums[kuai];
man = nums[man];
}
return kuai;
}
}
}

接雨水

思路:初始版,类似与前缀和跟后缀和,我开两个数组记录i左边的最大值和右边的最大值,然后求出当前位置能接的雨水。sum+=Math.max(0,Math.min(left[i],right[i])-nums[i])

优化: 双指针思路,左右两边各一个指针。并且维护两个变量记录,左指针左边的最大值和右指针右边的最大值。对于左指针来说左边的最大值肯定是确定的,右指针同理。所以只需要判断两边谁的最大值更小,那么这一边就可以结算了,并且移动指针,更新最大值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public int trap(int[] height) {
int l =1,r=height.length-2,lmax = height[0],rmax = height[height.length-1],sum=0;
while(l<=r){
if(lmax>=rmax){
//结算右边
sum+=Math.max(0,rmax-height[r]);
rmax = Math.max(rmax,height[r--]);
}else {
//结算左边
sum+=Math.max(0,lmax-height[l]);
lmax= Math.max(lmax,height[l++]);
}
}
return sum;
}

胖娃坐船

给定数组 people 。people[i]表示第 i 个人的体重 ,船的数量不限,每艘船可以承载的最大重量为 limit。

每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit。

返回 承载所有人所需的最小船数 。

思路:排序,然后双指针,一个指针指向最轻的,一个指针指向最重的,如果最轻的+最重的<=limit,那么可以一起载,两个指针同时移动,ans++。否则,最重的指针移动,ans++。即胖子自己一个船。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public int numRescueBoats(int[] people, int limit) {
Arrays.sort(people);
int n =people.length;
int l =0;
int r =n-1;
int ans = 0;
while(l<=r){
if(people[r]+people[l] <= limit){
r--;
l++;
}else {
r--;
}
ans++;
}
return ans;
}

变种:如果要求两个的体重加起来等于偶数呢? 很简单,分为奇偶两个数组,然后分别处理,把两个结果加起来即可。

供暖器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public int findRadius(int[] houses, int[] heaters) {
Arrays.sort(houses);
Arrays.sort(heaters);

int result = 0;
int heaterIndex = 0;

for (int house : houses) {
// 找到离当前房屋最近的加热器
while (heaterIndex < heaters.length - 1 &&
Math.abs(heaters[heaterIndex] - house) >=
Math.abs(heaters[heaterIndex + 1] - house)) {
heaterIndex++;
}

result = Math.max(result, Math.abs(heaters[heaterIndex] - house));
}

return result;
}

缺失的第一个正数

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

思路:我现在就想让每个位置都对应,即i位置上放的就是i+1。此时,我维护两个指针,l的左边是满足我这个条件的,r的右边是肯定不满足的垃圾。初始化l=0,r=n。

现在开始遍历:每次都观察l位置上的是否满足,如果满足那就l++。如果不满足判断它是不是我需要的,即它在不在当前l到r范围内(因为小于l的已经就位了,不需要你,大于r的已经超过了,不需要你)。关于为什么大于r就算超过这里解释一下。因为理想状态下我数组长度为n那么我就有1-n这些数,但一旦一个数不满足(r—),那我肯定到不了n了,最大就是r。还有一种情况,现在我需要你,但是在num[num[l]-1] 已经有一个相同的你了,说明你重复了。所以也会被当成垃圾。以上三种情况会被当成垃圾,跟—r位置交换。剩下最后一种情况,你是我需要的而且你还没到应该到的位置,于是我把你交换到对应的位置。下次循环,继续看i位置的数。 直到l<r。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
   // 时间复杂度O(n),额外空间复杂度O(1)
public static int firstMissingPositive(int[] arr) {
// l的左边,都是做到i位置上放着i+1的区域
// 永远盯着l位置的数字看,看能不能扩充(l++)
int l = 0;
// [r....]垃圾区
// 最好的状况下,认为1~r是可以收集全的,每个数字收集1个,不能有垃圾
// 有垃圾呢?预期就会变差(r--)
int r = arr.length;
while (l < r) {
if (arr[l] == l + 1) {
l++;
} else if (arr[l] <= l || arr[l] > r || arr[arr[l] - 1] == arr[l]) {
swap(arr, l, --r);
} else {
swap(arr, l, arr[l] - 1);
}
}
return l + 1;
}