题目:242. 有效的字母异位词
给定两个字符串 s
和 t
,编写一个函数来判断 t
是否是 s
的字母异位词。
注意:若 s
和 t
中每个字符出现的次数都相同,则称 s
和 t
互为字母异位词。
样例:
输入: s = "anagram", t = "nagaram" 输出: true 输入: s = "rat", t = "car" 输出: false
Map 实现
思路,使用HashMap 存入每一个 s 字符串的字符,如果存在则值加一,不存在则创建;遍历 t 字符串,如果存在则减一; 最后查看是否存在值不为0的key。
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
Map<Character, Integer> map = new HashMap<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
char d = t.charAt(i);
if (c == d) {
continue;
} else {
map.put(c, map.getOrDefault(c, 0) + 1);
if(map.containsKey(d)){
map.put(d, map.get(d)-1);
}else{
map.put(d, -1);
}
}
}
for (char key : map.keySet()) {
if(map.get(key) != 0){
return false;
}
}
return true;
}
数组实现
思路:该解法与上面 Map实现 一样,同样是遍历,但是解法考虑到字符的ASCII 值,因此可以创建固定长度的数组来解决问题
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
int[] alpha = new int[26];
for (int i = 0; i < s.length(); i++) {
alpha[s.charAt(i) - 'a']++;
alpha[t.charAt(i) - 'a']--;
}
for (int i = 0; i < 26; i++) {
if (alpha[i] != 0)
return false;
}
return true;
}
剪枝实现
剪枝:剪枝的目的是通过提前终止某些计算分支或减少搜索空间,以避免无效的计算或搜索不必要的路径,从而优化算法的执行时间或减少内存消耗。
思路:在数组解法的基础上,可以通过判断,提前走出循环搜索
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
int[] alpha = new int[26];
for (int i = 0; i < s.length(); i++) {
alpha[s.charAt(i) - 'a']++;
}
for (int i = 0; i < t.length(); i++) {
alpha[t.charAt(i) - 'a']--;
if (alpha[t.charAt(i) - 'a'] < 0) {
return false;
}
}
return true;
}
题目:349. 两个数组的交集
给定两个数组 nums1
和 nums2
,返回 它们的 交集。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
样例:
输入:nums1 = [1,2,2,1], nums2 = [2,2] 输出:[2]输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4] 输出:[9,4] 或 [4,9]
集合实现
思路:分别对两个数组进行遍历,将其中的数据存入Set 集合中。其中第二个集合判断数据是否存在于第一个集合中,存在则加入,最后将第二个集合转为数组返回。
public int[] intersection(int[] nums1, int[] nums2) {
if (nums1 == null || nums2 == null || nums1.length == 0 || nums2.length == 0) {
return new int[0];
}
Set<Integer> set1 = new HashSet<Integer>();
Set<Integer> set2 = new HashSet<Integer>();
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])) {
set2.add(nums2[i]);
}
}
return set2.stream().mapToInt(x -> x).toArray();
}
双指针
思路:将数组有序化后,使用双指针进行移动
public int[] intersection(int[] nums1, int[] nums2) {
Arrays.sort(nums1);
Arrays.sort(nums2);
int len1 = nums1.length;
int len2 = nums2.length;
int[] res = new int[len1 + len2];
int index = 0, index1 = 0, index2 = 0;
while (index1 < len1 && index2 < len2) {
int num1 = nums1[index1], num2 = nums2[index2];
if (num1 == num2) {
if (index == 0 || num1 != res[index - 1]) {
res[index++] = num1;
}
index1++;
index2++;
} else if (num1 < num2) {
index1++;
} else {
index2++;
}
}
return Arrays.copyOfRange(res, 0, index);
}
剪枝
思路:根据提示,数组大小控制在一定范围内,因此可以使用数组来模拟hash, 与 242.有效的字母异位词 一样
public int[] intersection(int[] nums1, int[] nums2) {
int hash[]=new int[1001];
int []res=new int[nums1.length+nums2.length];
for (int i = 0; i <nums1.length ; i++) {
hash[nums1[i]]=1;
}
int t=0;
for (int i = 0; i <nums2.length ; i++) {
if(hash[nums2[i]]==1){
hash[nums2[i]]=0;
res[t++]=nums2[i];
}
}
res=Arrays.copyOf(res,t);
return res;
}
题目:202. 快乐数
样例:
输入:n = 19 输出:true 解释: 12 + 92 = 82 82 + 22 = 68 62 + 82 = 100 12 + 02 + 02 = 1输入:n = 2 输出:false
暴力+Set 判断
思路:先遍历获取每一步每一位的值,然后把值加入 set 集合中,如果 set 中存在这个数,则说明原始值在算法中不断循环,即得不到1, 返回false
public boolean isHappy(int n) {
Set<Integer> set = new HashSet<>();
int tmp = getSum(n);
while (!set.contains(tmp)) {
if(set.size() == 0){
set.add(tmp);
}
if (tmp == 1) {
return true;
}
set.add(tmp);
tmp = getSum(tmp);
}
return false;
}
public int getSum(int num) {
int sum = 0;
while (num != 0) {
int remain = num % 10;//余数
num = num / 10;
sum += remain * remain;
}
return sum;
}
官方题解-快慢指针:
思路: 在上面解法的基础上进行优化,类似 第四天中的环形链表 ,使用快慢指针,考虑到如果存在循环,那么两个指针必定相遇,然后判断相遇的值即可。
public boolean isHappy(int n) {
int slowRunner = n;
int fastRunner = getSum(n);
while (fastRunner != 1 && slowRunner != fastRunner) {
slowRunner = getSum(slowRunner);
fastRunner = getSum(getSum(fastRunner));
}
return fastRunner == 1;
}
public int getSum(int num) {
int sum = 0;
while (num != 0) {
int remain = num % 10;//余数
num = num / 10;
sum += remain * remain;
}
return sum;
}
题目:1. 两数之和
样例:
输入:nums = [2,7,11,15], target = 9 输出:[0,1] 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。输入:nums = [3,3], target = 6 输出:[0,1]
暴力
时间复杂度O(n*2)
思路:双重 for 循环遍历, 判断指定数是否存在数组中
public int[] twoSum(int[] nums, int target) {
int tmp;
for (int i = 0; i < nums.length - 1; i++) {
tmp = target - nums[i];
for (int j = i + 1; j < nums.length; j++) {
if (nums[j] == tmp) {
return new int[] { i, j };
}
}
}
return new int[] { -1, -1 };
}
Hash
思路:使用 hash 表来减少一次 for 循环遍历 target - nums[i] 是否存在
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int i=0; i<nums.length; i++){
if(map.containsKey(target-nums[i])){
return new int[]{map.get(target-nums[i]),i};
}
map.put(nums[i],i);
}
return new int[]{-1,-1};
}