【应用C】C语言实现HashSet并模仿Java机制和语法(+源代码)


很久之前的一个课程作业,用C语言实现HashSet,体会HashSet的内部工作机制,同时模仿Java的机制和语法

01 - HashSet

  HashSet(哈希集合)是一种容器,Java.util包中提供了HashSet类,任何语言都可以自己实现HashSet类,HashSet具有以下特点:
  非线性容器,数据的存放位置不固定
  数据不可重复,数据在集合中是唯一不可重复的
  增删改查(特别是查找)效率非常高,HashSet是以空间换时间

  HashSet的结构如下,左边的List称为,其实是一个数组,数组的每一项都是一个链表,数据的增删查改都围绕着一种HashCode(哈希密码算法)进行,本例程中使用了最基本的求余运算

Alt

02 - HashCode

  HashCode(哈希密码)是一种映射算法,作用是把目标数据映射为容易处理的数据,可以理解为压缩,本例程是把目标数据映射为数组下标,例如有一些字符串的映射如下:
Alt

  建立映射(加密)后,目标数据就可以直接存放在数组中而且使用下标去访问,下标每次都需要计算(解密),如果忽略计算过程同时不考虑碰撞域,那么无论数据容量有多大,查找的速度都是O(1),常数级别,如果考虑碰撞域,情况要复杂一点,但是与O(log2n)接近

02 - 模仿Java机制和语法

  C语言并没有给出HashSet的API,但是Java有,在Java.util工具包中,本例程需要模仿Java的机制和语法,在Java中有泛型的机制,代码表现如下:

HashSet<Integer>  ih = new HashSet<Integer>();
ih.add( 3 );

HashSet<String>  sh = new HashSet<String>();
sh.add("abc");

  集合的类型在创建的时候指定,同时HashSet添加数据的时候会自动扩容,而且因为HashSet是非线性容器,集合的访问需要用迭代器

Iterator<Integer> ite = h.iterator();
while(  ite.hasNext()  )
{
	if(  ite.next() == date  )
	{/*funtion*/}
}

  因此,模仿Java的核心内容是:泛型、自动扩容、迭代器

2.1 - 模仿泛型

  HashSet类需要支持所有的数据类型,不只是intdouble等基本数据类型,还需要支持自定义数据类型struct,C语言中的void*能够容纳所有的指针类型,也就是所有的指针类型都可以直接赋值给void*,不过一旦赋值,再也没有途径知道原来的类型
  所以用户必须提供equal()函数实现泛型,同时hash_code()加密函数也需要用户提供,HashSet的结构如下

/*哈希表*/ 
typedef struct HashSet_Table{
	List **date_list;					//指向链表头结点数组的指针 
	int size;							//哈希表的大小 
	int Capacity;						//容量 
	float load_factor;					//加载因子
	int bucket_depth;					//桶的深度 
	int (*hash_code)(void* date , int depth); 		//哈希密码函数 
	int (*equal)(void *date_one , void *date_two);	//用于判断两组数据是否相等 
}HashSet; 

  只要提供equal()hash_code()之后,就可以用C语言的一个参数宏模仿Java的语法,在参数宏中,##用于连接两个字符串

#define HashSet(T)	HashSet* 
#define new_HashSet(T)  CreateHashSet(hash_code_##T , equal_##T)

  如以下代码:

int hash_code_string(void *date, int depth);
int equal_string(void *a , void *b)
HashSet(string) h = new_HashSet(string); 

#实际上被展开HashSet* h = CreateHashSet(hash_code_string,equal_string)

2.2 - 模仿自动扩容

  自动扩展发生在add()添加数据的时候,使用到C语言的一个库函数realloc进行,伪代码如下

void add(……)
{
	if( "没有相同的元素 " )				
	{
		if("超出范围")
		{
			realloc("扩容");
		}
		push_top("添加数据") ;			
	}
	return;
}

Alt

2.3 - 模仿迭代器

  迭代器的好处在于,遍历不需要程序员指定长度和递增方式,这些操作由迭代器自己完成,核心是一个current指针,每次调用getNext()的时候,就让指针自动记录下一个位置,伪代码

void* getNext(ite)
{
	if("当前节点是当前链表最后一个元素 ") 				
	{
		{"寻找下一个元素"}
	}
	else
	{
		ite->current = ite->next;		//若下一个节点不为空,则作为当前节点 
	}
	return ite->current->data;	
} 

Alt

03 - 结果测试

  测试字符串类型,首先提供equal()hash_code()

/*实现数据是字符型指针的函数*/ 
int equal_string(void *a , void *b)
{
	char *ap = (char*)a;
	char *bp = (char*)b;
	if( strcmp(ap,bp) == 0 )
		return True;
	return False;
}

/*实现字符型指针哈希密码函数*/
int hash_code_string(void *date, int depth)	 
{
	char *t = (char*)date;
	int result = 0;
	for(int i = 0;*t;i++,t++)
		{
			result += i*(*t);
		}
	return (result % depth);
}

然后main()函数中可以测试

	/*create*/
	HashClass *fun = Hash_Initialize();	
	HashSet(string) h = new_HashSet(string);
	……
	
	/*add*/ 
	for(int i = 0;i<len;i++)
	{
		fun->add(h , str[i]);
		……
	}
	
	/*iterator*/
	Iterator(string) ite = fun->CreateIterator(h);
	while(fun->hasNext(ite))
	{
		char *k = (char*)fun->getNext(ite);
		……
	 }

Alt

04 - 源码下载

  链接:百度网盘
  提取码:vbef

05 - 总结

  • C语言实现了求余加密算法的HashSet
  • 暂时提供int、double和string类型的equal()和hash_code()
  • 本例程把HashSet数据结构和操作分开,操作在HashClass内部,可以合并在HashSet中

猜你喜欢

转载自blog.csdn.net/Hxj_CSDN/article/details/86224972