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

基于线性表的查找具体分为顺序查找法、折半查找法以及分块查找法。三种方式都非常简单,在此引出一个概念,平均查找长度。即为了确定数据元素在列表中的位置,需和给定值进行比较的关键字个数的期望值,称为查找算法在查找成功时的平均查找长度。对于长度为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);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值