单词长度的最大乘积
剑指 Offer II 005. 单词长度的最大乘积 - 力扣(LeetCode)
分析
这里首先要找当方法来简单地判断两个单词是否有公共字母,这里因为只需要判断单词的字母种类,和顺序以及数量都没有关系。而小写字母的数量是有限的,那么我们可以选择位运算预处理每个单词。就是存在某个字母例如b,那么我们就把int类型单词掩码的32位中的第二位变成1。经过这样处理之后每个单词对应一个单词掩码,只需要对两个单词掩码进行与运算,如果两个单词没有共同字母,那么与运算的结果就为0。
位运算解法1
public int maxProduct(String[] words) {
//新建一个int[]用来储存掩码
int[] msk =new int[words.length];
//循环words数组
for (int i = 0; i < words.length; i++) {
int mask = 0;
for (int j = 0; j < words[i].length(); j++) {
mask|=1<<(words[i].charAt(j)-'a');
}
msk[i]=mask;
}
int max =0;
for (int i = 0; i < msk.length; i++) {
for (int j = i+1; j < msk.length; j++) {
if ((msk[i] & msk[j])==0){
//这里直接用的单词的长度相乘就好了,我最开始还搞错了,用bitcount来数数字的1的数量了,但这样实际上是不对的,因为一个单词中一个字母可能出现多次
max = Math.max(max,words[i].length()*words[j].length());
}
}
}
return max;
//这个算法的时间复杂度为O(N^3),空间复杂度为O(N)
//还有个优化方案就是我上述的问题,比如met和meet他们的位掩码都相同,但是还是都记录了一遍,我们这时候需要引入一个map来记录
}
位运算优化(存疑)
这里就是通过hashmap只保留相同掩码最长的单词,但是实际测试发现速度比较慢
public int maxProduct02(String[] words){
Map<Integer,Integer> map = new HashMap<>();
for (int i = 0; i < words.length; i++) {
int mask = 0;
for (int j = 0; j < words[i].length(); j++) {
mask|=1<<(words[i].charAt(j)-'a');
}
map.put(mask,Math.max(map.getOrDefault(mask,0),words[i].length()));
}
int maxProd = 0;
Set<Integer> maskSet = map.keySet();
for (int mask1 : maskSet) {
int wordLength1 = map.get(mask1);
for (int mask2 : maskSet) {
if ((mask1 & mask2) == 0) {
int wordLength2 = map.get(mask2);
maxProd = Math.max(maxProd, wordLength1 * wordLength2);
}
}
}
return maxProd;
}
排序数组中两个数字之和
剑指 Offer II 006. 排序数组中两个数字之和 - 力扣(LeetCode)
分析
我首先想到的就是循环数组的同时,用一个hashmap来记录,同时每次进入循环的时候查询map,如果符合结果那么就返回,如果都没有,那么就返回{0,0},我看官方解法没有提供map的解法,但感觉我这样写法简洁的同时,时间复杂度也只有O(N),额外空间复杂度为O(N)。
Map
public int[] twoSum(int[] numbers, int target) {
Map<Integer,Integer> map =new HashMap<>();
for (int i = 0; i < numbers.length; i++) {
if (map.containsKey(numbers[i])) return new int[]{map.get(numbers[i]),i};
map.put(target-numbers[i],i);
}
return new int[]{0,0};
}
数组中和为 0 的三个数
剑指 Offer II 007. 数组中和为 0 的三个数 - 力扣(LeetCode)
分析
这里万万没想到最复杂的是去重复部分,三个元素的数组,排序打乱,怎么去重复呢,暴力解法当然可以,但最后看题解确实可以在三重循环的时候就规避掉去重复的问题,首先先把数组排序,然后前两重循环自己控制不要两次循环取相同的数,后两重实际上是双指针,最后一重检索到答案就直接返回了,所以不需要去重。
双指针
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> lists = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if ((i>0&&nums[i-1]!=nums[i])||i==0){
for (int j = i+1; j < nums.length; j++) {
if ((j>i+1&&nums[j-1]!=nums[j])||j==i+1){
for (int k = nums.length-1; k >j ; k--) {
if (nums[k]==~(nums[i]+nums[j])+1){
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[j]);
list.add(nums[k]);
lists.add(list);
break;
}
}
}
}
}
}
return lists;
}