目录
链式散列:每个桶都是一个链表,一旦发生了冲突,就扔到这个链表里
(天知道他是什么 只能说这东西见到的讲解都讲得好烂啊,为了考试就以郭老师为准了,,,,我有什么办法嘛,我也想好好学的)
在java里面有一个叫做map
常用的操作,查询,增加,删除,一般是根据key进行操作的,key一般是唯一的
线性表描述的特点无非就是,遍历呗,常见的是数组描述法和链表描述法
数组描述的复杂度为:折半搜索(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;
- }
- }
- }
- };
跳表感觉是真的怪啊,分成多级跳表,类似在链表中加入折半查找key的编址地点,让搜索的复杂度变成logn,再加上删除增加本来的复杂度为1,所以
跳表的三个基本操作复杂度,均为logn
跳表的缺点:分配级和删除的时候难度都很大,所以一般没有哪个一眼顶真想不开写这东西
查询:跳表本质上是对链表进行折半模拟,因为正常来说,链表虽然具有更快的增删速度,但是碰上查询就直接歇菜了。数组查询某
个位置的复杂度仅仅是o(1),链表可能要到on。所以跳表就是加上一个索引,使用类似蛙跳的方式进行查询,最终也可以达到类似折半搜索的效果
(注意,原理不是折半而是蛙跳)

跳表一般分为多层,每一层的索引数目都是下一层的一半(向上取整)。第一层被称作0级链表。
平均下来每个元素的查询时间复杂度为logn
插入的话,我们需要确定从那一层开始改变索引,假如一共有k层,我们就执行k次的抛硬币判断,然后硬币向上的k次结果,就代表我们在k---1层的链表上都进行插入,平均复杂度仍然为logn
比如这上面的图我们恰好k=2,那就是每一层都添加索引

至于删除,就是把这个数据的每一层的索引都进行删除
散列表利用散列函数f(k),把原本的关键字key变成一个f(key),存table中,table一般是类似数组的那种东西,搜索的复杂度为1.
举个例子:java中的map差不多,map底层利用的是一个手动长度可变数组+每个桶都是一个链表的方式进行存储的
散列函数有很多,常见的散列函数是mod n
理想的散列表中,不会发生溢出,所以最理想的情况下,增删改查的复杂度都是1
但是散列表一般不会很理想,绝大多数时候,散列函数是无法保证完整的一对一映射,所以溢出的两种处理方式为