- 随想录2、LC242 有效的字母异位词
- 随想录3、LC349两个数组的交集
3. 随想录4、LC202 快乐数
给一个整数,计算该数字每一位数字的平方和。核心是先拿到每一位数字,怎么拿?
int = 2579
2579 / 10 = 257 … 9
257 10 = 25 … 7
25 / 10 = 2 … 5
2 / 10 = 0 … 2
当得数>0时,循环,所有的余数则为每一位数字了。
接下来是解题。
题目中已经暗示了,只有两种情况,一是无限循环但不等于1,二是无限循环等于1。至于为什么一定会无限循环,答案是因为int类型最大值为为2 147 483 647, 所以平方和最大的数是1 999 999 999,平方和为1 + 81*9 = 724。任何数的平方和都在1到724之间,724次循环之内一定有重复的。
所以用set来记录每一次的sum,如果sum一直不重复,循环,直到有重复出现。如果重复的值是1,就是快乐数。
代码
class Solution {
public boolean isHappy(int n) {
Set<Integer> set = new HashSet<>();
n = calcuSum(n);
if (n == 1){
return true;
}
while (!set.contains(n)){
set.add(n);
n = calcuSum(n);
}
if (n == 1){
return true;
}else{
return false;
}
}
public int calcuSum(int n){
int sum = 0;
while (n > 0){
sum += Math.pow(n%10, 2);
n = n/10;
}
return sum;
}
}
- 随想录5.LC1 两数之和
5. 随想录8. LC15 三数之和
整体思路:
比较重要的几个点:
- 先将数组排序
- 为了去重,i 和 left 和 right 都需要去重:
i 的去重: 如果重复了怎么办,i 是nums里遍历的元素,那么应该直接跳过去。但这里有一个问题,是判断 nums[i] 与 nums[i + 1]是否相同,还是判断 nums[i] 与 nums[i-1] 是否相同。如果是判断 nums[i] 与 nums[i + 1]是否相同,那我们就把 三元组中出现重复元素的情况直接pass掉了。 例如{-1, -1 ,2} 这组数据,当遍历到第一个-1 的时候,判断 下一个也是-1,那这组数据就pass了。所以,应为判断 nums[i] 与 nums[i-1] 是否相同。
left 与 right 的去重:如果不加left 和 right,下图为一个错误示例:
所以应加去重逻辑:必须要保证left < right,万一减着减着,right < left了,不仅无意义,且nums有可能刨指针异常。
//left right去重
while (left < right && nums[left+1] == nums[left]){
left++;
}
while (left < right && nums[right-1] == nums[right]){
right--;
}
还有一个易错点,left 和 right 是针对for来的,所以每次for遍历,都需要重新指定left和right,再开始while。
代码
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
Arrays.sort(nums);
int left = 0;
int right = nums.length - 1;
for (int i=0; i<nums.length; i++){
//在while循环前初始化left,right
left = i + 1;
right = nums.length - 1;
//i去重
if (i > 0){
if (nums[i] == nums[i-1]){
continue;
}
}
while (left < right){
List<Integer> temp = new ArrayList<>();
int sum = nums[i] + nums[left] + nums[right];
if (sum > 0){
right--;
}
else if (sum < 0){
left++;
}
else{
temp.add(nums[i]);
temp.add(nums[left]);
temp.add(nums[right]);
list.add(new ArrayList<>(temp));
//left right去重
while (left < right && nums[left+1] == nums[left]){
left++;
}
while (left < right && nums[right-1] == nums[right]){
right--;
}
left++;
right--;
}
}
}
return list;
}
}
这道题抛开i,单看left 和 right 的while遍历,类似于需要返回数值的两数之和。
(但上题的两数之和是要求返回索引)
6. LC49.字母异位词分组
题目链接
解法一:(用例都通过,但超时了)
复用判断两个字符串是否为字母异位词的方法,即LC242
暴力for循环,嵌套,遍历。i固定,j从i+1开始遍历,如果strs[i], strs[j]互为异位词,加入temp,并在record数组中标记该字符串以和别的字符串互为异位词。j遍历结束,temp加入list。i往前移一个,继续开始下一次遍历。
代码
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> list = new ArrayList<>();
int[] record = new int[strs.length];
for(int i=0; i<strs.length; i++){
if (record[i] != 0){
continue;
}
List<String> temp = new ArrayList<>();
temp.add(strs[i]);
for (int j=i+1; j<strs.length; j++){
if (record[j] != 0){
continue;
}
if (check(strs[i], strs[j])){
temp.add(strs[j]);
record[j] = 1;
}
}
record[i] = 1;
list.add(new ArrayList<>(temp));
}
return list;
}
public boolean check(String str1, String str2){
int[] array = new int[26];
for (int i=0; i<str1.length(); i++){
array[str1.charAt(i)-'a']++;
}
for (int i=0; i<str2.length(); i++){
array[str2.charAt(i)-'a']--;
}
for (int num: array){
if (num != 0){
return false;
}
}
return true;
}
}
上述解法耗时:主要是判断两个字符串是否异位词,要遍历两遍。2n
又暴力解法,n^2
解法二:
考虑哈希。
异位词的两个字符串排序后一定相等。则维护一个map,map的key为排序后的字符串,value为排序后与key相等的字符串list。
易错!
map.put(sNew, new ArrayList<>(temp));
- key要放的是排序后的sNew,而不是s
- value要新new ArrayList<>(temp)
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
Map<String, List<String>> map = new HashMap<>();
for (String s: strs){
//字符串排序
char[] array = s.toCharArray();
Arrays.sort(array);
String sNew = new String(array);
// 如果map中存在排序相同的字符串,将当前字符串添加到对应的列表中
if (map.containsKey(sNew)){
map.get(sNew).add(s);
}
//若不存在,将排序后的字符串作为key,当前字符串作为value,放进map中
else{
List<String> temp = new ArrayList<>();
temp.add(s);
map.put(sNew, new ArrayList<>(temp));
}
}
return new ArrayList<>(map.values());
}
}
class Solution {
public int longestConsecutive(int[] nums) {
if (nums.length == 0){
return 0;
}
Arrays.sort(nums);
int max = 1;
int[] dp = new int[nums.length];
dp[0] = 1;
for (int i=1; i<nums.length; i++){
if (nums[i] == nums[i-1]+1){
dp[i] = dp[i-1] + 1;
if (dp[i] > max){
max = dp[i];
}
}else{
dp[i] = 1;
}
}
return max;
}
}
7. LC128.最长连续序列
理解题意:
输入:nums1 = [0,3,7,2,5,8,4,6,0,1]。输出:9 。连续序列:0123456
输入:nums2 = [1,2,0,1]。输出:3 。连续序列:012
解法一:动规
先对数组排序。
dp[i]表示以i结尾的连续最长序列的长度。
nums2排序完为[0,1,1,2]。但因为不要求序列元素在数组中连续,所以其连续序列为012,长度为3。
所以三种情况:
- 如果nums[i] == nums[i-1]+1,则连续,dp[i] = dp[i-1] + 1
- 如果nums[i] == nums[i-1],则连续但不贡献连续长度,dp[i] = dp[i-1]
- 其他,dp[i] = 1
其间维护max。最大长度。
代码
class Solution {
public int longestConsecutive(int[] nums) {
if (nums.length == 0){
return 0;
}
Arrays.sort(nums);
int max = 1;
int[] dp = new int[nums.length];
dp[0] = 1;
for (int i=1; i<nums.length; i++){
if (nums[i] == nums[i-1]+1){
dp[i] = dp[i-1] + 1;
if (dp[i] > max){
max = dp[i];
}
}
else if (nums[i] == nums[i-1]){
dp[i] = dp[i-1];
}
else{
dp[i] = 1;
}
}
return max;
}
}
解法二:哈希
class Solution {
public int longestConsecutive(int[] nums) {
int res = 0; // 记录最长连续序列的长度
Set<Integer> numSet = new HashSet<>(); // 记录所有的数值
// 将数组中的值加入哈希表中
for(int num: nums){
numSet.add(num);
}
int seqLen; // 连续序列的长度
for(int num: numSet){
// 如果当前的数是一个连续序列的起点,统计这个连续序列的长度
if(!numSet.contains(num - 1)){
seqLen = 1;
// 不断查找连续序列,直到num的下一个数不存在于数组中
num++;
while(numSet.contains(num)){
seqLen++;
num++;
}
res = Math.max(res, seqLen); // 更新最长连续序列长度
}
}
return res;
}
}