目录
链式散列:每个桶都是一个链表,一旦发生了冲突,就扔到这个链表里
字典是什么
(天知道他是什么 只能说这东西见到的讲解都讲得好烂啊,为了考试就以郭老师为准了,,,,我有什么办法嘛,我也想好好学的)
在java里面有一个叫做map<key,value>的数据类型,映射其实就是一种hashing字典,字典中的数据是成对存在的,分为key和value。一般来说,查询key,就可以对应找到value。
常用的操作,查询,增加,删除,一般是根据key进行操作的,key一般是唯一的
字典线性结构的表述
1.线性表描述
线性表描述的特点无非就是,遍历呗,常见的是数组描述法和链表描述法
数组描述的复杂度为:折半搜索(logn)增加(n)删除(n)
链表描述的复杂度为:遍历搜索(n) 增加(1) 删除(n)
以下为链表描述法的具体实现
template <class K ,class V>
class elem{
public:
K key;V value;
elem* next=NULL;
elem(K key, V value) : key(key), value(value){}
};
template <class K ,class V>
class dict{
public:
int num=0;//初始长度为0;
elem<K,V>* point=NULL;
dict<K,V>(){
cout<<endl;//我的天哪clion的p事情是真的多啊超
//四个操作1,判断是否为空,2,返回数目,,3,删除数对,,4,增加书堆,,5两种查询
}
bool empty(){
return num;
}
V searchValueByKey(K key){
if(num==0){
return NULL;
}else{
elem<K,V>* temp=point;
while(temp&&temp->key!=key){temp=temp->next;}
if(temp){
return temp->value;
}else{
return NULL;
}
}
}
void add(K key,V value){
if(searchValueByKey(key)==NULL){
if(num==0){
point=new elem<K,V>(key,value);num++;
}else{
elem<K,V>* temp=point;
while(point){point=point->next;}
point=new elem<K,V>(key,value);num++;
}
}else{//如果原来有,那么就进行覆盖
elem<K,V>* temp= getElementByKey(key);
temp->value=value;
}
}
void del(){
if(num==0){
cout<<"字典中已经没有别的元素了"<<endl;
}else{
elem<K,V>* temp=point;
while(point->next){point=point->next;}
num--;
}
}
elem<K,V>* getElementByKey(K key){
if(num==0){
return NULL;
}else{
elem<K,V>* temp=point;
while(temp&&temp->key!=key){temp=temp->next;}
if(temp){
return temp;
}else{
return NULL;
}
}
}
void erase(K key){
if(searchValueByKey(key)!=NULL){//如果确实有这个点才进行删除
elem<K,V>* temp=point;
if(point->key==key){
point=point->next;delete temp;
}else{
while(point->next->key!=key){point=point->next;}
elem<K,V>* temp2=temp->next;
temp->next=temp->next->next;
delete temp2;
}
}
}
};
2。跳表描述
跳表感觉是真的怪啊,分成多级跳表,类似在链表中加入折半查找key的编址地点,让搜索的复杂度变成logn,再加上删除增加本来的复杂度为1,所以
跳表的三个基本操作复杂度,均为logn
跳表的缺点:分配级和删除的时候难度都很大,所以一般没有哪个一眼顶真想不开写这东西
查询:跳表本质上是对链表进行折半模拟,因为正常来说,链表虽然具有更快的增删速度,但是碰上查询就直接歇菜了。数组查询某
个位置的复杂度仅仅是o(1),链表可能要到on。所以跳表就是加上一个索引,使用类似蛙跳的方式进行查询,最终也可以达到类似折半搜索的效果
(注意,原理不是折半而是蛙跳)
跳表一般分为多层,每一层的索引数目都是下一层的一半(向上取整)。第一层被称作0级链表。
平均下来每个元素的查询时间复杂度为logn
插入的话,我们需要确定从那一层开始改变索引,假如一共有k层,我们就执行k次的抛硬币判断,然后硬币向上的k次结果,就代表我们在k---1层的链表上都进行插入,平均复杂度仍然为logn
比如这上面的图我们恰好k=2,那就是每一层都添加索引
至于删除,就是把这个数据的每一层的索引都进行删除
3.散列表描述
散列表利用散列函数f(k),把原本的关键字key变成一个f(key),存table中,table一般是类似数组的那种东西,搜索的复杂度为1.
举个例子:java中的map差不多,map底层利用的是一个手动长度可变数组+每个桶都是一个链表的方式进行存储的
散列函数有很多,常见的散列函数是mod n
理想的散列表中,不会发生溢出,所以最理想的情况下,增删改查的复杂度都是1
但是散列表一般不会很理想,绝大多数时候,散列函数是无法保证完整的一对一映射,所以溢出的两种处理方式为