哈希表
有效的字母异位词
哈希表
这个题目的基本思路就是把字符串里面的字符转化成字节储存在字节数组之中,然后遍历字节数组,放到长度为26的数组之中,每个索引位置对应1个字节,比如索引0对应的就是字符a,因为ASCII码减去了a的ASCII码,如果是是a,那就是0.因此26个字母可以对应到这数组的26个索引位置上。第一条字符串 字符,出现一次就在相应的位置加1 ,第二条出现就在上面减一。如果最后面都是0,说明大家所包含的字母是一样的,因为全部抵消了。
注意for后面是用的增强for。
1.只适合取数据,不能更改数据;
2.只用于数组,或实现Iterable接口的集合类上;set,list。
class Solution {
public boolean isAnagram(String s, String t) {
int[] record= new int[26];
for(char c: s.toCharArray()){
record[c - 'a']++;
}
for(char c: t.toCharArray()){
record[c - 'a']--;
}
for(int i:record){
if(i!=0){
return false;
}/*else{
return true;
}*/
}
return true; //这里必须要有返回值语句,否则会报错,即使前面for循环里面写if else一定会返回一个值
//为什么呢??????
}
}
至于为什么一定要加这个,我也还不是很清楚,到时候回过头来看看。
直接对比排序后的字符串就好了
要想排序先转化为字符数组,用sort进行排序
class Solution {
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
char[] str1=s.toCharArray();
char[] str2=t.toCharArray();
Arrays.sort(str1);
Arrays.sort(str2);
//return str1.equals(str2);
return Arrays.equals(str1,str2);
}
}
特别注意str1.equals(str2)和Arrays.equals(str1,str2)
不一样,前者和“==”相同,除非重写?貌似,后者是比较内容,不是比较地址。
两个数组的交集
哈希表
看每个元素是唯一的,这符合set的特性,不重复。可以遍历两个数组,把值储存在哈希表中,然后遍历短的哈希Set,把重复的值储存在一个哈希set中(不储存在数组之中是因为数组要提前定义大小,而且数组是可以重复的,set不会重复)。之后把这个set的值放在数组之中。
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
Set<Integer> set1= new HashSet<Integer>();
Set<Integer> set2= new HashSet<Integer>();
for(int num:nums1){
set1.add(num);
}
for(int num:nums2){
set2.add(num);
}
return intersectionset(set1,set2);
}
public int[] intersectionset(Set<Integer> set1, Set<Integer> set2){
Set<Integer> set3= new HashSet<Integer>();
if (set1.size()>set2.size()){
return intersectionset(set2,set1);
}
for (int i: set1){
if(set2.contains(i)){
set3.add(i);
}
}
int[] samearr= new int[set3.size()];
int index=0;
for(int i:set3){
samearr[index++]=i;
}
return samearr;
}
}
samearr[index++]=i 这个是先进行操作再赋值
因为不重复,所以可以把他们放在set之中。
快乐数
根据题目描述,要不就是最后变成1,要不就是一直循环,**也就是会出现相同的数。遇到这种情况,最先想到哈希表,**想到set,把和储存在set里面,一旦contains了也就是重复了,就证明不是开心数。
另外一个基础操作就是提取不同位置的数,比如三位数提取个十百位上的数字。就是最后定义的这个getsum这个函数里面有。%是取余,/是除,如果是2.8那么取2,最小整数。
class Solution {
public boolean isHappy(int n) {
Set<Integer> record = new HashSet<Integer>();
while(n!=1 && !record.contains(n)){
record.add(n);
n=getSum(n);
}
return n==1;
}
public int getSum(int n){
int res=0;
while(n>0){
int temp=n%10;
res+=temp*temp;
n=n/10;
}
return res;
}
}
两数之和
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] res= new int[2];
//if(nums==null||nums.length==0){
// return res;
//其实不需要上面这两句,因为题目里面说了长度大于等于2,况且如果是空的,那么也不会有两数之和。
//}
Map<Integer, Integer> map=new HashMap<Integer, Integer>();
for(int i=0;i<nums.length;i++){
int temp=target-nums[i];
if(map.containsKey(temp)){
res[1]=i;
res[0]=map.get(temp);
}
map.put(nums[i],i);
}
return res;
}
}
四数相加
还是利用哈希表的map来储存,之所以用map不用Set,是因为map可以储存两个值,一个是和,一个是出现的次数。
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer, Integer> map = new HashMap<>();
int temp;
int res = 0;
//统计两个数组中的元素之和,同时统计出现的次数,放入map
for (int i : nums1) {
for (int j : nums2) {
temp = i + j;
if (map.containsKey(temp)) {
map.put(temp, map.get(temp) + 1); //键是相加之和,值是出现的次数。因为即使是相同的和,他们对应的索引是不同的,所以要计算上。
} else {
map.put(temp, 1);
}
}
}
//统计剩余的两个元素的和,在map中找是否存在相加为0的情况,同时记录次数
for (int i : nums3) {
for (int j : nums4) {
temp = i + j;
if (map.containsKey(0 - temp)) {
res += map.get(0 - temp); //如果有相同的,就把次数累加上。get获取的是值。如果还出现相同的和,那么也会循环计数的。
}
}
}
return res;
}
}
赎金信
和第一道题基本一样,只是这里magazine的字符串可以更多,但是需要包含ransomNote的所有字符,按照第一题的解法,就是可以出现负数。
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
// 定义一个哈希映射数组
int[] record = new int[26];
// 遍历
for(char c : magazine.toCharArray()){
record[c - 'a'] += 1;
}
for(char c : ransomNote.toCharArray()){
record[c - 'a'] -= 1;
}
// 如果数组中存在负数,说明ransomNote字符串总存在magazine中没有的字符
for(int i : record){
if(i < 0){
return false;
}
}
return true;
}
}
三数之和
和两数之和一样,极其常考
这个循环太多了,目前不想自己敲一遍。
但是理解了,和哈希表没有任何关系,,用的是双指针
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int n = nums.length;
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<List<Integer>>();
// 枚举 a
for (int first = 0; first < n; ++first) {
// 需要和上一次枚举的数不相同
if (first > 0 && nums[first] == nums[first - 1]) {
continue;
}
// c 对应的指针初始指向数组的最右端
int third = n - 1;
int target = -nums[first];
// 枚举 b
for (int second = first + 1; second < n; ++second) {
// 需要和上一次枚举的数不相同
if (second > first + 1 && nums[second] == nums[second - 1]) {
continue;
}
// 需要保证 b 的指针在 c 的指针的左侧
while (second < third && nums[second] + nums[third] > target) {
--third;
}
// 如果指针重合,随着 b 后续的增加
// 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
if (second == third) {
break;
}
if (nums[second] + nums[third] == target) {
List<Integer> list = new ArrayList<Integer>();
list.add(nums[first]);
list.add(nums[second]);
list.add(nums[third]);
ans.add(list);
}
}
}
return ans;
}
}
四数之和
在上一个的题目基础上加一重循环,再加一些剪枝操作
直接抄的官网,有时间再自己看一下,乏了
写课题组要求的教材去
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> quadruplets = new ArrayList<List<Integer>>();
if (nums == null || nums.length < 4) {
return quadruplets;
}
Arrays.sort(nums);
int length = nums.length;
for (int i = 0; i < length - 3; i++) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
if ((long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
break;
}
if ((long) nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {
continue;
}
for (int j = i + 1; j < length - 2; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
if ((long) nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
break;
}
if ((long) nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target) {
continue;
}
int left = j + 1, right = length - 1;
while (left < right) {
int sum = nums[i] + nums[j] + nums[left] + nums[right];
if (sum == target) {
quadruplets.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
left++;
while (left < right && nums[right] == nums[right - 1]) {
right--;
}
right--;
} else if (sum < target) {
left++;
} else {
right--;
}
}
}
}
return quadruplets;
}
}
注意强转为long,因为可能会超过Int范围