Java版数据结构与算法 - 散列表



概述

散列表是一种数据结构,无论散列表的大小如何,其插入和搜索操作都非常快,接近于常数时间O(1)。散列表使用数组作为存储介质,并使用哈希技术生成元素要插入或从中定位的索引。

哈希

哈希是一种将一系列键值转换为数组索引范围的技术。我们将使用取模运算符来获得一系列键值。考虑一个大小为20的散列表示例,以及要存储的以下项目。项目采用(键,值)格式。

  • (1,20)

  • (2,70)

  • (42,80)

  • (4,25)

  • (12,44)

  • (14,32)

  • (17,11)

  • (13,78)

  • (37,98)

序号哈希值数组索引
111 % 20 = 11
222 % 20 = 22
34242 % 20 = 22
444 % 20 = 44
51212 % 20 = 1212
61414 % 20 = 1414
71717 % 20 = 1717
81313 % 20 = 1313
93737 % 20 = 1717

线性探测

我们可以看到,使用的哈希技术可能会创建已经使用的数组索引。在这种情况下,我们可以通过查看下一个单元格直到找到一个空单元格来搜索数组中的下一个空位置。这种技术称为线性探测。

序号哈希值数组索引线性探测后,数组索引
111 % 20 = 111
222 % 20 = 222
34242 % 20 = 223
444 % 20 = 444
51212 % 20 = 121212
61414 % 20 = 141414
71717 % 20 = 171717
81313 % 20 = 131313
93737 % 20 = 171718

基本操作

以下是散列表的基本主要操作。

  • 搜索 - 在散列表中搜索元素。

  • 插入 - 在散列表中插入元素。

  • 删除 - 从散列表中删除元素。

数据项

定义一个数据项,它包含一些数据和一个键,基于该键在散列表中进行搜索。

public class DataItem {
   private int key;
   private int data;

   public DataItem(int key, int data){
      this.key = key;
      this.data = data;
   }

   public int getKey(){
      return key;
   }

   public int getData(){
      return data;
   }   
}

哈希方法

定义一个哈希方法来计算数据项键的哈希码。

public int hashCode(int key){
   return key % size;
}

搜索操作

每当要搜索元素时,计算传递的键的哈希码,并使用该哈希码作为数组中的索引来定位元素。如果在计算出的哈希码处找不到元素,则使用线性探测来获取前面的元素。

public DataItem search(int key){               
   //get the hash 
   int hashIndex = hashCode(key);        
   //move in array until an empty 
   while(hashArray[hashIndex] !=null){
      if(hashArray[hashIndex].getKey() == key)
         return hashArray[hashIndex]; 
      //go to next cell
      ++hashIndex;
      //wrap around the table
      hashIndex %= size;
   }        
   return null;        
}

插入操作

每当要插入元素时,计算传递的键的哈希码,并使用该哈希码作为数组中的索引来定位索引。如果在计算出的哈希码处找到元素,则使用线性探测查找空位置。

public void insert(DataItem item){
   int key = item.getKey();

   //get the hash 
   int hashIndex = hashCode(key);

   //move in array until an empty or deleted cell
   while(hashArray[hashIndex] !=null
      && hashArray[hashIndex].getKey() != -1){
      //go to next cell
      ++hashIndex;
      //wrap around the table
      hashIndex %= size;
   }

   hashArray[hashIndex] = item;        
}

删除操作

每当要删除元素时,计算传递的键的哈希码,并使用该哈希码作为数组中的索引来定位索引。如果在计算出的哈希码处找不到元素,则使用线性探测来获取前面的元素。找到后,在那里存储一个虚拟项以保持散列表的性能不变。

public DataItem delete(DataItem item){
   int key = item.getKey();

   //get the hash 
   int hashIndex = hashCode(key);

   //move in array until an empty 
   while(hashArray[hashIndex] !=null){
      if(hashArray[hashIndex].getKey() == key){
         DataItem temp = hashArray[hashIndex]; 
         //assign a dummy item at deleted position
         hashArray[hashIndex] = dummyItem; 
         return temp;
      }               
      //go to next cell
      ++hashIndex;
      //wrap around the table
      hashIndex %= size;
   }        
   return null;        
}

散列表实现

DataItem.java

package com.tutorialspoint.datastructure;

public class DataItem {
   private int key;
   private int data;

   public DataItem(int key, int data){
      this.key = key;
      this.data = data;
   }

   public int getKey(){
      return key;
   }

   public int getData(){
      return data;
   }   
}

HashTable.java

package com.tutorialspoint.datastructure;

public class HashTable {
    
   private DataItem[] hashArray;    
   private int size;
   private DataItem dummyItem;

   public HashTable(int size){
      this.size = size;
      hashArray = new DataItem[size];
      dummyItem = new DataItem(-1,-1);
   }

   public void display(){
      for(int i=0; i<size; i++) {
         if(hashArray[i] != null)
            System.out.print(" ("
               +hashArray[i].getKey()+","
               +hashArray[i].getData() + ") ");
         else
            System.out.print(" ~~ ");
      }
      System.out.println("");
   }
   
   public int hashCode(int key){
      return key % size;
   }

   public DataItem search(int key){               
      //get the hash 
      int hashIndex = hashCode(key);        
      //move in array until an empty 
      while(hashArray[hashIndex] !=null){
         if(hashArray[hashIndex].getKey() == key)
            return hashArray[hashIndex]; 
         //go to next cell
         ++hashIndex;
         //wrap around the table
         hashIndex %= size;
      }        
      return null;        
   }
  
   public void insert(DataItem item){
      int key = item.getKey();

      //get the hash 
      int hashIndex = hashCode(key);

      //move in array until an empty or deleted cell
      while(hashArray[hashIndex] !=null
         && hashArray[hashIndex].getKey() != -1){
         //go to next cell
         ++hashIndex;
         //wrap around the table
         hashIndex %= size;
      }

      hashArray[hashIndex] = item;        
   }

   public DataItem delete(DataItem item){
      int key = item.getKey();

      //get the hash 
      int hashIndex = hashCode(key);

      //move in array until an empty 
      while(hashArray[hashIndex] !=null){
         if(hashArray[hashIndex].getKey() == key){
            DataItem temp = hashArray[hashIndex]; 
            //assign a dummy item at deleted position
            hashArray[hashIndex] = dummyItem; 
            return temp;
         }                  
         //go to next cell
         ++hashIndex;
         //wrap around the table
         hashIndex %= size;
      }        
      return null;        
   }
}

演示程序

HashTableDemo.java

package com.tutorialspoint.datastructure;

public class HashTableDemo {
   public static void main(String[] args){
      HashTable hashTable = new HashTable(20);

      hashTable.insert(new DataItem(1, 20));
      hashTable.insert(new DataItem(2, 70));
      hashTable.insert(new DataItem(42, 80));
      hashTable.insert(new DataItem(4, 25));
      hashTable.insert(new DataItem(12, 44));
      hashTable.insert(new DataItem(14, 32));
      hashTable.insert(new DataItem(17, 11));
      hashTable.insert(new DataItem(13, 78));
      hashTable.insert(new DataItem(37, 97));

      hashTable.display();

      DataItem item = hashTable.search(37);

      if(item != null){
         System.out.println("Element found: "+ item.getData());
      }else{
         System.out.println("Element not found");
      }

      hashTable.delete(item);
	
      item = hashTable.search(37);

      if(item != null){
         System.out.println("Element found: "+ item.getData());
      }else{
         System.out.println("Element not found");
      }
   }
}

如果我们编译并运行上述程序,则会产生以下结果:

 ~~  (1,20)  (2,70)  (42,80)  (4,25)  ~~  ~~  ~~  ~~  ~~  ~~  ~~ (12,44)  (13,78)  (14,32)  ~~  ~~  (17,11)  (37,97)  ~~ 
Element found: 97
Element not found
广告