算法之哈希表
有效的字母异位词
思想和滑动窗口找子串很像都是将字符串中的字符统计情况放到数组中,这个不需要进行窗口的移动,因为两个的长度必须相同。
快乐数
没有注意到提示无限循环,即当平方和重复出现时,不可能为快乐数,此时使用set对平方和进行存储,只有和前面都不重复时才会将此时的平方和放到set中,若重复则不为快乐数,当平方和为1时则为快乐数。
查看代码
public boolean isHappy(int n) {
Set<Integer> record=new HashSet<>();
while (n!=1 && !record.contains(n)){
record.add(n);
int sum=0;
while (n!=0){
int temp=n%10;
sum+=temp*temp;
n=n/10;
}
n=sum;
}
if (n==1){
return true;
}else {
return false;
}
}
两数之和(使用减法)
暴力求解要两层循环,使用map可以只遍历一次,哈希表可以用来快速判断所找的值存不存在
本题中,使用map的key部分用来存放数值,value部分用来存放下标。
每指向数组中的一个元素,就去map中查找对应的target减去元素值,如果有则找到,如果没有将该数组元素的值存到map中。
其中java操作:map.get("key")得到key对应的value值
map.put("key","value")向map中添加键值对
map.contain("key")查找该key对应的键值对在不在map中
查看代码
public int[] twoSum(int[] nums, int target) {
HashMap<Integer, Integer> numIndex = new HashMap<>();
int[] result=new int[2];
for (int i = 0; i < nums.length; i++) {
int number=target-nums[i];
if (numIndex.containsKey(number)){
result[0]=numIndex.get(number);
result[1]=i;
}else {
numIndex.put(nums[i],i);
}
}
return result;
}
四数相加
和“两数之和”的区别在于将两个数组的和存放在map里面的key中,和出现的次数存放在map里面的key中。
查看代码
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums1.length; i++) {
for (int j = 0; j < nums2.length; j++) {
int sum12=nums1[i]+nums2[j];
if (map.containsKey(sum12)){
Integer value = map.get(sum12);
map.put(sum12,value+1);//不能写成value++
}else {
map.put(sum12,1);
}
}
}
int count =0;
for (int i = 0; i < nums3.length; i++) {
for (int j = 0; j < nums4.length; j++) {
int sum34=nums3[i]+nums4[j];
if (map.containsKey(-sum34)){
count=count+map.get(-sum34);
}
}
}
return count;
}
出现问题:不能写成value++,这样会先用原来的值把map更新,然后再将value+1;
三数之和
此题最好使用快慢指针,使用哈希表很难判断里面的元素到底重不重。
原因:如果按照四数相加的思想先将数组中的元素所有都不重复的两两相加,将结果保存在map中作为key,对应两个数的下标作为value,第三个数选择不同于这两个下标的数,然后将结果和为0的下标放入结果中,这么做判重会非常复杂,没法保证List<List>中元素List里的数不相同,只能保证下标都不相同。
而二数之和和四数之和一个是返回下标,另一个是满足条件的次数,不需要满足数值不重复,则只用哈希表即可。
其中Java基础:for中每次循环最后都会i++,即便contine了,i++也会执行
数组升序排序:Arrays.sort(nums)
查看代码
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result=new ArrayList<>();
Arrays.sort(nums);//对数组进行升序排序
int left;
int right;
for (int i=0;i<nums.length;i++){
left=i+1;
right=nums.length-1;
if (i>0 && nums[i]==nums[i-1]){
continue;
}
while (left<right){
if (nums[i]+nums[left]+nums[right]>0){
right-=1;
} else if (nums[i]+nums[left]+nums[right]<0) {
left+=1;
}else {
result.add(Arrays.asList(nums[i],nums[left],nums[right]));
while (nums[left]==nums[left+1] && left+1<nums.length-1){
left+=1;
}
left++;
right--;
}
}
}
return result;
}
四数相加
思路和三数相加相同,因为要求也是输出不重复的结果
遇到的问题:
- 当指针所指向的四个数之和满足target之时,进行完while重复判断之后,需要再单独的right--,left++。因为此时只是找到了不重复的前一个值而不是当前值,需要再进行操作使两个指针达到不重复值。
- 在重复判断时,必须要将left<right的判断条件放在前面,这样如果前者不满足情况将不会再判断后者。
- 当数值相加以及超过int型时,此时判断可能出错,比如当数组为1000000000,1000000000,1000000000,1000000000,而target为-294967296,此时判断相等,因此需要在判断中强制转换int类型为long,java中long占64位(8个字节),int占32位(4个字节)
- 关于不重复的判断,必须要是nums[i]==nums[i-1]而不是能是nums[i]==nums[i+1]。
查看代码
public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
List<List<Integer>> result=new ArrayList<>();
int left;
int right;
for (int i = 0; i < nums.length; i++) {
if (i>0 &&nums[i]==nums[i-1]){
continue;
}
for (int j = i+1; j <nums.length ; j++) {
if (j-1>i && nums[j]==nums[j-1]){
continue;
}
left=j+1;
right=nums.length-1;
while (left<right){
if ((long)nums[left]+nums[right]+nums[i]+nums[j]>(double) target){
right--;
}else if ((double)nums[left]+nums[right]+nums[i]+nums[j]<(double)target){
left++;
}else {
result.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));
while (left<right&& nums[left]==nums[left+1]) left++;//left<roght必须放在前面
while (left<right&&nums[right]==nums[right-1]) right--;
left++;//因为上面只是用来找到不重复的前一个值,而不是不重复的当前值 所以最后还是要再left++right--
right--;
}
}
}
}
return result;
}