散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组就是散列表。散列表是算法上时间和空间做出权衡的例子,如果没有内存限制,我们可以直接将键作为数据的索引直接一次访问即可,如果没有时间限制我们直接通过之前的无序数组进行顺序查找即可。散列函数能够直接将关键字转化成索引,但是会出现相同索引的情况,这个时候我们需要处理冲突碰撞,我们会使用到拉链法和线性探测法解决碰撞问题。
拉链法
将整数散列最常用的就是除留余数法,浮点数字可以转为二进制数字然后使用除留余数法,字符串可以通过Horner的算法计算散列值,本文就简单的通过整数进行散列然后通过拉链法解决碰撞,需要用到本人之前的文章的算法-符号表的实现(顺序查找和二分查找):
@interface HashTable : NSObject -(instancetype)initWithData:(NSInteger)linkCount; @property (assign,nonatomic) NSInteger count;//键值对总数 @property (assign,nonatomic) NSInteger linkCount;//散列表的大小 @property (strong,nonatomic) NSMutableArray *sequenceTableArr;//存储顺序链表的数组 -(NSInteger)getHashCodeByKey:(NSString *)key; -(void)putData:(NSString *)key value:(NSString *)value; -(NSString *)getValue:(NSString *)key; @end
实现代码:
@implementation HashTable -(instancetype)initWithData:(NSInteger)linkCount{ self=[super init]; if (self) { self.linkCount=linkCount; SequenceTable *sequenceTable; for (NSInteger i=0; i<linkCount; i++) { sequenceTable=[[SequenceTable alloc]init]; [self.sequenceTableArr addObject:sequenceTable]; } } return self; } -(NSMutableArray *)sequenceTableArr{ if (!_sequenceTableArr) { _sequenceTableArr=[[NSMutableArray alloc]initWithCapacity:1]; } return _sequenceTableArr; } -(NSInteger)getHashCodeByKey:(NSString *)key{ NSInteger value=[key integerValue]; return value%self.linkCount; } -(void)putData:(NSString *)key value:(NSString *)value{ NSInteger index=[self getHashCodeByKey:key]; SequenceTable *table =self.sequenceTableArr[index]; [table put:key value:value]; } -(NSString *)getValue:(NSString *)key{ NSInteger index=[self getHashCodeByKey:key]; SequenceTable *table =self.sequenceTableArr[index]; return [table get:key]; } @end
测试代码:
HashTable *hashTable=[[HashTable alloc]initWithData:5]; [hashTable putData:@"3" value:@"FlyElephant"]; [hashTable putData:@"5" value:@"原文地址:http://www.cnblogs.com/xiaofeixiang"]; [hashTable putData:@"2" value:@"博客园"]; [hashTable putData:@"1" value:@"技术交流:228407086"]; [hashTable putData:@"7" value:@"keso"]; [hashTable putData:@"8" value:@"上海"]; [hashTable putData:@"9" value:@"北京"]; [hashTable putData:@"10" value:@"深圳"]; NSString *temp=[hashTable getValue:@"5"]; NSLog(@"keso:%@",temp);
线性探测法
解决碰撞的另外一个方法就是用大小为M的数组存储N个键值对,其中M>N,我们可以依靠数组的空位解决冲突的问题的,如果索引已经存在我们会依次向后找一直找到一个空位为止,首尾相连,实现的的时候将key和value分为两个数组实现,类似于符号表的二分查找:
@interface LinearHashTable : NSObject -(instancetype)initWithData:(NSInteger)capcity; @property (assign,nonatomic) NSInteger count;//符号表中键值对的总数 @property (assign,nonatomic) NSInteger capticity;//数组的大小 @property (strong,nonatomic) NSMutableArray *keys; @property (strong,nonatomic) NSMutableArray *values; -(NSInteger)getHashCodeByKey:(NSString *)key; -(void)putData:(NSString *)key value:(NSString *)value; -(NSString *)getValue:(NSString *)key; @end
实现代码:
@implementation LinearHashTable -(instancetype)initWithData:(NSInteger)capcity{ self=[super init]; if (self) { self.capticity=capcity; for (NSInteger i=0;i<capcity;i++) { [self.keys addObject:[NSNull null]]; [self.values addObject:[NSNull null]]; } } return self; } -(NSMutableArray *)keys{ if (!_keys) { _keys=[[NSMutableArray alloc]initWithCapacity:self.capticity]; } return _keys; } -(NSMutableArray *)values{ if (!_values) { _values=[[NSMutableArray alloc]initWithCapacity:self.capticity]; } return _values; } -(void)resize:(NSInteger)capcity{ LinearHashTable *table=[[LinearHashTable alloc]initWithData:capcity]; for (NSInteger i=0;i<self.capticity; i++) { if (self.keys[i]!=[NSNull null]) { [table putData:self.keys[i] value:self.values[i]]; } } self.keys=table.keys; self.values=table.values; self.capticity=table.capticity; } -(NSInteger)getHashCodeByKey:(NSString *)key{ return [key integerValue]%self.capticity; } -(void)putData:(NSString *)key value:(NSString *)value{ if (self.count>=self.capticity/2) { [self resize:self.capticity*2]; } NSInteger i; for (i=[self getHashCodeByKey:key];self.keys[i]!=[NSNull null];i=(i+1)%self.capticity) { if ([self.keys[i] isEqualToString:key]) { self.values[i]=value; return; } } self.keys[i]=key; self.values[i]=value; self.count=self.count+1; } -(NSString *)getValue:(NSString *)key{ for (NSInteger i=[self getHashCodeByKey:key]; self.keys[i]!=NULL; i=(i+1)%self.capticity) { if ([self.keys[i] isEqualToString:key]) { return self.values[i]; } } return NULL; } @end
相比上面的拉链法,此处多了一个resize,以免N接近于M的时候效率很低,N最好小于M的1/2~
测试代码:
LinearHashTable *hashTable=[[LinearHashTable alloc]initWithData:12]; [hashTable putData:@"2" value:@"FlyElephant"]; [hashTable putData:@"3" value:@"原文地址:http://www.cnblogs.com/xiaofeixiang"]; [hashTable putData:@"15" value:@"博客园"]; [hashTable putData:@"6" value:@"技术交流:228407086"]; [hashTable putData:@"9" value:@"keso"]; [hashTable putData:@"12" value:@"FlyElephant"]; [hashTable putData:@"13" value:@"北京"]; NSString *temp=[hashTable getValue:@"12"]; NSLog(@"博客园:%@",temp);
效果如下: