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

散列表的实现经常叫做散列。

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

因此比如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

时间: 2024-11-29 08:26:57

数据结构--解决散列冲突,分离链接法的相关文章

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

散列方法的主要思想是根据结点的关键码值来确定其存储地址:以关键码值K为自变量,通过一定的函数关系h(K)(称为散列函数),计算出对应的函数值来,把这个值解释为结点的存储地址,将结点存入到此存储单元中.检索时,用同样的方法计算地址,然后到相应的 单元里去取要找的结点.通过散列方法可以对结点进行快速检索.散列(hash,也称"哈希")是一种重要的存储方式,也是一种常见的检索方法. 因此,散列函数更像是一种映射,散列函数的选择有很多种,下面以散列函数为关键值对10取余为例,说明散列的插入关键

散列之分离链接法

1 #include <vector> 2 #include <list> 3 #include <string> 4 #include <algorithm> 5 using std::vector; 6 using std::list; 7 using std::string; 8 using std::find; 9 10 int hash(const string &key) 11 { 12 int hashVal = 0; 13 14 fo

数据结构--解决散列冲突,平方探测法

上代码: package com.itany.quadraticprobing; import java.util.LinkedList; import java.util.List; //使用平方探测的散列表 来解决散列时的冲突问题 public class QuadraticProbingHashTable<T> { private static final int DEFAULT_TABLE_SIZE=11; private HashEntry<T>[] array; pri

数据结构--开放定址法解决散列冲突时几种探测法的比较

开放定址法解决散列冲突时主要有线性探测法,平方探测法和双散列法,以下代码通过插入大量随机数,来统计几种探测法产生冲突的次数. #include<iostream> using namespace std; #define MinTablesize 10 #define Num 3000 typedef unsigned int Index; typedef Index Position; struct Hashtal; typedef struct Hashtal *Hashtable; in

[C++]实现散列表的分离链接法的数据结构

散列表,英文叫做Hash Table,因此也叫哈希表,是一种根据关键字值来确定主存中存储位置的数据结构.通过一个散列函数(关于键值的函数),来确定存储该关键字的位置. 主要的方法有: 1.分离链接法(拉链法) 分离链接法的散列函数为 position = key % n. 即关键字的存储位置为关键字的值对表项的数量取模.若表项大小为13,对于关键值为27的项,存储在1(27 % 13 = 1)的表项处.为了减少冲突,n往往取素数.对于同余的关键字,采用 队列(链表) 的方式连接在一起,新放入的元

数据结构之散列(开放定址法)

1 // OHash 2 // 关键字:int 3 // Hash函数:hash(X) = X mod TableSize 4 // 冲突解决方法:开放定址法.Index(X, i) = (hash(X) + f(i)) mod TableSize, f(0) = 0 5 // 其中f(i)为: 6 // 1. 线性, f(i) = i 7 // 2. 平方, f(i) = i ^ 2 8 // 3. 双散列, f(i) = i * hash2(X), hash2(X, R) = R - (X

解决hash冲突之分离链接法

解决hash冲突之分离链接法 分离链接法:其做法就是将散列到同一个值的所有元素保存到一个表中. 这样讲可能比较抽象,下面看一个图就会很清楚,图如下 相应的实现可以用分离链接散列表来实现(其实就是一个linkedList数组) 至于基本的增加.删除和查询的思路都是先根据散列函数来确定遍历哪个链表.然后再到被确定的链表中执行一次查找,然后再进行相应的操作. 接下来就讲几个注意点吧 (一)什么时候需要rehash来扩大散列表的大小 讲这个的时候,先介绍一下什么是装填因子. 装填因子 = 关键字个数 /

哈希表冲突的两个解决方法线性探测和分离链接法

1.线性探测法 就是当要插入的行号发生冲突时,往下一个行号存放哈希值,直到没有冲突. 2.分离链接法 就是将一个行号做成链表的形式,如果有这个行号的冲突便新建一个节点将其插入这个行号的链表中. 在Mahout中,FastByIDMap是基于散列的,但它在处理散列冲突时使用的是线性探测,而非分离链接.因为线性探测不必为每个条目新增加一个额外的Map.Entry对象,节省了内存开销:在数据规模很庞大的时候,这种优势更加体现得出来.

算法学习 - Hash Table操作,分离链接法解决哈希冲突

分离链接法 hash table是映射机制的,最大的优点就是它的操作是O(1)级别的.但是会出现哈希冲突,这就需要几种办法来解决.这里先说一种:分离链接法. 就是当插入的位置已经存在一个值之后,那么在这个值之后插入,就可以了,也叫拉链法.(但是其实会降低查找速度,变成O(n)级别) 下面是代码: // // main.cpp // HashTable_SeparateChaining // // Created by Alps on 14-8-5. // Copyright (c) 2014年