hash第一篇---基本理解

        先举个例子,假设,内存只允许申请一段长度为100个整形的连续空间,但我们有10000个整形数据需要存储,当然,我们可以用链表将这10000个数据零散的存储,但这样查找和删除无疑都会很慢,用数组空间又不足,为了实现空间和时间的均衡,我们可以把数组和链表结合利用。基本解决思路是先拿出100个数据存进数组,每个数组元素又可以用做链表的头或尾,这样一定程度上加快了查找速度,又解决了空间问题。但首先,哪个数字放在数组中哪个位置,这就需要我们建立一种映射关系,10000个数字抢100个位子必然会产生冲突,这需要我们设定一种解决冲突的方案。这就是,hash表的基本思想,也是实现hash表的一种最基本的方法。hash表可以简单理解为是一种数据结构,这种结构意义在于实现存储查找时间和空间的均衡。
      JDK中提供的hashTable就是hash表的实现,HashaSet,HashMap(实际上是hashtable的升级版,但结构用法相似)等都是基于hash表实现了set或是map接口。
      个人理解,尽管hashTable,hashTable,HashMap都用到了map作为存储结构,看似挺复杂,但说到底也是按照数组和链表的原理来分析。影响HashTable使用性能的两个因素:初始容量和加载因子。容量是hash表中桶的数量,即数组元素个数。假设,一开始只有5000个数据要加入表,但后来可能会渐渐增加至10000个,为了减少开始时的空间消耗,我们可以在创建表时先申请一段较小的空间,即设定一个初始容量,之后数据渐渐增多,如果容量及数组长度保持不变就需要不断增加链表长度,这就意味着查找效率的降低,所以必须增大表的容量,以适当的空间代价换取较高的时间效率,这是rehash()过程,但什么时候rehash()才能实现时间和空间的尽量均衡呢?这就用到加载因子这个概念了。默认的是0.75,加载因子太高会增加查询的时间。

     hash算法是hash表的核心,也是最复杂的部分,需要根据具体情况设定,这里先不说,以后会在单独写一篇。

     最后上一段自己的代码,没什么技术含量,写这个其实就是在给自己理理思路。

public class TestHash {	

	int initialSize=10;
	//申请固定大小的学生结点类数组
	protected StudentNode student_Array[]=new StudentNode[initialSize];

	
	/**
	 * 传入学生结点对象,将其学号转换成int值进行hash计算,返回code值作为相应数组下标;
	 * 若返回的数组下标对应元素为空,直接将该结点放在该位子;若返回下标位子不为空,将该节点前置在该位置结点,形成链表
	 * @param studentNode 学生结点
	 */
	public void add(StudentNode studentNode){
		int num=studentNode.number;
		int code=getHashCode(num);
		if(student_Array[code]==null){
				student_Array[code]=studentNode;
		}else{
				StudentNode temp=student_Array[code];
				while(temp.next!=null){
					temp=temp.next;
				}		
				temp.next=studentNode;					
		}	
		
		
	}
	
	
	public StudentNode get(int key){
		StudentNode student=null;
		//将该关键字转换成哈希吗作为数组下标
		 int hashCode=getHashCode(key);
		 
		 StudentNode tempt=student_Array[hashCode];
		 while(tempt.number!=key){
			 tempt=tempt.next;
		 }
		 
		 student=tempt;
		return student;
	}
	
	public void delete(int key){
		int hashCode=getHashCode(key);
		int count=0;
		
		//找到对应结点并结算其在链表中的位子
		 StudentNode tempt=student_Array[hashCode];
		 while(tempt.number!=key ){
			 tempt=tempt.next;
			 count++;			 
		 }
		 
		 //如果是链表中第一个结点
		 if(count==0){
			 student_Array[hashCode]=student_Array[hashCode].next;		
		 }else{//如果不是第一个节点		
			 //找到所求结点的前一个节点
			 StudentNode p=student_Array[hashCode];
			 while(count>1 ){
				 p=p.next;
			 }
			    //结点后继直接接在结点前驱上
				 p.next=tempt.next;
			 }

	}	
	/**
	 * 传入关键字,进行哈希计算,返回对应code值,作为数组下标
	 * @param key  关键字(学号)
	 * @return  code 对应编码(数组下标)
	 */ 
	public int  getHashCode(int key){
		int code = 0;		
		code=key%5;		
		return code;		
	}
public class StudentManager {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		StudentManager stuManager=new StudentManager();
		TestHash hashTest=new TestHash();
		
		stuManager.addStudent(hashTest);	
		stuManager.travelHash(hashTest);
		
	    stuManager.deleteStudent(hashTest, 105);
		stuManager.travelHash(hashTest);
		
	}
		
	public void addStudent(TestHash hashTest){
		StudentNode studentNode;
		//向hash表中添加20个学生,以学号为关键字
		for(int i=1;i<21;i++){
			studentNode=new StudentNode();
			studentNode.number=100+i;
			hashTest.add(studentNode);
			if(studentNode.number==105){
				studentNode.name="李四";
			}
			if(studentNode.number==110){
				studentNode.name="张三";
			}
			if(studentNode.number==115){
				studentNode.name="王五";
			}
		}
		
	}
	
	public void travelHash(TestHash hashTest){
		//遍历哈希表
		for(int i=0;i<10;i++){
			if(hashTest.student_Array[i]!=null){
				StudentNode temp;
				temp=hashTest.student_Array[i];
				while(temp!=null){
					temp=temp.next;		

				}			
			}					
		}	
	}
		
	public StudentNode  findStudent(TestHash hashTest,int key){
		StudentNode student=hashTest.get(key);
		return student;
	}
	
	public void deleteStudent(TestHash hashTest,int key){
		hashTest.delete(key);
	}
}
class StudentNode {
	
	String name;//姓名
	int number;//学号
	StudentNode  next;

}

    

猜你喜欢

转载自zyj--july.iteye.com/blog/1706405