算法-散列表

散列表(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);

效果如下:

posted @ 2015-07-27 23:03  Fly_Elephant  阅读(1092)  评论(2编辑  收藏  举报