哈希表是用来储存键值(key/value)对的数据结构,它有以下属性与方法
通过键做索引可以得到值,如hash[key]=value
哈希表不能在声明时指定类型,所以调用其中的值必须进行强制转换。
哈希表常用的场景,一是当字符等数据重复多次出现,并且需要记录次数的时候,二是存在关联或者成对的数据需要调用时,比如人名和学号。
寻找重复的子树
给定一棵二叉树 root
,返回所有重复的子树。对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可。
之前的博客总结过,二叉树相关的问题无非就是两个思路:递归和遍历序列。这道题由于二叉树的形状完全没有限制,单纯的递归是比较困难的。
利用序列中重复的部分来寻找重复的子树是比较合适的,所以只要把所有子树的序列都通过哈希表保存下来,再检查重复就可以了。注意这里的遍历必须要记录空子树,这样记录下的序列才能是与子树完全对应的。
注意这里的序列不能用二叉树节点的类型。因为重复子树中的节点只是节点数据相同,别不是同一个节点(指针不同),为了方便处理数据,可以用string来保存序列。
public class Solution {
public IList<TreeNode> FindDuplicateSubtrees(TreeNode root) {
Hashtable hash=new Hashtable();
List<TreeNode> tree=new List<TreeNode>();
DFS(root,hash,tree);
return tree;
}
public string DFS(TreeNode root,Hashtable hash,List<TreeNode> tree)
{
if(root==null)
return "";
string str="";
str=Convert.ToString(root.val)+","
+DFS(root.left,hash,tree)+","+DFS(root.right,hash,tree);
if(!hash.ContainsKey(str))
hash.Add(str,false);
else if(!(bool)hash[str])
{hash[str]=true;tree.Add(root);}
return str;
}
}
两数之和 III - 数据结构设计
因为不知道数据的总量,而且储存后需要遍历数据,所以我们选择用列表来储存数据。
在寻找数据对的时候如果采取双重循环,在数据量非常的大的情况下很容易超时。如果是数据已经通过一次排序,就可以在一次循环中找到结果。
如果是给定的数组,我们需要用array.sort()方法,而这里只需要在构造list的时候把数据插入合适的位置就可以了。
注意这里面当某个数字第二次出现时还是要把它记录下来,因为这里的两个整数相加可以是相同的整数,但是当第三次及以上为了提高效率应该不保存。
public class TwoSum {
List<int> nums;
Hashtable hash;
public TwoSum() {
nums=new List<int>();
hash=new Hashtable();
}
public void Add(int number) {
if(!hash.ContainsKey(number))
{
hash.Add(number,true);
int i=0;
while(i<nums.Count&&number>nums[i])
i++;
if(i<nums.Count)
nums.Insert(i,number);
else
nums.Add(number);
}
else if((bool)hash[number])
{
nums.Insert(nums.IndexOf(number),number);
hash[number]=false;
}
}
public bool Find(int value) {
if(nums.Count<2)
return false;
int i=0;int j=nums.Count-1;
while(i<nums.Count-1&&j>0&&i<j)
{
if(nums[i]+nums[j]==value)
return true;
else if(nums[i]+nums[j]>value)
j--;
else
i++;
}
return false;
}
}
四数相加 II
这题显然四重循环是会超时的,可以拆成两个二重循环,先用哈希表记录第一个二重循环之和,注意如果有重复要记录重复次数。第二个循环查找结果是否保存在哈希表中,如果是,则加上它重复出现的次数。
public class Solution {
public int FourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Hashtable hash=new Hashtable();
for(int i=0;i<nums1.Length;i++)
for(int j=0;j<nums2.Length;j++)
{
if(!hash.ContainsKey(nums1[i]+nums2[j]))
hash.Add(nums1[i]+nums2[j],1);
else
hash[nums1[i]+nums2[j]]=(int)hash[nums1[i]+nums2[j]]+1;
}
int sum=0;
for(int i=0;i<nums3.Length;i++)
for(int j=0;j<nums4.Length;j++)
{
if(hash.ContainsKey(0-nums3[i]-nums4[j]))
sum+=(int)hash[0-nums3[i]-nums4[j]];
}
return sum;
}
}
前 K 个高频元素
给你一个整数数组 nums
和一个整数 k
,请你返回其中出现频率前 k
高的元素。你可以按 任意顺序 返回答案。
首先通过哈希表保存每个元素出现的次数,其次通过哈希表的Keys属性和foreach循环找出前k个高频元素。
public class Solution {
public int[] TopKFrequent(int[] nums, int k) {
Hashtable hash=new Hashtable();
for(int i=0;i<nums.Length;i++)
{
if(!hash.ContainsKey(nums[i]))
hash.Add(nums[i],1);
else
hash[nums[i]]=(int)hash[nums[i]]+1;
}
int[] sort=new int[k];
ICollection key = hash.Keys;
for(int i=0;i<k;i++)
{
int max=0;
while(!hash.ContainsKey(nums[max]))
max++;
max=(int)nums[max];
foreach(int num in key)
{
if((int)hash[num]>(int)hash[max])
max=num;
}
hash.Remove(max);
sort[i]=max;
}
return sort;
}
}
单词的唯一缩写
这题题目很长,但是意思很简单。通过一个哈希表保存词典中的每个单词和它的缩写。对于输入单词,如果哈希表中不存在它的缩写或者缩写对应的单词与它完全相同且唯一,则输出true;
public class ValidWordAbbr {
Hashtable hash;
public ValidWordAbbr(string[] dictionary) {
hash=new Hashtable();
for(int i=0;i<dictionary.Length;i++)
{
if(!hash.ContainsValue(dictionary[i]))
{
if(dictionary[i].Length>2)
{
string s=suoxie(dictionary[i]);
if(!hash.ContainsKey(s))
hash.Add(s,dictionary[i]);
else if(hash[s]!=null&&!hash.ContainsValue(dictionary[i]))
hash[s]=null;
}
}
}
}
public bool IsUnique(string word) {
if(word.Length>2)
{
string s=suoxie(word);
if(!hash.ContainsKey(s)||hash.ContainsValue(word))
return true;
return false;
}
else
return true;
}
public string suoxie(string word)
{
return word[0]+Convert.ToString(word.Length-2)+word[word.Length-1];
}
}