1. 程式人生 > >數據結構--解決散列沖突,分離鏈接法

數據結構--解決散列沖突,分離鏈接法

code any += 有一種 線性 return lean 沖突 ceo

散列表的實現經常叫做散列。

散列是一種用以常數平均時間運行插入。刪除,和查找的技術。可是那些須要元素信息排序的樹操作不會得到支持。

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

數據結構--解決散列沖突,分離鏈接法