二刷:
(最长连续序列,三数之和):大循环外层,一个一个元素判断,具体的判断逻辑使用到哈希或双指针
接雨水:双指针法再看
哈希统计频数 或 去重--hot100堆的题
一:哈希
1.两数之和
哈希表,差值在数组里
class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer,Integer> set =new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (set.containsKey(target-nums[i])) {
return new int[]{set.get(target-nums[i]),i};
}
set.put(nums[i], i);
}
return new int[]{0,0};
}
}
2.字母异位词分组
遍历字符串数组,对于每个字符串,将其转换为字符数组,并对字符数组进行排序。 然后将排序后的字符数组转换为字符串,并将其作为键,将原始字符串添加到对应键的值列表中。 最后将哈希表中的值列表转换为结果列表返回。
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
HashMap<String,List<String>> map =new HashMap<>();
for (String str : strs) {
char[] arr=str.toCharArray();
Arrays.sort(arr);
//将排序后的字符串作为哈希表中的key值
String key = String.valueOf(arr);
List<String> result=new ArrayList<>();
if (!map.containsKey(key)) {
map.put(key, result);
}
map.get(key).add(str);
}
return new ArrayList(map.values());
}
}
Map通常用于存储键值对,其中每个键映射到一个值。当我们尝试访问一个不存在的键时,Map会返回null值。如果map的value值为空,就会抛出空指针异常。
getOrDefault()方法的语法如下:
该方法接受两个参数:要查找的键和默认值。如果键存在,则返回与该键关联的值,否则返回指定的默认值。
(无key键存在,返回指定的默认值,
List<String> result= map.getOrDefault(key, new ArrayList<String>());
map.getOrDefault(key, new ArrayList<String>());获取key的值,是new ArrayList<String>(),
存在 result中)
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
HashMap<String,List<String>> map =new HashMap<>();
for (String str : strs) {
char[] arr=str.toCharArray();
Arrays.sort(arr);
//将排序后的字符串作为哈希表中的key值
String key = String.valueOf(arr);
//getOrDefault避免空指针异常
List<String> result= map.getOrDefault(key, new ArrayList<String>());
result.add(str);
map.put(key, result);
}
return new ArrayList(map.values());
}
}
3.最长连续序列
class Solution {
public int longestConsecutive(int[] nums) {
HashMap<Integer,Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
map.put(nums[i], i);
}
int result=0;
for (int num : nums) {
int mid=num;
if (!map.containsKey(mid-1)) {
while (map.containsKey(mid+1)) {
mid++;
}
}
result=Math.max(result, mid-num+1);
}
return result;
}
}
Set set=new HashSet();
class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer> set =new HashSet<>();
for (int num : nums) {
set.add(num);
}
int result=0;
for (int num : nums) {
int cur=num;
if (!set.contains(cur-1)) {
while (set.contains(cur+1)) {
cur++;
}
}
result=Math.max(result, cur-num+1);
}
return result;
}
}
1050ms,没提升。
前面都搞错了,建立了哈希set,但是后面查询还是在nums数组里查,中间那个for写错了。
class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer> set =new HashSet<>();
for (int num : nums) {
set.add(num);
}
int result=0;
for (Integer num : set) {
int cur=num;
if (!set.contains(cur-1)) {
while (set.contains(cur+1)) {
cur++;
}
}
result=Math.max(result, cur-num+1);
}
return result;
}
}
二:双指针
双指针法(快慢指针法):
通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。定义快慢指针:
快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
慢指针:指向更新 新数组下标的位置
解题:(1)同一端出发,还是两端出发
(2)快指针:谁先动,满足某个条件,先动的是快
1.移动零
本题中:从同一端出发,
快指针去找0,慢指针走非0的元素,找到0后,快慢指针交换元素
class Solution {
public void moveZeroes(int[] nums) {
int slow=0;
for (int fast = 0; fast < nums.length; fast++) {
if (nums[fast]!=0) {
int mid =nums[fast];
nums[fast]=nums[slow];
nums[slow]=mid;
slow++;
}
}
}
}
2.盛最多水的容器
体积等于 高*底长
移动两端小的数的指针,减少底长,高不变。再比较移动后与移动前的体积大小。
class Solution {
public int maxArea(int[] height) {
int V =0;
int left=0;
int right=height.length-1;
while (left<=right) {
int V_mid=0;
if (height[left]<height[right]) {
V_mid=height[left]*(right-left);
left++;
}else{
V_mid=height[right]*(right-left);
right--;
}
V=Math.max(V, V_mid);
}
return V;
}
}
class Solution {
public int maxArea(int[] height) {
int V =0;
int left=0;
int right=height.length-1;
while (left<=right) {
if (height[left]<height[right]) {
//V_mid=height[left]*(right-left);
V=Math.max(V, height[left]*(right-left));
left++;
}else{
//V_mid=height[right]*(right-left);
V=Math.max(V, height[right]*(right-left));
right--;
}
}
return V;
}
}
3.三数之和
哈希解法
两层for循环就可以确定 a 和b 的数值了,可以使用哈希法来确定 0-(a+b) 是否在 数组里出现过,其实这个思路是正确的,但是我们有一个非常棘手的问题,就是题目中说的不可以包含重复的三元组。把符合条件的三元组放进vector中,然后再去重,这样是非常费时的,很容易超时,也是这道题目通过率如此之低的根源所在。
双指针解法
两层循环O(n2)。先对数组排序。
最外层循环i,从0开始。内层循环,左右两个指针left,right.如果i,left,right的和大于0,左移right,使3者的和变小。当left>=right结束内层循环。
快速创建List集合,并快速的添加元素
使用Arrays的asList()方法,直接返回一个集合
List<String> stringList = Arrays.asList("张三", "李四", "王五");
// 遍历集合
stringList.forEach(name -> System.out.println(name)); // JDK8新特性
初始化添加元素
List<String> list = new ArrayList<String>(){
{
this.add("a");
this.add("b");
this.add("c");
}
};
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> set =new ArrayList<>();
if(nums == null || nums.length < 3) return set;
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if(nums[i] > 0) break;
// 如果当前数字大于0,则三数之和一定大于0,所以结束循环
if(i > 0 && nums[i] == nums[i-1]) continue;
// 去重,相邻重复的跳过找三数和的操作
int left=i+1;
int right=nums.length-1;
while (left<right) {
int sum=nums[i]+nums[left]+nums[right];
if (sum<0 ) {
left++;
}else if (sum>0) {
right--;
}else {
set.add(Arrays.asList(nums[i],nums[left],nums[right]));
while(left<right&&nums[left]==nums[left+1]) left++;//去重
while(left<right&&nums[right]==nums[right-1]) right--;//去重
//找到一组后,两边要同时移动,只移一边肯定不相等
left++;
right--;
}
}
}
return set;
}
}
//代码简写
if (sum<0 ) left++;
else if (sum>0) right--;
4.接雨水
给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
方法一:朴素解法
对每个元素,左边,右边搜素找最大值,再用二者间的最小值与当前元素做差,即为当前能接到的水。
class Solution {
public int trap(int[] height) {
int V=0;
for (int i = 1; i < height.length-1; i++) {
int L_M=0;
int R_M=0;
for (int j = i-1; j >=0; j--) {
L_M=Math.max(L_M, height[j]);
}
for (int j = i+1; j < height.length; j++) {
R_M=Math.max(R_M, height[j]);
}
int Min_LR=Math.min(L_M, R_M);
if (Min_LR>height[i]) {
V=V+Min_LR-height[i];
}
}
return V;
}
}
方法二:双指针
遍历每个元素的同时,两边找最大值,计算该点处的蓄水量
class Solution { public int trap(int[] height) { int left=0; int right=height.length-1; int LMVal=0; int RMVal=0; int sum=0; while (left<=right) { if (height[left]<=height[right] ) { //左边比右边小,从左边开始找 LMVal=Math.max(LMVal, height[left]); sum+=LMVal-height[left]; left++; }else{ RMVal=Math.max(RMVal, height[right]); sum+=RMVal-height[right]; right--; } } return sum; } }