您的位置:首页 > 理论基础 > 数据结构算法

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

2016-07-30 19:24 585 查看
基于线性表的查找具体分为顺序查找法、折半查找法以及分块查找法。三种方式都非常简单,在此引出一个概念,平均查找长度。即为了确定数据元素在列表中的位置,需和给定值进行比较的关键字个数的期望值,称为查找算法在查找成功时的平均查找长度。对于长度为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);
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: