学习目标:
第三章哈希表
- 242.有效的字母异位词
- 349. 两个数组的交集
- 202. 快乐数
- 383.赎金信
学习内容:
242.有效的字母异位词
思路:设置一个存放元素出现次数的数组,遍历s字符串时出现的元素在对应数组位置自增1,遍历t字符串时则自减1,确定在数组的下标位置时不用知道’a’对应的具体的ASCII码因为字符在计算时会自动转换为ASCII码对应的值进行计算,例如'a' - 'a' =0。
最后判断存放元素出现次数的数组中的数据是否全为0,若满足则返回true,否则证明s和t肯定有不相同的元素,因此返回false。
public boolean isAnagram(String s, String t) {
//设置大小为26 的数组用来存放每个字母出现的次数
char[] hash = new char[26];
//遍历s字符串时每个字母出现次数加1,遍历t字符串时每个字母出现次数-1
for (int i = 0; i < s.length(); i++) {
hash[s.charAt(i) - 'a']++;//不用直到'a‘的ASCII码,因为字符直接进行减法操作会自动转成ASCII进行计算得到对应的下标,例如:'a' - 'a' =0
}
for (int i = 0; i < t.length(); i++) {
hash[t.charAt(i) - 'a']--;
}
//若最终存放字母出现次数的数组中数据均为0则说明满足字母异位词
for (int i = 0; i < hash.length; i++) {
if(hash[i] != 0){
return false;
}
}
return true;
}
349. 两个数组的交集
(1)思路:使用Hashset,最终将结果强转位数组,将nums1数组中的数据全部存入HashSet中,HashSet会自动去重,随后用nums2数组中的数据与HashSet中的数据进行比较,若HashSet包含则将该元素存入到结果HashSet中,并且能过自动去重,最后将HashSet强制转换为数组。
public int[] intersection_01(int[] nums1, int[] nums2) {
//使用Hashset,最终将结果强转位数组
if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) {
return new int[0];
}
Set<Integer> set1 = new HashSet<>();
Set<Integer> resSet = new HashSet<>();
//将nums1数组中的数据全部存入HashSet中,HashSet会自动去重
for (int i = 0; i < nums1.length; i++) {
set1.add(nums1[i]);
}
for (int i = 0; i < nums2.length; i++) {
if(set1.contains(nums2[i])){//contains方法表示该元素是否包含在该HashSet中
resSet.add(nums2[i]);//若包含则添加进存放结果的HashSet中,自带去重效果
}
}
return resSet.stream().mapToInt(x -> x).toArray();//返回值强制转换位数组类型
}
}
(2)思路:使用Hashset,最终将结果强转位数组,将nums1数组中的数据全部存入HashSet中,HashSet会自动去重,随后用nums2数组中的数据与HashSet中的数据进行比较,若HashSet包含则将该元素存入到结果HashSet中,并且能过自动去重,最后将HashSet强制转换为数组,但转换方式与上述不同。
public int[] intersection_02(int[] nums1, int[] nums2) {
//使用Hashset,最终将结果先转成Intefer类型数组再转成Int类型数组
if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) {
return new int[0];
}
Set<Integer> set1 = new HashSet<>();
Set<Integer> resSet = new HashSet<>();
//将nums1数组中的数据全部存入HashSet中,HashSet会自动去重
for (int i = 0; i < nums1.length; i++) {
set1.add(nums1[i]);
}
for (int i = 0; i < nums2.length; i++) {
if(set1.contains(nums2[i])){//contains方法表示该元素是否包含在该HashSet中
resSet.add(nums2[i]);//若包含则添加进存放结果的HashSet中,自带去重效果
}
}
Integer[] result = resSet.toArray(new Integer[resSet.size()]);
int[] result01 = new int[result.length];
for (int i = 0; i < result01.length; i++) {
result01[i] = result[i];
}
return result01;
}
(3)思路:设置两个较大的数组用来存放对应数据出现的次数,随后通过判断下标相同时两个存储次数数组对应的值是否都大于0,若满足该条件则说明该下标都存在,也就是说原数组中该数据(也就是下标)都有出现过,因此可以存入集合,最终将集合的值赋予数组即可。
public int[] intersection_03(int[] nums1, int[] nums2) {
//设置两个较大的数组用来存放对应数据出现的次数
int[] hash1 = new int[1002];
int[] hash2 = new int[1002];
for (int i = 0; i < nums1.length; i++) {
hash1[nums1[i]]++;
}
for (int i = 0; i < nums2.length; i++) {
hash2[nums2[i]]++;
}
//设置结果集合
List<Integer> resList = new LinkedList<>();
//如果同一下标对应的值都大于0则说明两个数组都存在这个下标也就是原来数组中的数据
for (int i = 0; i < 1002; i++) {
if(hash1[i] > 0 && hash2[i] > 0){
resList.add(i);
}
}
//设置结果数组并且接收集合数据
int[] res = new int[resList.size()];
for (int i = 0; i < res.length; i++) {
res[i] = resList.get(i);
}
return res;
}
202. 快乐数
思路:若该数为1则一定为快乐数;若不为1则创建一个哈希Set,用来存放计算时得出的数据,在根据规则进行计算之后,将新得出的数据与Set中存储的数据进行比较,若包含则说明是死循环则可以返回false说明原数非快乐数,若不包含则继续向下循环进行计算,每次计算要判断各个位数数字的平方和是否为1,若为1则返回true,否则继续循环判断。
public boolean isHappy(int n) {
Set<Integer> nums = new HashSet<>();
//n不为1并且Set不包括n才进入循环,若Set包括n说明已经进行判断过了,再进行判断结果也是一样,也就是死循环了
while(n != 1 && !nums.contains(n)){
nums.add(n);
n = next(n);
//若和为1则返回true
if(n == 1){
return true;
}
}
//若原本是1则直接return true
if(n == 1){
return true;
}
return false;
}
public int next(int n){
int sum = 0;
int temp = 0;
//各个位数之和
while(n > 0){
temp = n % 10;
sum += temp * temp;
n = n / 10;
}
return sum;
}
383.赎金信
暴力解法:
思路:利用双for循环,每遍历magazine中的一个字符就遍历一遍ransomNote,若存在ransomNote的元素与magazine[i]相等,则将ransomNote中的该元素删除,然后i向后移动。如此循环结束后,根据判断ransomNote的长度即可判断出来ransomNote 能不能由 magazine 里面的字符构成。若长度为0则说明ransomNote 能由 magazine 里面的字符构成,否则不能,因为必然存在字符是magazine不包括的。
public static String removeChar(int index,String Str){
//删除ransomNote中被查找到的相同的字符
Str=Str.substring(0,index)+Str.substring(index+1,Str.length());//substring的取值范围是:[,)
return Str;
}
public boolean canConstruct_01(String ransomNote, String magazine) {
//暴力解法
for (int i = 0; i < magazine.length(); i++) {
for (int j = 0; j < ransomNote.length(); j++) {
if(magazine.charAt(i) == ransomNote.charAt(j)){//在magazine中找到ransomNote后将ransomNote的该字符删除,并且i向后移动因为magazine中的字符不能重复使用
ransomNote = removeChar(j,ransomNote);
break;
}
}
}
if(ransomNote.length() == 0){//若ransomNote的长度为0说明所有字符都被删除完成,也就说明magazine中包含全部的ransomNote字符,因此返回true
return true;
}
return false;
}
哈希表思路:
思路:与题目242.有效的字母异位词大体思路一致,设置一个数组用来存放每个元素出现的次数,magazine元素才用加法,ransomNote采用减法,若数组中存在数据小于0则说明ransomNote不能由magazine中的元素构成
public boolean canConstruct_02(String ransomNote, String magazine){
//与题目242.有效的字母异位词大体思路一致,设置一个数组用来存放每个元素出现的次数,magazine元素才用加法,ransomNote采用减法,若数组中存在数据小于0则说明ransomNote不能由magazine中的元素构成
if(ransomNote.length() > magazine.length()){
return false;
}
int[] nums = new int[26];
for (int i = 0; i < ransomNote.length(); i++) {
nums[ransomNote.charAt(i) - 'a']--;
}
for (int i = 0; i < magazine.length(); i++) {
nums[magazine.charAt(i) - 'a']++;
}
for (int i = 0; i < nums.length; i++) {
if(nums[i] < 0){
return false;
}
}
return true;
}
学习时间:
下午四小时。