查找

基本概念:

查找结构(查找表):用于查找的数据集合称为查找结构(查找表),它可以是一个链表,也可以是一个数组或其他数据类型。对于查找表经常进行的操作一般有四种:
1.查询某个特定的数据元素是否在查找表中
2.检索满足条件的某个特定元素的各种属性
3.在查找表中插入一个数据元素
4.从查找表中删除某个数据元素
如果查找表只涉及前两个操作的查找表称为静态查找表,适合静态查找表的方法为:顺序查找,折半查找,散列查找等。
需要动态的修改查找表则称为动态查找表,适合动态查找表的方法有:二叉排序树的查找、散列查找。
平均查找长度:在查找过程中,一次查找的长度是指需要比较的关键码次数,而平均查找长度则是所有查找过程中进行的关键码比较次数的平均值。
这里写图片描述
Pi为查找第i个元素的概率,一般认为每个元素的查找概率相等,Ci为找到第i个元素所需的比较次数。平均查找长度是衡量查找算法效率的最主要指标。
例:查找一个整数数组中第二大的数

const int MINNUMBER=-32767;
int find_sec_max(int data[], int count){
    int maxnumber = data[0];
    int sec_max = MINNUMBER;
    for(int i =1; i < count; ++i){
        if(data[i] > maxnumber){
            sec_max = maxnumber;
            maxnumber= data[i];
        }
        else{
            if(data[i] > sec_max)
                sec_max = data[i];
        }
    }
    return sec_max;
}

折半查找

折半查找又称为二分查找,适用于已经排好序的顺序表,基本思路为:首先将给定K与表中中间位子元素的关键字比较,若相等,则查找成功,返回该元素的存储位置;若不等,则所需查找的元素只能在中间数据以外的前半部分或后半部分中。然后在缩小的范围中继续进行同样的查找,如此重复知道找到为止。
代码如下:

int Binary_Search(SeqList L, ElemType key){
    int low = 0, high = L.TableLen-1, mid;
    while(low <= high){
        int mid = (low+high)/2;//取中间位置
        if(L.elem[mid] == key)
            return mid;//查找成功则返回所在的位置
        else if(L.elem[mid] > key){
            low = mid+1//从前半部分继续查找
        }
        else
            high = mid-1;//从后半部分继续查找
    }
    return -1;
}

上述算法对数组元素重复的时候不支持,如数组是:
[2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2]
需要用到判定树

判定树

折半查找的过程可用二叉树来表示,称为判定树。树中的每个圆形节点表示一个记录,节点中的值为该记录的关键字值;树中最下面的叶节点都是方形的,它表示查找不成功的情况。若有序序列有n个元素,则对应的判定树有n个元素,则对应的判定树有n个圆形的非叶节点和n+1个方形的叶节点。
折半查找的时间复杂度为O(logn),比顺序查找的效率高。

键树(前缀树)

键树的定义与Trie树

键树又称数字查找树。它是一颗度大于等于2的树,树中的节点只含有组成关键字的符号。
键树的存储通常有两种方式:

1.用树的孩子-兄弟链表来表示键树

每个Node有三个域:
symbol域:存储关键字的一个字符
son域:存储指向第一颗子树的根的指针
brother域:存储指向右兄弟的指针
这时的键树又称为双链树。如图所示
这里写图片描述
查找过程是,从根节点出发,顺着son找,如果相等,继续下一个son。否则沿着brother查找。直达找到了空指针为止。此时若仍未完成key的匹配,查找不成功。

用多重链表表示(Trie树,字典树)

Trie树的简单实现:

const int branchNum = 26;
//在结点钟不设数据域,每个分枝接点所表示的字符均由其双亲节点中(指向该节点)的指针所在位置决定
struct Trie_node{
    bool isStr;//
    Trie_node *next[branchNum];
    Trie_node():isStr(false){memset(next, NULL, sizeof(next));}
};

class Trie{
public:
    Trie();
    //插入一个单词
    void insert(const char* word);
    //查找单词
    void search(char* word);
    //销毁字典树
    void deleteTrie(Trie_node* root);
    Trie_node* getTrie(){return root;}
private:
    Trie_node* root;
};

void Trie::search(char* word){
    Trie_node *location = root;
    while(*word){
    //不存在则创建一个新的节点
        if(location->next[*word-'a'] == NULL){
            Trie_node *tmp = new Trie_node();
            location->next[*word-'a'] = tmp;
        }
        location = location->next[*word-'a'];
        word++;
    }
    location->isStr = true;
}
/*从根节点出发,沿和给定值相应的指针逐层向下,若到达给定值的
尾部,且此节点标记为true,则成功;若节点中的给定值对应的指针
为空,或到达给定值尾部时,节点中的标记为false,则查找不成功*/
bool Trie::search(char *word){
    Trie_node *location = root;
    while(*word && location){
        location = location->next[*word-'a'];
        word++;
    }
    return (location != NULL && location->isStr);
}

void Trie::deleteTrie(Trie_node *root){
    for(int i = 0; i < branchNum; ++i){
        if(root->next[i] != NULL)
            deleteTrie(root->next[i]);
    }
    delete root;
}

Trie树的典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎用于文本词频统计。
Trie树的优点是最大限度地减少无畏的字符串比较。
Trie树的缺点是如果存在大量字符串且这些字符串基本没有公共前缀,则相应的Trie树将非常消耗内存。

哈希表

哈希表的基本概念

哈希表也称散列表,它是基于快速存取的角度设计的数据结构,是一种典型的“空间换时间”的做法。
哈希表是根据关键字(key,value)直接进行访问的数据结构,将关键字
通过某种规则映射到数组中的某个位置,以加快查找的速度。映射规则称为哈希函数,存放记录的数组称为哈希表。

哈希函数

所有散列函数都有如下一个特性:如果两个散列值是不相同的,那么这两个散列值的原始输入也是不相同的。

哈希冲突时的解决办法

线性探测法、二次探测法、伪随机探测法,再散列法,链地址法、建立一个公共溢出区
采用链地址法处理长度的时候,哈希表查找成功的平均长度与装填因子有关
装填因子= 表中填入的记录数/哈希表的长度

一致性哈希

几种常见的分布式存储方式

普通集群

普通集群把固定的key映射到固定的结点上,结点只存放各自key的数据。将key和结点的关系作为一张单独的表格进行维护,当其中一个节点宕机,节点上的数据需要迁移,此表格也需要维护。
此方法的问题时,当需要查找某个key值对应的数据时,必须遍历所有表格,直到需要到存放此key值的节点,然后再去对应节点读取数据,查找速度慢。

hash集群

为了降低复杂性和其他开销,对数据的key进行哈希。
此种方法的不足是:加入某个时候其中一个节点宕机了,那这个节点的数据就完全不可用了。当要进行数据迁移时,因为这时候节点少了,重新取模时,大部分数据都要迁移,这个时候只能整个集群的数据都重新映射一遍才能达到效果。

一致性哈希

一致性哈希是一种哈希算法,在移除或添加一个节点时,它能够尽可能地改变已存在key的映射关系。
一致性哈希将整个哈希值空间组成一个虚拟的圆环,假设某哈希函数Hash的值空间为0-2^32-1.一致性哈希的基本思想就是使用相同的哈希算法将数据和节点都映射到环形哈希空间中。
把数据映射到Hash空间
假设有4个数据object1-object4,通过哈希函数计算出的哈希值key在环上的分布如hash(object1) = key1…hash(object4) = key4
把节点映射到Hash空间
具体可以选择节点服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。
把数据映射到节点
现在节点和数据都已经通过同一个哈希算法Hash映射到哈希数值空间了,在这个环形空间中,如果沿着顺时针方向从数据的key值出发,知道遇见一个节点机器,那么久将该数据存储在这个节点上,因为数据和节点的哈希值是固定的,因此这个节点必然是唯一和确定的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值