本文内容是基于小象学院——林沐 《面试算法 LeetCode 刷题班》,后期仍将对相关内容进行不定期更新!
问题1:当遇上 负数或非常大的整数,字符串,及浮点数,数组,对象等,如何进行哈希映射?
需要利用哈希函数,将关键字值转换为整数再对表长取余,从而关键字值被转换成哈希表的表长范围内的整数。
问题2: 采用上述方法,但不排除地址冲突(不同元素对应同一个地址)的问题,如何解决?
这就采用拉链法,构造哈希表来解决冲突,可以将哈希表设置为单个的指针数组,每一个指针指向的是哈希函数的结果相同的单链表. 插入时采用头插法,搜索时依次遍历.
实现:
struct ListNode
{
int val;
ListNode *next;
ListNode (int x) : val(x),next(nullptr) {}
};
int hash_func(int key, int table_len) {
return key % table_len;
}
void insert(ListNode *hash_table[], ListNode *node, int table_len) { // 头插法
int hash_key = hash_func(node->val, table_len);
node->next = hash_table[hash_key];
hash_table[hash_key] = node;
}
bool search(ListNode *hash_table[], int value, int table_len) {
int hash_key = hash_func(value, table_len);
ListNode *head = hash_table[hash_key];
while (head)
{
if (head->val == value) {
return true;
}
head = head->next;
}
return false;
}
还可以同 STL 中的 map 包含在 中
给定一个字符串,求该字符串可以生成的最长回文字符串长度.
观察回文字符串的特点,字符串长度为奇数时有中心字符,除了中心字符外,或者字符串长度为偶数长度时,其他字符只要头部出现尾部就要对应出现.
本题思路:
解决方法:
class Solution {
public:
int longestPalindrome(string s) {
int char_map[128] = { 0 };
int max_length = 0;
int flag = 0;
for (int i = 0; i < s.length(); i++)
{
char_map[s[i]] += 1;
}
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;
}
};
已知字符串pattern 与 字符串str, 确认str是否与 pattern匹配,两者均只包含小写字母,以空格间隔. pattern = “abba”, str = “dog cat cat dog” 则为 true ,反之则为 false.
本题思路:
解决方法:
class Solution {
public:
bool wordPattern(string pattern, string str) {
map word_map;
char used[128] = { 0 };
string word;
int pos = 0;
str.push_back(' ');
for (int i = 0; i < str.length(); i++)
{
if (str[i] == ' ')
{
if (pos == pattern.length())
{
return false;
}
if (word_map.find(word) == word_map.end())
{
if (used[pattern[pos]]) { // 单词未出现,字符出现
return false;
}
word_map[word] = pattern[pos];
used[pattern[pos]] = 1;
}
else
{
if (word_map[word] != pattern[pos])
{
return false; // 单词出现,字符未出现
}
}
word = ""; // 完成一个单词的插入和查询后,清空word
pos++; // 指向pattern字符的pos 指针前移
}
else {
word += str[i]; // 读取字符
}
}
if (pos != pattern.length())
{
return false; // 有多余的pattern字符
}
return true;
}
};
已知一组字符串,将所有由颠倒字母顺序构成的字放到一起输出。
Example:
Input: ["eat", "tea", "tan", "ate", "nat", "bat"],
Output:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
本题思路1:
设置字符串到字符串向量的哈希表。遍历字符串向量strs中的单词strs[i]:
遍历哈希表 anagram,将全部key对应的value push至最终结果中
解决方法:
class Solution {
public:
vector> groupAnagrams(vector& strs) {
map> anagram;
vector> result;
for (int i = 0; i < strs.size(); i++)
{
string str = strs[i];
sort(str.begin(), str.end());
if (anagram.find(str) == anagram.end())
{
vector item;
anagram[str] = item;
}
anagram[str].push_back(strs[i]);
}
map>::iterator it;
for (it = anagram.begin();it != anagram.end() ; it++)
{
result.push_back((*it).second);
}
return result;
}
};
本题思路2:
将字符串映射到26个字母的字符数量为 key , 以字符串向量为 value ,剩余过程同上。
解决方法:
void change_to_vector(string &str, vector &vec) {
for (int i = 0; i < 26; i++)
{
vec.push_back(0);
}
for (int i = 0; i < str.length(); i++)
{
vec[str[i] - 'a']++;
}
}
class Solution {
public:
vector> groupAnagrams(vector& strs) {
map, vector> anagram;
vector> result;
for (int i = 0; i < strs.size(); i++)
{
vector vec;
change_to_vector(strs[i], vec);
if (anagram.find(vec) == anagram.end())
{
vector item;
anagram[vec] = item;
}
anagram[vec].push_back(strs[i]);
}
map, vector>::iterator it;
for (it = anagram.begin();it != anagram.end() ; it++)
{
result.push_back((*it).second);
}
return result;
}
};
本题思路:
char_map
记录字符数量,如果word中没有出现该字符,对word尾部添加字符并检查result是否需要更新;否则 j 向前移动,更新 char_map
的字符数量,直到字符s[i]的数量为1;更新 word ,将 word 赋值为 j 和 i 之间的字串。解决方法:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int j = 0;
int result = 0;
string word = "";
int char_map[128] = { 0 };
for (int i = 0; i < s.length(); i++)
{
char_map[s[i]]++;
if (char_map[s[i]] == 1)
{
word.push_back(s[i]);
if (result < word.length())
{
result = word.length();
}
}
else
{
while (j1)
{
char_map[s[j]]--;
j++;
}
}
word = "";
for (int k = j; k <= i; k++)
{
word += s[k];
}
}
return result;
}
};
将 DNA 序列看作只包含[‘A’,‘C’,‘G’,‘T’] 4个字符的字符串,给一个DNA字符串,找到所有长度为10的,且超过1次的子串。
本题思路1:
依次取出给定的字符串中所有长度为 10 的字符串,然后使用 map 来统计字串出现个数,然后输出,复杂度为 O(n)
解决方法:
class Solution {
public:
vector findRepeatedDnaSequences(string s) {
vector result;
map word_map;
for (int i = 0; i < s.length(); i++)
{
string word = s.substr(i, 10);
if (word_map.find(word) != word_map.end())
{
word_map[word]++;
}
else
{
word_map[word] = 1;
}
}
map::iterator it;
for (it = word_map.begin(); it !=word_map.end(); it++)
{
if ( (*it).second > 1 ) {
result.push_back(it->first);
}
}
return result;
}
};
本题思路2:
????
已知字符串S与字符串T,求S在的最小窗口(区间),使得这个区间中包含了字符串T中的所有字符。
Example:
Input: S = "ADOBECODEBANC", T = "ABC"
Output: "BANC"
本题思路1:
1.设置两个 字符哈希 数组, map_s
和 map_t
代表当前处理的窗口区间中的字符变量,map_t 代表子串 T 的字符数量。
2.设置两个指针( i 和 begin)指向字符第一个字符
3.i 指针向后逐个扫描字符串中的字符,在这个过程中,循环检查 begin 指针是否可以向前移动:
如果当前 begin 指向的字符T中没出现,直接前移 begin;
如果 begin 指向的字符T中出现了,但是当前区间窗口中的该字符数量足够,向前移动 begin, 并更新 map_s
否则不能移动 begin, 跳出检查
4.指针i 每向前扫描一个字符,即检查一下是否可以更新最终结果
在整个过程中,使用 begin 和 i 维护一个窗口, 该窗口中的子串满足题目条件,窗口线性向前滑动,整体复杂度为 O(n)。
解决方法:
class Solution {
public:
bool is_window_ok(int map_s[], int map_t[] , vector &vec_t) {
for (int i = 0; i < vec_t.size(); i++) {
if (map_s[vec_t[i]] < map_t[vec_t[i]]) // 遍历 t 中字符
{
return false; // 若s出现该字符的数量小于 t 中出现的数量
}
}
return true;
}
string minWindow(string s, string t) {
const int MAX_ARRAY_LEN = 128;
int map_s[MAX_ARRAY_LEN] = { 0 }; //记录 s 字符串各字符个数
int map_t[MAX_ARRAY_LEN] = { 0 }; //记录 t 字符串各字符个数
vector vec_t;
for (int i = 0; i < t.length(); i++)
{
map_t[t[i]]++;
}
for (int i = 0; i < MAX_ARRAY_LEN; i++)
{
if (map_t[i] > 0) {
vec_t.push_back(i); // 遍历,将字符串t中出现的字符存储到 vec_t中
}
}
int window_begin = 0;
string result;
for (int i = 0; i < s.length(); i++)
{
map_s[s[i]]++;
while (window_begin < i)
{
char begin_ch = s[window_begin];
if (map_t[begin_ch] == 0)
{
window_begin++;
}
else if (map_s[begin_ch] > map_t[begin_ch]) {
map_s[begin_ch]--;
window_begin++;
}
else
{
break;
}
}
if (is_window_ok(map_s,map_t,vec_t))
{
int new_window_len = i - window_begin + 1;
if (result =="" || result.length() > new_window_len)
{
result = s.substr(window_begin, new_window_len);
}
}
}
return result;
}
};