内容:哈希和双指针
个人总结:
1. 哈希用来找某一个数,数组可以用同一个索引进行加减,找到有没有相同的数,但是受限于长度不能太长;Set和Map都有对应的containsKey方法,Set可以去重,Map可以存储键值对.
2. 很多时候,用于减少时间复杂度,数据需要重复利用,避免重复在遍历,于是先把数据存入哈希当中,再根据哈希能快速找到某个值的特性,能减少一定的时间复杂度。
3.三数之和和四数之和,其实有点像二分查找,同样需要排序,定义左节点和右节点。不同的是,mid被替代了。很重要的一点就是去重,去重不能马虎。
一、四数相加
将四个数拆分成两组,相当于变成了两数相加,时间复杂度o(n^4) 变为 o(n^2)
public class FourSumCount {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
// 已知四个数组长度为n, 求有多少个这样的i,j,k,l,满足加起来为0
Map<Integer, Integer> map = new HashMap<>();
for (int a : nums1) {
for (int b : nums2) {
map.put(a + b, map.getOrDefault(a + b, 0) + 1); // 找a+b的value,没有默认为0
}
}
int count = 0;
for (int a : nums3) {
for (int b : nums4) {
if (map.containsKey(-a - b)) {
count += map.getOrDefault(-a - b, 0);
// count += map.get(-a-b);
}
}
}
return count;
}
}
二、赎金信(就是字母异或分组的扩展)
public class CanConstruct {
// 由小写英文字母构成,第二个数的字母在第一个数中存不存在
public boolean canConstruct(String ransomNote, String magazine) {
int [] arr = new int[26];
for(int j = 0;j < magazine.length();j++){
arr[magazine.charAt(j) - 'a'] += 1;
}
for(int i = 0;i < ransomNote.length();i++){
arr[ransomNote.charAt(i) - 'a'] -= 1;
}
for(int i : arr){
if(i<0){
return false;
}
}
return true;
}
}
三、三数之和(二刷)
两个月前做的题目,今天重新做了,很多都忘记了,看了视频重新自己敲代码,发现一刷很多咩注意的点,也算是收获满满吧
public class ThirdSum {
public static List<List<Integer>> threeSum(int[] nums) {
// 先排序,便于利用双指针
Arrays.sort(nums);
int j;
int k;
List<List<Integer>> all_list = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
if(nums[i] > 0){
break;
}
j = i +1 ;
k = nums.length -1;
// 去重
/**
* 去重很关键,注意这里是i-1, 不能是i+1
*/
if (i >0 && nums[i] == nums[i-1]) continue;
while(j < k){
int sum = nums[i] + nums[j] + nums[k];
/**
* 去重
*/
while(nums[j] == nums[j+1]){ j++;}
while(nums[k] == nums[k-1]){ k--;}
if (sum == 0){
ArrayList<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[j]);
list.add(nums[k]);
all_list.add(list);
j++;
k--;
}else if(sum < 0){
j++;
}else {
k--;
}
}
}
return all_list;
}
四、四数之和(二刷)
public class FourSum {
public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
List<List<Integer>> all_list = new ArrayList<>();
/**
* 比三数之和的不同就是多了一步,固定两个数
*/
for (int i = 0;i<nums.length;i++){
for (int j = i+1;j<nums.length;j++){
// 减枝操作
if(nums[i] > target){
return all_list;
}
int left = j+1;
int right = nums.length-1;
// TODO: 2024/5/15 去重
if (i>0 && nums[i] == nums[i-1]) continue; // TODO: 2024/5/15 这里不用while,因为不改变条件
if (nums[j] == nums[j-1]) continue;
int sum = nums[i]+nums[j]+nums[left]+nums[right];
while (left<right){
if (sum == target){
ArrayList<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[j]);
list.add(nums[left]);
list.add(nums[right]);
all_list.add(list);
while (right > left && nums[right] == nums[right - 1]) right--; // right>left条件要加上
while (right > left && nums[left] == nums[left + 1]) left++;
left++;
right--;
}else if(sum < target){
left++;
}else {
right--;
}
}
}
}
return all_list;
}
}