594. 最长和谐子序列(简单)
和谐数组是指一个数组里元素的最大值和最小值之间的差别 正好是
1
。现在,给你一个整数数组
nums
,请你在所有可能的子序列中找到最长的和谐子序列的长度。数组的子序列是一个由数组派生出来的序列,它可以通过删除一些元素或不删除元素、且不改变其余元素的顺序而得到。
解法一、哈希统计
如果不存在键,放入1。如果存在键,更新数量。如果存在num+1或者-1的键,更新最大值。
java
class Solution {
public int findLHS(int[] nums) {
HashMap<Integer,Integer> hm = new HashMap<>();
int max = 0;
for(int num : nums){
if(!hm.containsKey(num)){
hm.put(num,1);
}else{
hm.put(num,hm.get(num) + 1);
}
if(hm.containsKey(num+1)){
max = Math.max(max,hm.get(num) + hm.get(num+1));
}
if(hm.containsKey(num-1)){
max = Math.max(max,hm.get(num) + hm.get(num-1));
}
}
return max;
}
}
解法二、排序+滑动窗口
滑窗和双指针做法到底什么差别orzzzz
java
class Solution {
public int findLHS(int[] nums) {
Arrays.sort(nums);
int n = nums.length, ans = 0;
for (int i = 0, j = 0; j < n; j++) {
while (i < j && nums[j] - nums[i] > 1) i++;
if (nums[j] - nums[i] == 1) ans = Math.max(ans, j - i + 1);
}
return ans;
}
}
350. 两个数组的交集 II(简单)
给你两个整数数组
nums1
和nums2
,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。
解法一、排序
java
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
List<Integer> res = new LinkedList<>();
Arrays.sort(nums1);
Arrays.sort(nums2);
int i = 0,j = 0;
int n1 = nums1.length,n2 = nums2.length;
while(i < n1 && j < n2){
while(i < n1 && j < n2&& nums1[i] < nums2[j])i++;
while(i < n1 && j < n2 && nums1[i] > nums2[j])j++;
while(i < n1 && j < n2 && nums1[i] == nums2[j]){
res.add(nums1[i]);
i++;
j++;
}
}
int[] r = new int[res.size()];
for(int k = 0;k < res.size();k++){
r[k] = res.get(k);
}
return r;
}
}
解法二、哈希
不过其实感觉如果是哈希的话,直接用数组(桶计数)就挺好的,遍历一个++,遍历另一个--
java
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
if (nums1.length > nums2.length) {
return intersect(nums2, nums1);
}
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int num : nums1) {
int count = map.getOrDefault(num, 0) + 1;
map.put(num, count);
}
int[] intersection = new int[nums1.length];
int index = 0;
for (int num : nums2) {
int count = map.getOrDefault(num, 0);
if (count > 0) {
intersection[index++] = num;
count--;
if (count > 0) {
map.put(num, count);
} else {
map.remove(num);
}
}
}
return Arrays.copyOfRange(intersection, 0, index);
}
}
作者:力扣官方题解
链接:https://leetcode.cn/problems/intersection-of-two-arrays-ii/solutions/327356/liang-ge-shu-zu-de-jiao-ji-ii-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
554. 砖墙(中等)
你的面前有一堵矩形的、由
n
行砖块组成的砖墙。这些砖块高度相同(也就是一个单位高)但是宽度不同。每一行砖块的宽度之和相等。你现在要画一条 自顶向下 的、穿过 最少 砖块的垂线。如果你画的线只是从砖块的边缘经过,就不算穿过这块砖。你不能沿着墙的两个垂直边缘之一画线,这样显然是没有穿过一块砖的。
给你一个二维数组
wall
,该数组包含这堵墙的相关信息。其中,wall[i]
是一个代表从左至右每块砖的宽度的数组。你需要找出怎样画才能使这条线 穿过的砖块数量最少 ,并且返回 穿过的砖块数量 。
解法一、哈希表
本质上还是统计题。需要转化思维,算穿过的砖最少,也就是算穿过缝隙最多。例如,对于用例
wall = [[1,2,2,1],[3,1,2],[1,3,2],[2,4],[3,1,2],[1,3,1,1]]
相当于统计1/3/5/6 、 3/4/6 、 1/4/6 、 2/6 、 3/4/6 、 1/4/5/6 中除了6以外,哪个数字出现最多,这个数字也就是代码里面的max。要注意对于不在边缘划线的条件,需要通过哈希表移除最大值的方式达到,而不能在最后的for循环里设置if。
此外有个细节:多写一个循环遍历哈希表值里最大值,比在第一个for里算要快。
java
class Solution {
public int leastBricks(List<List<Integer>> wall) {
int num = wall.size();
int max = 0,sum = 0;
HashMap<Integer,Integer> hm = new HashMap<>();
for(List<Integer> bricks : wall){
sum = 0;
for(int brick : bricks){
sum+=brick;
if(!hm.containsKey(sum)){
hm.put(sum,1);
}else{
hm.put(sum,hm.get(sum) + 1);
}
}
}
hm.remove(sum);
for(int t : hm.values()){
max = Math.max(max,t);
}
return num - max;
}
}
609. 在系统中查找重复文件(中等)
给你一个目录信息列表
paths
,包括目录路径,以及该目录中的所有文件及其内容,请你按路径返回文件系统中的所有重复文件。答案可按 任意顺序 返回。一组重复的文件至少包括 两个具有完全相同内容的文件。
输入列表中的单个目录信息字符串的格式如下:
"root/d1/d2/.../dm f1.txt(f1_content) f2.txt(f2_content) ... fn.txt(fn_content)"
这意味着,在目录
root/d1/d2/.../dm
下,有n
个文件 (f1.txt
,f2.txt
...fn.txt
) 的内容分别是 (f1_content
,f2_content
...fn_content
) 。注意:n >= 1
且m >= 0
。如果m = 0
,则表示该目录是根目录。输出 是由 重复文件路径组 构成的列表。其中每个组由所有具有相同内容文件的文件路径组成。文件路径是具有下列格式的字符串:
"directory_path/file_name.txt"
解法一、哈希
(备注写在前面)官方做法里的键值对是String--List<String>
遍历给的字符串组,对于每一个固定的字符串,用空格分割。此时的temp[0]是地址,之后的都是文件名和内容。把内容context作为键存入哈希,值则是对应的list中下标,方便每个文件找到自己在res中的位置。
这里有一个问题:如果重复时再存的话,第一次重复需要存两个,其余只需要存一个。所以不妨逆向思维,先把所有的都存进去,再删去不重复的(即size == 1)。
但是,尤其对于size == 1情况下的删除细节,也出过很多问题。最初时尝试了for的int i循环,但由于一边遍历一边删,它的size很快就变小了,也就是会访问过界。第二次时尝试了for-each循环,结果报错java.util.ConcurrentModificationException
java
for(List<String> list : res){
if(list.size() == 1)res.remove(list);
}
经检查,发现是遍历器和remove的删除问题,事故原因溯至轮子(源码)的某个值更新出现差错。于是再进行优化。解决方法有两种,两种都依旧是for的int i循环①每次删除操作后,i--②从后往前遍历。
java
class Solution {
public List<List<String>> findDuplicate(String[] paths) {
List<List<String>> res = new LinkedList<>();
HashMap<String,Integer> hm = new HashMap<>();
for(String s : paths){
String[] temp = s.split(" ");
for(int i = 1;i < temp.length;i++){
String context = temp[i].substring(temp[i].indexOf('(')+1);
StringBuilder sb = new StringBuilder();
sb.append(temp[0]).append('/').append(temp[i].substring(0,temp[i].indexOf('(')));
if(hm.containsKey(context)){//如果已经存在
res.get(hm.get(context)).add(sb.toString());//取出对应
}else{//放进去
hm.put(context,res.size());
List<String> tempList = new LinkedList<>();
tempList.add(sb.toString());
res.add(tempList);
}
}
}
for(int i = res.size()-1;i >= 0;i--){
if(res.get(i).size() == 1)res.remove(i);
}
return res;
}
}
454. 四数相加 II(中等)
给你四个整数数组
nums1
、nums2
、nums3
和nums4
,数组长度都是n
,请你计算有多少个元组(i, j, k, l)
能满足:
0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
解法一、哈希
两两分组判断,时间复杂度O(n^2)
java
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
HashMap<Integer,Integer> hm = new HashMap<>();
int res = 0;
for(int num1 : nums1){
for(int num2 : nums2){
int sumAB = num1 + num2;
if(!hm.containsKey(sumAB)){
hm.put(sumAB,1);
}else{
hm.put(sumAB,hm.get(sumAB)+1);
}
}
}
for(int num3 : nums3){
for(int num4 : nums4){
int sumCD = -num3-num4;
if(hm.containsKey(sumCD)){
res+= hm.get(sumCD);
}
}
}
return res;
}
}
解法二、桶排序
时间复杂度差太多了。。第一个for循环之前确认范围,第一个for循环统计数量,第二个for循环查找符合条件的数量并加入res。原理没变,数据结构优化了
java
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int[] n1 = getMaxMin(nums1);
int[] n2 = getMaxMin(nums2);
int[] n3 = getMaxMin(nums3);
int[] n4 = getMaxMin(nums4);
int maxSum = Math.max(n1[0] + n2[0], -n3[1] - n4[1]);
int minSum = Math.min(n1[1] + n2[1], -n3[0] - n4[0]);
int[] map = new int[maxSum - minSum + 1];
for (int i : nums1) {
for (int j : nums2) {
map[i + j - minSum] ++ ;
}
}
int count = 0;
for (int i : nums3) {
for (int j : nums4) {
count += map[- i - j - minSum];
}
}
return count;
}
public int[] getMaxMin (int[] nums) {
int[] num = Arrays.copyOf(nums, nums.length);
Arrays.sort(num);
int min = num[0];
int max = num[nums.length - 1];
return new int[] {max, min};
}
}
碎碎念
- 594重要的思路转换------能够删除任何数,就是能够选择任何想要的数。滑动窗口很有意思,虽然没搞懂和双指针根本上的差别。应该只是基于双指针实现?这附近埋个思考。
- 554的思路转换很有意思。技术力感觉还好,配得上中等题。
- 609写起来太冗余复杂了,但是也很有趣。确实感觉到细节处理能力上升了,而且一开始打好代码框架确实很重要
- 454看了下题解才学会二二分组来着。
- 其实还有个18,但是太难了,今天没睡午觉有点困,不想做。。