数据结构笔记--总结各种查找算法及其应用

来源:互联网 发布:java微信 api好友列表 编辑:程序博客网 时间:2024/06/07 17:56

基于线性表的查找具体分为顺序查找法、折半查找法以及分块查找法。三种方式都非常简单,在此引出一个概念,平均查找长度。即为了确定数据元素在列表中的位置,需和给定值进行比较的关键字个数的期望值,称为查找算法在查找成功时的平均查找长度。对于长度为n的列表,查找成功时的平均查找长度为ASL = P1C1 + P2C2 + P3C3 + … + PnCn = ∑PiCi (i=i..n) 其中Pi为查找列表中第i个元素的概率,Ci为找到列表中第i个元素时,已经进行过的关键字比较次数。那么顺序表的平均查找长度就是1/2(n+1),折半查找的平均查找长度为((n+1)/n)log(n+1)-1,当索引使用顺序查找时的分块查找的平均查找长度为1/2(n/s+s)+1,当索引使用二分查找是的平均查找长度为log(n/s+1)+s/2。其中表的长度为n,分为b块,每块含有s个元素。

// 二分查找,返回下标template<typename T>int BinSearch(T *array, int len, T key){    int lowIndex = 0, highIndex = len - 1;    int midIndex = 0;    while (lowIndex <= highIndex)    {        midIndex = (lowIndex + highIndex) / 2;        if (key == array[midIndex])            return midIndex;        else if (key < array[midIndex])            highIndex = midIndex - 1;        else            lowIndex = midIndex + 1;    }    return -1;}

基于树的查找:又称树表查找法,主要包括二叉排序树、平衡二叉排序树和B_树。这里主要介绍二叉排序树。
二叉排序树的插入
1、若二叉排序树是空树,则key(待插入值)成为二叉排序树的根;
2、若二叉排序树非空,则将key与二叉排序树的根比较,如果key等于根节点的值,停止插入;
如果key小于根节点的值,则将key插入左子树;
如果key大于根节点的值,则将key插右左子树。

// 向二叉排序树中插入void InsertBT(BitTree **root, DataType key){    if ((*root) == NULL)    {        *root = new BitTree();        (*root)->m_data = key;        (*root)->m_lchild = (*root)->m_rchild = NULL;    }    else    {        if (key < (*root)->m_data)        {            InsertBT(&(*root)->m_lchild, key);        }        else if (key >(*root)->m_data)        {            InsertBT(&(*root)->m_rchild, key);        }    }}

二叉排序树的删除
1、若p(待删除的节点)为叶子节点,则可将其直接删除。
2、若p只有左子树,或者只有右子树,则可将p的左子树或右子树直接改为p的双亲节点的左子树。
3、若p既有左子树,又有右子树,则首先找到p的左子树中最大的值,并将其值赋给p节点,然后删掉这个最大值。偷梁换柱。

// 在二叉排序树中删除void DeleteBT(BitTree **root, DataType key){    BitTree *pCul = *root, *pParent = NULL;    while (pCul != NULL)    {        if (key == pCul->m_data)        {            break;        }        pParent = pCul;        if (key < pCul->m_data)        {            pCul = pCul->m_lchild;        }        else        {            pCul = pCul->m_rchild;        }    }    if (pCul == NULL)    {        return;    }    if (pCul->m_lchild == NULL)    {        if (pParent == NULL)        {            *(root) = pCul->m_rchild;            delete pCul;            pCul = NULL;        }        else        {            if (pParent->m_lchild == pCul)            {                pParent->m_lchild = pCul->m_rchild;            }            else if (pParent->m_rchild == pCul)            {                pParent->m_rchild = pCul->m_rchild;            }        }        delete pCul;        pCul = NULL;    }    else    {        BitTree *pTempCul = pCul->m_lchild, *pTempParent = pCul;        while (pTempCul->m_rchild != NULL)        {            pTempParent = pTempCul;            pTempCul = pTempCul->m_rchild;        }        pCul->m_data = pTempCul->m_data;        if (pCul == pTempParent)        {            pTempParent->m_lchild = pTempCul->m_lchild;        }        else        {            pTempParent->m_rchild = pTempCul->m_lchild;        }        delete pTempCul;        pTempCul = NULL;    }    return;}

二叉排序树的平均查找长度为log n;

计算式查找法:哈希法
处理冲突的方法:1、开放定址法,当key的hash地址出现冲突时,以此地址为基础,在产生一个hash地址,直到不冲突为止。
2、再hash法,同时构造多个不同的hash函数,当hash地址发生冲突时,再使用另一个hash函数重新计算,并写入新表。
3、链地址法,将所有出现冲突的元素构成一个单链表。
4、建立公共溢出区,将hash表分为基本表与溢出表两部分,没发生冲突的放在基本表,否则放在溢出表。

拓展:查找一组序列中最小的K个数
将这组序列中前k个数维护成一个最大堆,然后遍历序列,将序列中的元素与最大堆的首元素进行比较,如果序列中的元素大于堆中的元素,那么他就一定大于堆中其他元素,即不是最小的k个数,丢弃。否则,交换。在STL中,set和multiset都是使用红黑树实现的,所以这两个容器可以用来模拟最大堆。multiset允许插入元素重复。

void SearchLeastNum(const vector<DataType> &array, const size_t k, multiset<DataType, greater<DataType>> &least){    least.clear();    if (k < 1 || array.size() < k)    {        return;    }    auto vIt = array.begin();    for (; vIt != array.end(); ++vIt)    {        // 先初始化一个包含K个元素的最大堆        if (least.size() < k)        {            least.insert(*vIt);        }        // 如果要插入的元素小于最大堆的最大值,替换        else        {            auto sIt = least.begin();            if (*vIt < *sIt)            {                least.erase(sIt);                least.insert(*vIt);            }        }    }}
0 0
原创粉丝点击