散列(hash,也称“哈希”)是一种重要的存储地址,也是一种常见的检索方法。
按散列存储方式构造的存储结构称为散列表(hash table)。散列表中的一个位置称为槽(slot)。散列技术的核心是散列函数(hash function)。对任意给定的动态查找表DL,如果选定了某个“理想的”散列函数h及相应的散列表HT,则对DL中的每个数据元素X。函数值h(X.key)就是X在散列表HT中的存储位置。插入(或建表)时数据元素X将被安置在该位置上,并且检索X时也到该位置上去查找。由散列函数决定的存储位置称为散列地址。因此,散列的核心就是:由散列函数决定关键码值(X.key)与散列地址h(X.Key)之间的对应关系,通过这种关系实现组织存储并检索。
一般情况下,散列表的存储空间是一个一位数组HT[M],散列地址是数组的下标。涉及散列方法的目标,就是设计某个散列函数h,0<=h(K)(指散列地址)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestDemo10
{
class Program
{
static void Main(string[] args)
{
//选择数组大小很重要的一点就是选择素数,10007也不会大到占用太多内存而降低程序占用的内存
string[] names = new string[10007];
string name;
string[] someNames = new string[] { "name1", "name2", "name3", "name4", "name5", "name6", "name7", "name8", "name9", "name10" };
int hashVal;
for (int i = 0; i < 10; i++)
{
name = someNames[i];
//hashVal = SimpleHash(name, someNames);
InHash(name, someNames);
//names[hashVal] = name;
}
ShowDistrib(names);
Console.Read();
}
static int SimpleHash(string s, string[] arr)
{
int tot = 0;
char[] cname;
cname = s.ToCharArray();
//数组.GetUpperBound(0)得到的值与数组.Length得到的值相差一个长度。
/*
* GetUpperBound(0)方法得到第一个纬度的索引的上限
* Length得到的是该纬度索引的长度
* 一般来说,GetUpperBound(0)=Length-1
* */
int cNameLength = cname.GetUpperBound(0);
int cNamecount = cname.Length;
for (int i = 0; i <= cname.GetUpperBound(0); i++)
{
tot += (int)cname[i];
}
return tot % arr.GetUpperBound(0);
}
static void ShowDistrib(string[] arr)
{
for (int i = 0; i < arr.GetUpperBound(0); i++)
{
if (arr[i] != null)
{
Console.WriteLine(i + " " + arr[i]);
}
}
}
///
/// 这个函数利用霍尔法则来计算多项式函数(关于37)。
///
///
/// 集合
///
static int BetterHash(string s, string[] arr)
{
long tot = 0;
char[] cname;
cname = s.ToCharArray();
for (int i = 0; i <= cname.GetUpperBound(0); i++)
{
tot += 37 * tot + (int)cname[i];
}
int arrLength = arr.GetUpperBound(0);
tot = tot % arr.GetUpperBound(0);
if (tot < 0)
{
tot += arr.GetUpperBound(0);
}
return (int)tot;
}
static bool InHash(string s, string[] arr)
{
int hval = BetterHash(s, arr);
if (arr[hval] == s)
{
return true;
}
else
{
return false;
}
}
}
}
在以下的讨论中,我们假设处理的值是为整型的关键码,否则我们总可以建立一种关键码与正整数之间的对应关系,从而把关键码的检索转化为对与其对于的正整数的检索;同时,进一步假定散列函数的值落在0到M-1之间。散列函数的选取原则是:
10.3 查找散列表中数据
为了在散列表中查找数据,需要计算关键字的散列值,然后访问数组中的对于的元素。
static bool Inhash(string s,string [] arr){
int hval=Betterhash(s,arr);
if(arr[hval]==s)
return true;
else
return false;
}
10.4 解决冲突
产生冲突原因:在初始定义散列列表时,声明倾向只有一个数据值存在散列表元素内,如果没有冲突,那么会顺利进行,但是如果散列函数为两个数据项返回了相同的数据,那么就有问题了。
1、桶式散列法
桶是一种存储在散列表元素内的简单数据结构,它可以存储多个数据项,这里的实现会用ArrayList,它会允许运行超出范围而且允许分配更多的空间,这种方法数据结构会使实现更加高效。
插入一个数据项,首先要用散列函数确定哪一个ArrayList用来存储数据项,然后查看是否存在,若不存在则调用add方法添加,反之不管。移除一个数据项首先确定散列值,并且转到对应的ArrayList,查看ArrayList并确定是否存在,存在则移除。
当使用桶式散列法,尽量保持所用的ArrayList数量尽肯能的少。这样会在添加或移除时减少所需额外工作。一旦有了冲突,ArrayList容量会变为2,每次容量满了会扩大两倍。
2、开放定址法
开放定址函数会在散列数组内寻找单元来放置数据项。如果尝试的第一个单元是满的,那么就尝试下一个,直至找到一个空单元为止。开放定址策略有两种:线性探查法和平方探查法。
线性探查法:线性探查法用线性函数来试图插入数组单元,这就意味着需要找到一个空单元。线性探查法的问题是数组内相邻单元的数据元素会趋近聚类(将物理或抽象对象的集合分成由类似的对象组成的多个类的过程被称为聚类)。http://www.nowamagic.net/academy/detail/3008050(转)
平方探查法:等待补充...
开放定址函数会在散列数组内寻找单元来放置数据项。如果尝试的第一个单元是满的,那么就尝试下一个,直至找到一个空单元为止。开放定址策略有两种:线性探查法和平方探查法。
线性探查法:线性探查法用线性函数来试图插入数组单元,这就意味着需要找到一个空单元。线性探查法的问题是数组内相邻单元的数据元素会趋近聚类(将物理或抽象对象的集合分成由类似的对象组成的多个类的过程被称为聚类)。http://www.nowamagic.net/academy/detail/3008050(转)
平方探查法:等待补充...
开放定址函数会在散列数组内寻找单元来放置数据项。如果尝试的第一个单元是满的,那么就尝试下一个,直至找到一个空单元为止。开放定址策略有两种:线性探查法和平方探查法。
线性探查法:线性探查法用线性函数来试图插入数组单元,这就意味着需要找到一个空单元。线性探查法的问题是数组内相邻单元的数据元素会趋近聚类(将物理或抽象对象的集合分成由类似的对象组成的多个类的过程被称为聚类)。http://www.nowamagic.net/academy/detail/3008050(转)
平方探查法:等待补充...
HashTable类是字典对象的一种特殊类型,其中的数值都是在源于关键字的散列代码的基础上进行存储的。这个类用来避免冲突的策略就是桶的思想。桶是具有相同散列代码的对象的虚拟组合,如果两个关键字都具有相同的散列代码,那么就把他们放置在同一个桶内,否则就把每一个具有唯一散列代码关键字放置在他自己的桶内。
用一个HashTable对象内的桶的数量被称为是负载系数。负载系数是元素与桶数量之间的比率。此系数初始为1.0.当实际系数达到初始系数的时候,就把负载系数增加成一个最小的素数,这个最小素数是当前桶数量的两倍。负载系数很重要,系数越小,HashTable对象的性能就越好。
HashTable类的添加数据和取值:
class Program
{
static void Main(string[] args)
{
Hashtable hTable = new Hashtable(25);
hTable.Add("salayr",100000);
hTable.Add("name", "Tom");
hTable.Add("age",44);
hTable.Add("dept","information technology");
hTable["sex"] = "Male";//不存在即会添加
hTable["age"] = 45;//存在即会修改
Console.WriteLine("The Keys is:");
foreach (var Keys in hTable.Keys)
{
Console.WriteLine(Keys);
}
Console.WriteLine("The Values is:");
foreach (var Values in hTable.Values)
{
Console.WriteLine(Values);
}
Console.WriteLine("The key -- Values is:");
foreach (var Keys in hTable.Keys)
{
Console.WriteLine(Keys+";"+hTable[Keys]);
}
Console.Read(); } }
开放定址函数会在散列数组内寻找单元来放置数据项。如果尝试的第一个单元是满的,那么就尝试下一个,直至找到一个空单元为止。开放定址策略有两种:线性探查法和平方探查法。
线性探查法:线性探查法用线性函数来试图插入数组单元,这就意味着需要找到一个空单元。线性探查法的问题是数组内相邻单元的数据元素会趋近聚类(将物理或抽象对象的集合分成由类似的对象组成的多个类的过程被称为聚类)。http://www.nowamagic.net/academy/detail/3008050(转)
平方探查法:等待补充...