刷题日记 - 6
最近更新:2025-10-20   |   字数总计:2.5k   |   阅读估时:10分钟   |   阅读量:
  1. 刷题日记 - 6
    1. 205. 同构字符串 - 力扣(LeetCode)
    2. 290. 单词规律 - 力扣(LeetCode)
    3. 242. 有效的字母异位词 - 力扣(LeetCode)
    4. 202. 快乐数 - 力扣(LeetCode)
    5. 219. 存在重复元素 II - 力扣(LeetCode)
    6. 228. 汇总区间 - 力扣(LeetCode)
    7. 57. 插入区间 - 力扣(LeetCode)

刷题日记 - 6

205. 同构字符串 - 力扣(LeetCode)

难度:简单

哈希表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution {
public boolean isIsomorphic(String s, String t) {
int sLen = s.length(); // 获取字符串s的长度
int tLen = t.length(); // 获取字符串t的长度
if(sLen != tLen) return false; // 如果两个字符串长度不同,直接返回false
if(sLen < 1) return true; // 如果字符串长度小于1,返回true

char[] sChars = s.toCharArray(); // 将字符串s转换为字符数组
char[] tChars = t.toCharArray(); // 将字符串t转换为字符数组

int[] map = new int[128]; // 创建一个数组用于存储s字符到t字符的映射
int[] charStatus = new int[128]; // 记录当前字符串t是否已经被映射了 0表示未被映射,1表示被映射
Arrays.fill(map, -1); // 初始化数组,所有值为-1

boolean res = true; // 结果标志,初始设为true
for (int i = 0; i < sLen; i++) {
char s_ = sChars[i]; // 获取s当前字符
char t_ = tChars[i]; // 获取t当前字符

if (map[s_] == -1 && charStatus[t_] == 0) { // 如果s当前字符还没有映射,并且目标t字符也没有被映射
map[s_] = t_; // 将s当前字符映射到t当前字符
charStatus[t_] = 1; // 记录t字符已经被映射了
} else { // 如果当前字符s已经有映射,或者字符t已经被映射了
if (map[s_] == -1 || map[s_] != t_) { // 检查是否与已有映射匹配, 或者当前映射的对不对
res = false; // 如果不匹配,设置结果为false
break; // 结束循环
}
}
}
return res; // 返回结果
}
}

因为输入的字符属于ascii字符,数量不多且范围确定,可以用数组模拟哈希表。

值得注意的是:不同字符不能映射到同一个字符上,所以需要一个新的charStatus[]来记录当前字符被是否被映射了。

对于当前字符s_t_

  • 如果发现s_没有映射,t_没有被映射,那么就更新映射关系,标记t_已经被映射了
  • 如果发现s_没有映射,t_却被映射了,那可以直接退出了,因为t_那个肯定不是被s_映射的,出现了多个字符映射到一个字符的情况,False
  • 如果发现s_有映射,t_也被映射了,就直接判断当前s_映射的是不是t_即可。
  • 如果发现s_有映射了,t_没有被映射 。。。。。。。(这是不可能会出现的情况)

290. 单词规律 - 力扣(LeetCode)

难度:简单

哈希表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution {
public boolean wordPattern(String pattern, String s) {
// 使用空格分割字符串s,得到单词数组
String[] words = s.split(" ");
int len = words.length;

// 如果单词数组的长度和模式字符串长度不一致,则返回false
if (len != pattern.length()) return false;

// 用于记录字符和单词的映射
HashMap<Character, String> map = new HashMap<>();
// 用于记录已经映射的单词
HashSet<String> set = new HashSet<>();
// 将模式字符串转换为字符数组
char[] patternChars = pattern.toCharArray();

// 遍历模式字符串和单词数组
for (int i = 0; i < len; i++) {
char cur = patternChars[i];
String word = words[i];

// 如果当前字符和单词都没有被映射,则建立映射关系
if (!map.containsKey(cur) && !set.contains(word)) {
map.put(cur, word);
set.add(word);
} else {
// 如果字符已被映射但映射的单词不匹配,或单词已被映射但映射的字符不匹配,则返回false
if (!map.containsKey(cur) || !map.get(cur).equals(word)) {
return false;
}
}
}

return true;
}
}

同上一题,不过这一题不能再用数组模拟哈希表了。

242. 有效的字母异位词 - 力扣(LeetCode)

难度:简单

哈希表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public boolean isAnagram(String s, String t) {
int len = s.length();
if(len!=t.length())return false;

int[] sCnt = new int[26];
int[] tCnt = new int[26];

for(char ch:s.toCharArray()){
sCnt[ch-'a']++;
}
for(char ch:t.toCharArray()){
tCnt[ch-'a']++;
}

boolean res = true;
for(int i=0;i<26;i++){
if(sCnt[i]!=tCnt[i]){
res = false;
break;
}
}
return res;
}
}

因为st仅包含小写字母,那么范围是确定的,所以可以用数组模拟哈希表,记录每个字母的出现次数,最后比较两个数组即可。

进阶:如果输入字符串包含 Unicode 字符,范围不确定了,直接用哈希表即可。

Unicode字符和ASCII字符的区别主要在于字符集的大小、编码范围和表示的字符种类。


每个ASCII字符用1个字节(8位),实际只用了7位,0-127

Unicode 采用不同的编码方式,包括UTF-8(每个字符占用1到4个字节)、UTF-16(每个字符占用2或4个字节)和UTF-32(每个字符占用4个字节)

ASCII字符在UTF-8编码中是完全兼容的

202. 快乐数 - 力扣(LeetCode)

难度:简单

哈希表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution {
public boolean isHappy(int n) {
// 使用HashSet来记录出现过的数字,防止无限循环
HashSet<Integer> set = new HashSet<>();
set.add(n);

// 结果标志变量,初始化为true
boolean res = true;

// 当n不为1时继续循环
while(n != 1) {
int tmp = n; // 临时变量存储当前数字
int sum = 0; // 存储每位数字平方和
int cur = -1; // 当前位的数字

// 计算每位数字的平方和
while(tmp > 0) {
cur = tmp % 10; // 取最后一位数字
tmp /= 10; // 去掉最后一位数字
sum += cur * cur; // 将平方加到sum中
}
n = sum; // 更新n为平方和

// 如果新数字已经出现过,说明陷入循环,返回false
if(set.contains(n)) {
res = false;
break;
}
// 将新数字加入集合
set.add(n);
}

// 返回结果
return res;
}
}

识别循环:用哈希表记录本轮的数字是否已经出现过,如果出现过就代表陷入循环。

219. 存在重复元素 II - 力扣(LeetCode)

难度:简单

哈希表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public boolean containsNearbyDuplicate(int[] nums, int k) {
// 使用HashMap来记录数字及其对应的最新出现位置
HashMap<Integer, Integer> map = new HashMap<>();
int len = nums.length; // 数组的长度

boolean res = false; // 结果标志变量,初始化为false

// 遍历数组
for (int i = 0; i < len; i++) {
int cur = nums[i]; // 当前数字

// 如果当前数字不在map中,则将其加入map
if (!map.containsKey(cur)) {
map.put(cur, i);
} else {
// 如果当前数字已经在map中,获取上次出现的位置
int lastCurIdx = map.get(cur);
// 检查当前索引与上次出现位置的差是否小于等于k
if (i - lastCurIdx <= k) {
res = true;
break; // 满足条件,退出循环
}
// 更新当前数字的最新出现位置
map.put(cur, i);
}
}
return res; // 返回结果
}
}

哈希表:Key存储数组中的值,Value存该值最新的索引。

索引差的判断:在找到重复元素后,需要检查它们的索引差(该元素目前最新与上一个最新)是否小于等于k。

228. 汇总区间 - 力扣(LeetCode)

难度:简单

区间题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Solution {
public List<String> summaryRanges(int[] nums) {

int len = nums.length;
List<String> res = new ArrayList<>();
if (len < 1) return res; // 如果数组为空,返回空列表

int start = nums[0]; // 初始化起始值为第一个元素
int last = nums[0]; // 初始化最后一个值为第一个元素
int p = 1; // 指针从第二个元素开始遍历

while (p < len) {
int cur = nums[p];
if (last + 1 == cur) { // 如果当前元素是连续的
last = cur; // 更新最后一个值为当前元素
} else {
// 构造字符串并加入结果列表
if (start == last) {
res.add(String.valueOf(start)); // 单个元素
} else {
res.add(start + "->" + last); // 区间
}
// 更新新区间的起始值和最后一个值
start = cur;
last = cur;
}
p++;
}

// 处理最后一个区间
if (start == last) {
res.add(String.valueOf(start));
} else {
res.add(start + "->" + last);
}
return res;
}
}

区间处理的逻辑理解代码实现能力。

57. 插入区间 - 力扣(LeetCode)

难度:中等

区间题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class Solution {
public int[][] insert(int[][] intervals, int[] newInterval) {
// 创建一个用于存储结果的列表
List<int[]> res = new ArrayList<>();
int len = intervals.length;
int p = 0;
// 新区间的左右边界
int left = newInterval[0];
int right = newInterval[1];
boolean flag = false; // 用于标记新区间是否已插入

// 遍历所有区间
while (p < len) {
int[] interval = intervals[p];
if (interval[1] < left) {
// 当前区间在新区间左侧且不重叠,直接添加到结果列表
res.add(interval);
} else if (interval[0] <= right) {
// 当前区间与新区间有重叠,更新新区间的左右边界
left = Math.min(interval[0], left);
right = Math.max(interval[1], right);
} else if (interval[0] > right) {
// 当前区间在新区间右侧且不重叠,插入新区间并标记为已插入
res.add(new int[]{left, right});
flag = true;
break;
}
p++;
}

// 处理剩余的区间
while (p < len) {
res.add(intervals[p]);
p++;
}

// 如果新区间还未被插入,则插入新区间
if (!flag) {
res.add(new int[]{left, right});
}

// 将结果列表转换为二维数组并返回
return res.toArray(new int[res.size()][]);
}
}

处理重叠区间边界条件

遍历所有原区间,相对于插入区间,有三种情况;

  • 原区间在插入区间的左边
  • 原区间有一部分在插入区间中
  • 园区见在插入区间的右边

第一种和第三种情况都很简单,主要看第二种情况:计算两个交叉区间的等效区间,然后再将更新插入区间为等效区间,进行下一轮判断,直到找到下一个原区间满足第三种情况,这时候就直接可以将插入区间加入res了,包括这个在内的后面所有的原区间都可以直接加入res。但也有可能,插入区间就是最后一个区间,所以最后要判断一下插入区间是否被加入res了。