LeetCode刷题 --哈希表 与 字符串【2020第一版】

1.哈西表

1.1 哈希表概述

\quad 哈西表(hash table,也叫散列表),是根据关键字值(key)直接进行访问的数据结构,它通过把关键字映射到表中一个位置(数组下标)来直接访问,以加快查找关键字值的速度。这个映射函数叫做哈西(散列)函数,存放记录的数组叫做哈西(散列)表

//哈西排序: 实践复杂度O(表长+n) n为元素个数
int random[10] = {999,1,44,555,666,8,12,902,11,520}; //在0-1000中的随意取值
int hash_map[1000] = {0};
for(int i=0; i<10; ++i)
	++hash_map[random[i]];
for(int i=0; i<1000; ++i)
	for(int j=0; j<hash_map[i]; ++j)
		cout<< i <<'\t';
cout<<"排序完成";

1.2 哈西映射中的问题及解决办法

Q1:

  1. 负数或非常大的整数,如何进行哈西? :-5,9999999;
  2. 字符串如何进行哈西? : abcdefg, XYZ;
  3. 无法直接哈西的数据类型:浮点,数组,对象,如何进行哈西?
    A1: 哈西函数解决映射问题。 //TABLE_LEN取质数,冲突会比其他数字少
    利用哈西函数,将关键字值(key)[大整数,字符串,浮点数等]转换为整型,再对表长取余,从而关键字值被转换为哈希表的表长范围内的整数。
    Q2:
    哈西冲突:不同的整型或字符串由于哈西函数的选择,被映射到了同一下标处,产生了冲突。
    A2:哈西表解决冲突问题。
    拉链法解决哈西冲突,构建哈西表。
    将所有哈西函数结果相同的节点连接在同一个单链表中。
    插入value:
    将元素value插人哈希表,若元素value的哈希函数值为hash_key,将value对应的节点以头插法的方式插入到以table[hash_key]为头指针的单链表中。
    查找value:
    若元素value的哈希函数值为hash_key,遍历以**table[hash_key]**为头指针的单链表,查找链表各个节点的值域是否为value。
struct ListNode
{
	int val;
	ListNode* next;
	ListNode(int x): val(x), next(NULL){}
};
//利用对表取余解决哈希映射
int hash_func(int key, int table_len)
{
	return key%table_len; 
}
//利用单链表解决哈西冲突
//插入value值
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; //使用头插法将value插到表对应位置的后接链表上
}
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;
}

2.哈西例题

2.1 最长回文串

在这里插入图片描述
A:

  1. 利用哈西统计字符个数;
  2. 对于偶数字符直接记录结果,对于奇数字符丢弃一个记录结果;
  3. 利用flag==1来记录是否有奇数字符;
  4. 最后返回结果加上flag即可。
int longestPalindrome(string s)
{
	int Max_Len = 0;
	int hash_map[128] = {0}; //建立字符哈西表
	int flag = 0; //奇数字符标识
	for(atuo K:s)
	{
		++hash[K]; //统计字频
	}
	for(int i=0; i<128;++i)
	{
	  	if(hash_map[i]%2==0)
	  		Max_Len += hash_map[i];
	  	else
	  	{
	  		Max_Len += (hash_map[i]-1); //奇数字符丢弃一个再入果
	  		flag = 1;
	  	}
	}	
	return Max_Len + flag;
}
//一开始的时候想找最长奇数字符入果,其实是所有奇数字符均课入果,只不过要丢弃一个。

2.2 词语模式匹配

在这里插入图片描述
A:

  1. 设置单词(字符串)到pattern字符的映射(哈希);利用flag[26]来记录pattern字符是否使用
  2. 遍历str,按照空格拆分单词,同时对应的前移指向pattern字符的指针,每拆分出一个单词,判断:
    • [one] 单词未出现在哈希表中:
      当前pattern中的字符已被使用,返回false,
      将单词与当前的pattern字符做哈希,标记当前pattern中的字符已被使用
    • [two] 单词出现在哈希表中:
      如果当前单词在哈希表中的哈希值不是当前的pattern,返回false;
  3. 注意边界问题,单词个数与pattern字符个数不匹配,返回false。
bool wordPattern(string pattern, string str) 
{
	map<string,char> hash_map;
	int flag[26] = {0}; //标识hash_value是否已被使用
	string word; //记录当前单词
	int pos; //记录当前pattern的字符位置
	str.push_back(' ');//为str最末尾单词添加‘ ’,作为单词切割标识符
	for(int i=0; i<str.size();++i)
	{
		if(str[i]!=' ')
			word+=str[i];
		else
		{
			if(pos==pattern.size()) //有单词,没有pattern了,异常退出
				return false;
			else
			{
				if(hash_map.find(word)!=hash_map.end()) //当前单词再hash_map中没有key
				{
					if(flag[pattern[pos]-'a'])
						return false;  //如果当前字符已经被使用,说明哈西冲突的存在,异常退出
					hash_map[word] = pattern[pos]; //建立单词到字符的映射关系
					flag[pattern[pos]-'a']=1; //标识pattern已经被使用
				}
				else //当前单词已经在hash_map中
				{
					if(hash_map[word]!=pattern[pos])  //有hash冲突
						return false;
				}
				word = "";
				++pos;				
			}
		}
	}
	if(pos!=pattern.size())  //还有pattern字符,异常退出
		return false;
	return true;
}

2.3字母异位词分组

在这里插入图片描述
A:利用排序构建排序后的单词与原单词的哈希,异位词排序后会映射到同一个哈希表。

//利用排序后一样 构建哈希函数
vector<vector<string>> groupAnagrams(vector<string>& strs)
{
	map<string,vector<string>> hash_map;
	for(auto s:strs)
	{
		string temp = s;
		sort(s.begin(),s.end()); //构建key
		hash_map[s].push_back(temp);//构建value表
	}
	vector<vector<string>> result;
	for(auto k:hash_map)
	{
		result.push_back(i.second);
	}
	return result;
}

2.4 无公共字符的最长子串

在这里插入图片描述
A:设置双指针,并使用哈希表记录字符个数,一旦出现2即移动begin指针,将begin指针移到重复字符的下一个位置,

 int lengthOfLongestSubstring(string s) 
    {
        string word = "";
        int result=0;
        int begin = 0;
        int char_map[128] = {0};
        for(int i=0;i<s.size();++i)
        {
            ++char_map[s[i]];
            if(char_map[s[i]]==1)
            {
                word += s[i];
                if(result<word.size())
                    result = word.size();
            }
            else
            {
                while(begin<=i && char_map[s[i]]>1)
                {
                    --char_map[s[begin]];
                    ++begin;
                }
                word = "";
                for(int j=begin; j<=i; ++j)
                    word += s[j];
            }
        }
        return result;        
    }

2.5 重复的DNA序列

在这里插入图片描述
A: 利用位运算将DNA序列看出一个4进制数,通过哈西映射到2^20次方的下标中。

vector<string> findRepeatedDnaSequences(string s) 
{
	vector<int> hash_map(2^20,0); //建立哈西表
	vector<string> res; //结果数组
	int char_map[128] = {0};
	int key = 0;
	for(int i=9; i>=0; ++i)
		key = key<<2 + char_map[s[i]];
	hash_map[key] = 1;
	for(int i=10; i<s.size();++i)
	{
		key = key>>2;
		key = key | (char_map[s[i]]<<18);
		++hash_map[key];
	}
	for(int i=0; i<(2<<20); ++i)
	{
		if(hash_map[i]>1)
			res.push_back(change_int_to_DNA(i));
	}
	return res;
}
string chang_int_to_DNA(int i)
{
	static char DNA = {'A','C','G','T'};
	string str;
	for(int i=0; i<10; ++i)
	{
		str += DNA{DNA&3};
		DNA = DNA>>2;
	}
	return str;
}
//用无序哈希表来存储
vector<string> res;
    vector<string> findRepeatedDnaSequences(string s) 
    {
        if(s.size()<10) return res;
        unordered_map<string,int> counter;
        for(int i=0; i<s.size()-9;++i)
        {
            string t = s.substr(i,10);
            if(counter[t]==1)
                res.push_back(t);
            ++counter[t];
        }
        return res;
    } 

2.6最小覆盖子串

在这里插入图片描述
A:利用双指针,并记录当前子串长,和含T的字符个数,达到个数,更新子串,优化左指针,否则,优化右指针。

class Solution {
public:
    string minWindow(string s, string t) 
    {
       int hash[256] = {0};
       for(auto c:t) ++hash[c];
       int start = 0;
       int end = 0;
       int res = -1;
       int cnt = 0;
       string ans = "";
       int m = t.size();
       int n = s.size();
       int length = -1;
       while(end<n)
       {
           char c = s[end];
           --hash[c];
           if(hash[c]>=0) ++cnt;
           while(cnt==m)
           {
               if(length==-1 || length>end-start+1)
               {
                   length = end - start +1;
                   ans = s.substr(start,length);
               }
               c = s[start];
               ++hash[c];
               if(hash[c]>=1)  --cnt;
               ++start;
           }
           ++end;
       }    
       return ans;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值