一、直接定制法
1、统计每个字符出现的次数
以每一个字符的asc码作为下标,统计每个字符出现的次数,遇到的第一个1即第一个出现一次的字符:时间复杂度为O(n);空间复杂度为O(1),因为该字符串为一个常量,所以空间复杂度为O(1)
2、按升序排序:把字符串收集回来(因为下标是有次序的)
二、除留余数法
三、平方取中法
四、折叠法(分段后相加,取相加后结果的后几位)
五、随机数法(给定一个数,让随机函数随机一个地址)
六、数学分析法
程序实现:
闭散列:从发生哈希冲突的位置开始,找下一个空余位置
给每一个位置给定一个状态:EMPTY:空状态;EXIST:存在状态,空间中有数据;delete:删除状态
1、线性探测:逐个朝后找下一个位置
插入:计算出该数字的哈希地址,若该地址处没有元素为空状态,则插入;若是有,则插入下一个状为空的元素
删除:计算出该数字的哈希地址,若该为置为存在状态则,判断该元素是否为要删除的元素,删除后将该位置置为删除状态
查找:计算出该数字的哈希地址,判断是否为要查找的数字,若是,返回下标;若不是,往后继续查找,判断是否为要查找的数字(遇到空则继续探测,遇到存在
状态则进行比较,查完都没找到,则不存在这个元素)
缺点:存在数据的堆积:只要一个冲突会引起后面的都发生冲突如:3 13 14 15 16,13会占据14的位置,14会占据15的位置……
闭散列:从发生哈希冲突的位置开始,找下一个空余位置
给每一个位置给定一个状态:EMPTY:空状态;EXIST:存在状态,空间中有数据;delete:删除状态
1、线性探测:逐个朝后找下一个位置
插入:计算出该数字的哈希地址,若该地址处没有元素为空状态,则插入;若是有,则插入下一个状为空的元素
删除:计算出该数字的哈希地址,若该为置为存在状态则,判断该元素是否为要删除的元素,删除后将该位置置为删除状态
查找:计算出该数字的哈希地址,判断是否为要查找的数字,若是,返回下标;若不是,往后继续查找,判断是否为要查找的数字(遇到空则继续探测,遇到存在
状态则进行比较,查完都没找到,则不存在这个元素)
缺点:存在数据的堆积:只要一个冲突会引起后面的都发生冲突如:3 13 14 15 16,13会占据14的位置,14会占据15的位置……
.h
# pragma once
# include <stdio.h>
# include<stdlib.h>
# include<string.h>
# include<assert.h>
#define MAX_SIZE 10
typedef int DataType;
typedef enum{
EXIST,EMPTY,DELETE
}State;
typedef struct HTElem
{
DataType _data;
State _state;
}HTElem;
typedef struct HashTable
{
HTElem _array[MAX_SIZE];
int _total;//哈希表中元素的个数(包括存在和删除)
int _size;//计算哈希表中有效元素的个数
}HashTable,HT;
void HashTableInit(HT *ht);
void HashTableInsert(HT *ht, DataType data);
void HashTableDelete(HT *ht, DataType data);
int HashTableFind(HT *ht, DataType data);
int HashTableSize(HT *ht);
int HashTableEmpty(HT *ht);
.c
test.c
结果:
# include"hsxihanshu.h"
void HashTableInit(HT *ht)
{
int i = 0;
for (; i < MAX_SIZE; ++i)
ht->_array[i]._state = EMPTY;
ht->_size = 0;
ht->_total = 0;
}
void HashTableInsert(HT *ht, DataType data)
{
int hashAddr = -1;
int i = 0;
assert(ht);
if (ht->_total == MAX_SIZE)
return;
//计算哈希地址
hashAddr = hashFunc(data);
while (EMPTY != ht->_array[hashAddr]._state)
{
if (EXIST == ht->_array[hashAddr]._state)
{
if (data == ht->_array[hashAddr]._data)
return;
}
hashAddr++;
if (hashAddr == MAX_SIZE)
hashAddr = 0;
}
//插入元素
ht->_array[hashAddr]._data = data;
ht->_array[hashAddr]._state = EXIST;
ht->_size++;
ht->_total++;
}
int hashFunc(DataType data)
{
return data%MAX_SIZE;
}
void HashTableDelete(HT *ht, DataType data)
{
int ret = -1;
assert(ht);
ret = HashTableFind(ht, data);
if (-1 != ret)
{
ht->_array[ret]._state = DELETE;
ht->_size--;
}
}
int HashTableFind(HT *ht, DataType data)
{
int hashAddr = -1;
int startAddr = -1;
assert(ht);
hashAddr = hashFunc(data);
startAddr = hashAddr;
while (ht->_array[hashAddr]._state != NULL)
{
if (EXIST == ht->_array[hashAddr]._state)
{
if (data == ht->_array[hashAddr]._data)
return hashAddr;
}
hashAddr++;
if (hashAddr == MAX_SIZE)//越界了,从头再找
hashAddr = 0;
if (hashAddr == startAddr)
return -1;
}
return -1;//没有找到
}
int HashTableSize(HT *ht)
{
return ht->_size;
}
int HashTableEmpty(HT *ht)
{
assert(ht);
return 0 == ht->_size;
}
test.c
# include"hsxihanshu.h"
void test()
{
HashTable ht;
HashTableInit(&ht);
HashTableInsert(&ht, 23);
HashTableInsert(&ht, 14);
HashTableInsert(&ht, 74);
HashTableInsert(&ht, 33);
HashTableInsert(&ht, 19);
HashTableInsert(&ht, 29);
printf("size=%d\n", HashTableSize(&ht));
if (-1 != HashTableFind(&ht, 33))
printf("该元素不存在");
else
printf("该元素存在");
if (3 != HashTableFind(&ht, 33))
printf("该元素不存在");
else
printf("该元素存在");
}
void test2()
{
HashTable ht;
HashTableInit(&ht);
HashTableInsert(&ht, 23);
HashTableInsert(&ht, 14);
HashTableInsert(&ht, 74);
HashTableInsert(&ht, 33);
HashTableInsert(&ht, 19);
HashTableInsert(&ht, 29);
HashTableDelete(&ht, 33);
if (-1 != HashTableFind(&ht, 33))
printf("该元素不存在");
else
printf("该元素存在");
if (3 != HashTableFind(&ht, 33))
printf("该元素不存在");
else
printf("该元素存在");
printf("size=%d\n", HashTableSize(&ht));
}
int main()
{
//test();
test1();
system("pause");
return 0;
}
结果:
2.二次探测:
解决数据堆积的方法:二次探测:是数据更加离散:H0表示第一次探测的地址
第i次探测:Hi=H0+i^2;第Hi+1次探测:Hi+1=H0+(i+1)^2
Hi+1=Hi+2*i+1
二次探测的缺陷:可能会越界,因为每次都在累加,距离不断加大
解决二次探测的缺陷的方法:越界后模回去
.h
# pragma once
# include <stdio.h>
# include<stdlib.h>
# include<string.h>
# include<assert.h>
#define MAX_SIZE 10
typedef int DataType;
typedef enum{
EXIST, EMPTY, DELETE
}State;
typedef struct HTElem
{
DataType _data;
State _state;
}HTElem;
typedef struct HashTable
{
HTElem _array[MAX_SIZE];
int _total;//哈希表中元素的个数(包括存在和删除),防止出现死循环
int _size;//计算哈希表中有效元素的个数
int _IsLineDetective;//是否为线性探测
}HashTable, HT;
//初始化
void HashTableInit(HT *ht,int IsLineDetetive);
//插入
void HashTableInsert(HT *ht, DataType data);
//删除
void HashTableDelete(HT *ht, DataType data);
//查找
int HashTableFind(HT *ht, DataType data);
//计算哈希表格中的元素个数
int HashTableSize(HT *ht);
//判断哈希表格是不是空的
int HashTableEmpty(HT *ht);
.c
# include"haxihanshu.h"
//初始化
void HashTableInit(HT *ht,int IsLineDetetive)
{
int i = 0;
for (; i < MAX_SIZE; ++i)
ht->_array[i]._state = EMPTY;
ht->_IsLineDetective = IsLineDetetive;
ht->_size = 0;
ht->_total = 0;
}
//插入
void HashTableInsert(HT *ht, DataType data)
{
int hashAddr = -1;
int i = 0;
assert(ht);
if (ht->_total == MAX_SIZE)
return;
//计算哈希地址
hashAddr = hashFunc(data);
while (EMPTY != ht->_array[hashAddr]._state)//状态不为空
{
if (EXIST == ht->_array[hashAddr]._state)//状态为存在
{
if (data == ht->_array[hashAddr]._data)
return;
}
//hashAddr++;//发生冲突后朝后走
// if (hashAddr == MAX_SIZE)//判断是否越界
// hashAddr = 0;//越界后跳转到为0的地址处,即从头开始找空位置
}
if (ht->_IsLineDetective)//线性探测
DetectiveLine(hashAddr);
else
DetectiveLine2(hashAddr, ++i);//二次探测
//插入元素
ht->_array[hashAddr]._data = data;//插入数据
ht->_array[hashAddr]._state = EXIST;//状态从空改为存在
ht->_size++;
ht->_total++;
}
int hashFunc(DataType data)
{
return data%MAX_SIZE;
}
//删除
void HashTableDelete(HT *ht, DataType data)
{
int ret = -1;
assert(ht);
ret = HashTableFind(ht, data);
if (-1 != ret)
{
ht->_array[ret]._state = DELETE;
ht->_size--;
}
}
//查找
int HashTableFind(HT *ht, DataType data)
{
int hashAddr = -1;
int startAddr = -1;//开始查找的地址
int i = 0;
assert(ht);
hashAddr = hashFunc(data);
startAddr = hashAddr;
while (ht->_array[hashAddr]._state != EMPTY)
{
if (EXIST == ht->_array[hashAddr]._state)
{
if (data == ht->_array[hashAddr]._data)
return hashAddr;
}
//hashAddr++;//没有找到,继续往后找
//if (hashAddr == MAX_SIZE)//越界了,从头再找
// hashAddr = 0;
//if (hashAddr == startAddr)//哈希地址等于开始查找的起始地址,说明找了一圈都没有找到
// return -1;
if (ht->_IsLineDetective)
{
DetectiveLine(hashAddr);
//找了一圈没有找到
if (hashAddr == startAddr)
return -1;
}
else
Detective2(hashAddr, ++i);
}
return -1;//没有找到
}
//计算哈希表格中的元素个数
int HashTableSize(HT *ht)
{
return ht->_size;
}
//判断哈希表格是不是空的
int HashTableEmpty(HT *ht)
{
assert(ht);
return 0 == ht->_size;
}
int DetectiveLine(int hashAddr)//线性探测
{
hashAddr++;
if (hashAddr == MAX_SIZE)
hashAddr = 0;
return hashAddr;
}
void Detective2(int hashAddr, int i)//二次探测
{
hashAddr = hashAddr + 2 * i + 1;
if (hashAddr >= MAX_SIZE)//越界了
hashAddr %= MAX_SIZE;//越界之后模回来
return hashAddr;
}
二次探测的缺点:
(1、是静态的
(2、不能存放字符串
(3、哈希函数用的除留余数法,除数不是素数
(4、什么时候增容
(2、不能存放字符串
(3、哈希函数用的除留余数法,除数不是素数
(4、什么时候增容