您的位置:首页 > 理论基础 > 数据结构算法

数据结构--解决散列冲突,分离链接法

2017-08-13 11:37 302 查看
散列表的实现经常叫做散列。

散列是一种用以常数平均时间运行插入。删除,和查找的技术。可是那些须要元素信息排序的树操作不会得到支持。

因此比如findMax,findMin以及排序后遍历这些操作都是散列不支持的。

假设当一个元素被插入时与已经插入的元素散列(比方散列表的数组序号,非常多元素插入到同一个数组序号中)。那么就会产生一个冲突,这个冲突须要消除。解决冲突的办法有两种:

1 分离链接法

2 开放定址法(包含线性探測,平方探測,双散列)

两者都有可能须要再散列

以下说明分离链接法,上代码:

package com.itany.separatechainning;

import java.util.LinkedList;
import java.util.List;

/*
*解决冲突的第一个方法时分离链接法 其做法是将散列到同一个值得全部元素保留到一个表中
*为运行一次查找 我们通过散列函数来决定到底遍历哪一个链表,然后我们再在被确定的链表中运行一次查找
*散列表存储一个List链表数组
*/
public class SeparateChainingHashTable<T>
{
private static final int DEFAULT_TABLE_SIZE=101;
//我们定义散列表的填装因子:散列表中元素个数和散列表大小的比  这里是1 即集合链表的平均长度是1
private int currentSize;
private List<T>[] theLists;
public SeparateChainingHashTable()
{
this(DEFAULT_TABLE_SIZE);
}
public SeparateChainingHashTable(int size)
{
//对数组中的List进行初始化
theLists=new LinkedList[nextPrime(size)];
for(int i=0;i<theLists.length;i++)
{
theLists[i]=new LinkedList<T>();
}
}
public void makeEmpty()
{
for (List<T> list : theLists)
{
list.clear();
}
currentSize=0;
}
public void insert(T t)
{
List<T> whichList=theLists[myHash(t)];
if(!contains(t))
{
whichList.add(t);
currentSize++;
if(currentSize>theLists.length)
reHash();
}
}
public boolean contains(T t)
{
//通过myHash找出是哪一个集合
List<T> whichList=theLists[myHash(t)];
return whichList.contains(t);
}
public void remove(T t)
{
List<T> whichList=theLists[myHash(t)];
if(contains(t))
{
whichList.remove(t);
currentSize--;
}
}
private int myHash(T t)
{
int hash=t.hashCode();
//对每个t得到数组的序号 大小从0-theLists.length-1 进行分配
hash%=theLists.length;
//防止hash值为负数
if(hash<0)
hash+=theLists.length;
return hash;
}
//有一种情况是currentSize>theLists.length 须要对数组进行扩容 即再散列
//由于列表装的太满 那么操作时间将会变得更长。且插入操作可能失败  此时的方法时新建另外一个两倍大的表
//并且使用一个新的相关的散列函数(由于计算时tableSize变了),扫描整个原始散列表,计算每个元素的新散列值   并将它们插入到新表中
private void reHash()
{
List<T>[] oldLists=theLists;//复制一下一会要用    theLists在又一次new一个
theLists=new LinkedList[nextPrime(2*theLists.length)];
for(int i=0;i<theLists.length;i++)
{
theLists[i]=new LinkedList<T>();
}
//把原来的元素拷贝到新的数组中  注意是把集合中的元素复制进去
for(int i=0;i<oldLists.length;i++)
{
for (T t : oldLists[i])
{
insert(t);
}
}

}
//表的大小是一个素数 这能够保证一个非常好的分布
//是否是素数
private static boolean isPrime(int num)
{
int i=1;
while((num%(i+1))!=0)
{
i++;
}
if(i==num-1)
{
return true;
}
else
{
return false;
}
}

private static int nextPrime(int num)
{
while(!isPrime(num))
{
num++;
}
return num;
}

}


package com.itany.separatechainning;

//能够放在散列表中的Employee类的样例
/*
* 想把类放在散列表中 必须提供两个方法。一个是equals方法 由于要在list的集合中进行查找contains方法时会用到equals进行比較
* 另一个是hashCode方法  由于须要通过它来找出Employee对象该放在哪一个集合中(找出数组的相应序号)
*/

public class Employee
{
private String name;
public Employee(String name)
{
this.name=name;
}
@Override
public boolean equals(Object obj)
{
return obj instanceof Employee && name.equals(((Employee)obj).name);
}
public int hashCode()
{
//String类是有自己的hashCode方法
return name.hashCode();
}
/*
* String类自己的hashCode方法
* public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;

for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
*/
}
package com.itany.separatechainning;

public class Test
{

public static void main(String[] args)
{
Employee e1=new Employee("zhangsan");
Employee e2=new Employee("lisi");
Employee e3=new Employee("wangwu");
Employee e4=new Employee("zhaoliu");
SeparateChainingHashTable<Employee> sc=new SeparateChainingHashTable<Employee>();
System.out.println(sc.contains(e1));
sc.insert(e1);
System.out.println(sc.contains(e1));
sc.remove(e1);
System.out.println(sc.contains(e1));

}

}


结果:

false

true

false
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: