刷题时间:2021.8.31-2021.9.3
任意元素的映射
利用哈希函数,将关键字值(key)(大整数、字符串、浮点数等)转换为整数再对表长取余,从而关键字值被转换为哈希表的表长范围内的整数。
拉链法解决冲突,构造哈希表
将所有哈希函数结果相同的结点连接在同一个单链表中。若选定的哈希表长度为m,则可将哈希表定义为一个长度为m的指针数组t[0..m-1], 指针数组中的每个指针指向哈希函数结果相同的单链表。
插入value:将元素value插入哈希表,若元素value的哈希函数值为hash_key,将value对应的节点以头插法的方式插入到以t[hash key]为头指针的单链表中。
查找value:若元素value的哈希函数值为hash_key,遍历以t[hash_ key]为头指针的单链表,查找链表各个节点的值域是否为value。
1、LeetCode409最长回文串
思路:1.利用字符哈希方法,统计字符串中所有的字符数量;
2.设置最长回文串偶数字符长度为max_ length = 0;
3.设置是否是否有中心点标记flag=0;
4.遍历每一个字符,字符数为count, 若count为偶数,max_ length += count;若count为奇数,max_ length += count-l, flag= 1;
5.最终最长回文子串长度: max_ length + flag。
class Solution {
public int longestPalindrome(String s) {
int[] char_map = new int[128];//字符哈希
int max_length = 0;//回文串偶数部分的最大长度
int flag = 0;//是否有中心点
for(int i=0;i<s.length();i++){
char c = s.charAt(i);
char_map[c]++;//利用整数的数组下标实现字符哈希,统计字符个数
}
for(int i=0;i<128;i++){
if(char_map[i]%2 == 0){//如果某字符为偶数个,则均可以使用在回文串里
max_length += char_map[i];
}else{
max_length += char_map[i]-1;//如果某字符为奇数个,则丢弃一个,其余的使用在回文串里
flag = 1;//此时标记回文串可以有中心点
}
}
return max_length + flag;//最终结果是偶数部分的最大长度+中心点字符
}
}
2、LeetCode290单词规律
思路:1.设置单词(字符串)到pattern字符的映射(哈布);使用数组used[128]记录pattern字符是否使用。
2.遍历str,按照空格拆分单词,同时对应的向前移动指向pattern字符的指针,每拆分出一个单词,判断:
如果该单词从未出现在哈希表中:如果当前的pattern字符已被使用,则返回false;将单词与当前指向的attern字符做映射;标记当前指向的pattern字符已使用,否则:如果当前单词在哈希表中的映射字符不是当前指向的pattern字符,则返回false。
3.若单词个数与pattern字符个数不匹配,返回false。
LeetCode评论代码
class Solution {
public boolean wordPattern(String pattern, String s) {
String[] strings = s.split(" ");
if (pattern.length() != strings.length) {
return false;
}
HashMap<Object, Integer> hashMap = new HashMap<>();
//如果出现位置不匹配,或者一个已经出现而另一个没有出现过,则匹配失败。使用Objects.equals方法可以避免对null值进行判断
for (int i = 0; i < pattern.length(); i++) {
if(!Objects.equals(hashMap.put(pattern.charAt(i), i), hashMap.put(strings[i], i))){
return false;
}
}
return true;
}
}
3、LeetCode49字母异位词分组
思路:设置字符串到字符串向量的哈希表anagram,遍历字符串向量strs中的单词strs[i]:
1)设置临时变量str = strs[i],对str进行排序。
2)若sr未出现在anagram中,设置str到一个空字符串向量的映射。
3)将strs[i]添加至字符串向量anagram[str]中。
遍历哈希表anagram,将全部key对应的value push至最终结果中。
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
Map<String, List<String>> map = new HashMap<>();
for (int i = 0; i < strs.length; i++) {
char[] chars = strs[i].toCharArray();
//对字符串内部进行字符排序
Arrays.sort(chars);
String key = String.valueOf(chars);
//根据key放入不同的组
if (!map.containsKey(key)) {
//若无法在哈希表中找到key,设置一个空的字符串
List<String> list = new ArrayList<>();
list.add(strs[i]);
map.put(key, list);
} else {
//在对应的字符串中push结果
map.get(key).add(strs[i]);
}
}
return new ArrayList<>(map.values());
}
}
4、LeetCode3无重复字符的最长子串
思路:1.设置一个记录字符数量的字符哈希,char_map;
2.设置一个记录当前满足条件的最长子串变量word;
3.设置两个指针(记作指针i与指针begin)指向字符串第一个字符;
4.设置最长满足条件的子串的长度result;
5.i指针向后逐个扫描字符串中的字符,在这个过程中,使用char_ map记录字符数量。如果word中没出现过该字符:对word尾部添加字符并检查result是否需要更新;否则:begin指针向前移动,更新char_map中的字符数量,直到字符s[i]的数量为1;更新word,将word赋值为begin与i之间的子串。
在整个过程中,使用begin与i维护一个窗口,该窗口中的子串满足题目条件(无重复的字符),窗口线性向前滑动,整体时间复杂度为O(n)。
LeetCode评论代码
class Solution {
public int lengthOfLongestSubstring(String s) {
int result=0;
int start=0,end=0;//代表当前窗口的开始和结束位置
Set<Character> set=new HashSet<Character>();
//当窗口中出现重复的字符时,start++,没有重复时,end++,每次更新长度的最大值
while(start<s.length() && end<s.length()){
if(set.contains(s.charAt(end))){
set.remove(s.charAt(start));
start++;
}
else{
set.add(s.charAt(end));
end++;
result=Math.max(result, end-start);
}
}
return result;
}
}
5、LeetCode189重复的DNA序列
思路:枚举DNA字符串的中所有长度为10的子串,将其插入到哈希Map中,并记录子串数量;遍历哈希map,将所有出现超过1次的子串存储到结果中。算法复杂度O(n)。
LeetCode官方题解
class Solution {
public List<String> findRepeatedDnaSequences(String s) {
int L = 10, n = s.length();
HashSet<String> seen = new HashSet(), output = new HashSet();
for (int start = 0; start < n - L + 1; ++start) {
String tmp = s.substring(start, start + L);
if (seen.contains(tmp)) output.add(tmp);
seen.add(tmp);
}
return new ArrayList<String>(output);
}
}
6、LeetCode76最小覆盖子串
思路:1.设置两个字符哈希数组,map_ s与map_t, map_s代表当前处理的窗口区间中的字符数量,map_ t代表子串T的字符数量。
2.设置两个指针(记作指针i与指针begin)指向字符串第一个字符;
3.i指针向后逐个扫描字符串中的字符,在这个过程中,循环检查begin指针是否可以向前移动;
如果当前begin指向的字符T中没出现,直接前移begin; 如果begin指向的字符T中出现了, 但是当前区间窗口中的该字符数量足够,向前移动begin,并更新map_ s;否则不能移动begin,跳出检查。
4.指针每向前扫描一个字符, 即检查一下是否可以更新最终结果(找到最小的包含T中各个字符的窗口)。在整个过程中,使用begin与i维护一个窗口,该窗口中的子串满足题目条件(包含T中所有字符),窗口线性向前滑动,整体时间复杂度为O(n)。
LeetCode评论代码
class Solution {
public static String minWindow(String s, String t) {
int left = 0,right = 0;
int count = t.length();
int res_left = 0;
int res_right = -1;
int substringLen = Integer.MAX_VALUE;
int[] map = new int[128];
for (int i = 0; i < t.length(); i++) {
map[t.charAt(i)]++;
}
while (right < s.length()) {
map[s.charAt(right)]--;
if (map[s.charAt(right)] >= 0) {
count--;
}
while (count == 0) {
int tempLen = right - left + 1;
if (substringLen > tempLen) {
res_left = left;
res_right = right;
substringLen = tempLen;
}
char departingCharacter = s.charAt(left);
map[departingCharacter]++;
if (map[departingCharacter] > 0) {
count++;
}
left++;
}
right++;
}
return s.substring(res_left, res_right + 1);
}
}