查找专题

Posted Rainbowman 0

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了查找专题相关的知识,希望对你有一定的参考价值。

1. 静态查找

静态查找 vs 动态查找

静态查找只做查询操作,而动态查找表还需要做插入和删除操作,如:若查找的元素不在表中,则进行插入;或删除查找到的元素。

静态查找中主要想说二分查找,这是一种很简单但很高效的查找方式,但只能针对有序表,无序表不能进行二分查找。

最好情况下时间复杂度为O(1)
最坏情况下时间复杂度为O(logn)

参考:时间复杂度推导

来道leetcode例题

在这里插入图片描述

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if(nums.empty())  return 0;

        int l = 0, r = nums.size()-1;
        int count = 0;
        int i;
        while(l<=r){
            i = (l + r)/2;
            if(nums[i]==target) break;
            if(nums[i]<target)  l = i + 1;
            else r = i - 1;
        }
        if(l>r)  return 0;

        for(int j=i-1; j>=0; j--){
            if(nums[j]!=target)  break;
            count++;
        }
        for(int j=i; j<nums.size(); j++){
            if(nums[j]!=target)  break;
            count++;
        }

        return count;
    }
};

2. 动态查找

动态查找主要想说的是二叉排序树的查找、增加和删除结点的问题。

二叉排序树的左子树结点的值均小于根结点,右子树结点的值均大于根结点,左右子树也是二叉搜索树(递归定义)。

二叉排序树有一条重要性质:

使用中序遍历二叉排序树,得到的是一递增序列。

二叉排序树的查找:

因为二叉排序树的特殊性质,所以查找某一元素是否在二叉排序树中时,不需要像别的二叉树一样使用先序(或中序或后序)遍历所有的元素,而只需:

比较要查找的元素N和根元素的大小:
(1)若相等,则返回指向根结点的指针;
(2)若小于根元素,则继续查找根结点的左子树;
(3)若大于根元素,则继续查找根结点的右子树;
若最后找到,则返回找到结点的指针;若未找到,则返回空指针。

感觉有点二分查找的味道~

二叉排序树的插入:

插入是建立在查找的基础上完成的:在查找过程中,若没找到则返回空指针,之所以返回空指针是因为找到最后的叶子结点的孩子结点了(当然为空)还没找到,而我们要插入的元素即为该叶子节点的孩子(左孩子或有孩子)。

因此,查找过程中需要有一个先驱指针f指向目前对比结点p的父亲结点,若目前对比结点已经为空(说明没有查到),则将要查找的元素插入到f结点的孩子结点(左孩子或右孩子)。

注意: 若二叉排序树为空树,则新插入的结点为新的根结点;否则,新插入的结点必为一个新的叶子结点,其插入位置由查找过程得到。

二叉排序树的删除:

删除某结点有三种情况:

(1)要删除的是叶子结点:
直接删除,然后将其父亲结点的相应指针置为空;

(2)要删除的结点只有左子树(或右子树):
将其父亲结点的相应指针指向其左子树(或右子树),然后删除该结点;
在这里插入图片描述
在这里插入图片描述

(3)要删除的结点既有左子树又有右子树:
找到其前驱节点,即该二叉排序树中第一个比要删除的结点小的结点(位于要删除结点的左子树的最右边,即其没有右子树),将要删除的结点的值置为前驱结点的值,然后将前驱结点的父结点的指针指向前驱结点的左子树(即删除前驱节点;)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二叉排序树的插入、查找、删除代码:

#include <iostream>
#include <stdlib.h>
using namespace std;
struct BiTreeNode
{
    int data;
    BiTreeNode *lchild;
    BiTreeNode *rchild;
};
typedef BiTreeNode * BiTree;

bool SearchBST(BiTree T,int N,BiTreeNode *f,BiTreeNode * &p)
{
    if(!T){p=f;return false;}
    else if(T->data==N) {p=T;return true;}
    else if(N<T->data) SearchBST(T->lchild,N,T,p);
    else if(N>T->data) SearchBST(T->rchild,N,T,p);
}

//插入特点:新插入的结点一定是个叶子结点,而且是
//查找不成功时p指向查找路径上的最后一个结点(要插入的结点为该结点的左孩子或右孩子)
bool InsertBST(BiTree &T,int N)
{
    BiTreeNode *f=0;
    BiTreeNode *p=0;
    if(!SearchBST(T,N,f,p))
    {
        BiTreeNode *s=new BiTreeNode();
        s->data=N;
        s->lchild=0;s->rchild=0;
        //这一个别忘了!!
        if(!p)T=s;

        else if(N<p->data)p->lchild=s;
        else if(N>p->data)p->rchild=s;
        return true;
    }
    return false;
}
void InOrdTraverse(BiTree T)
{
    if(T)
    {
        InOrdTraverse(T->lchild);
        cout<<T->data<<" ";
        InOrdTraverse(T->rchild);
    }
}
// 删除节点
bool Delete(BiTree &p)
{
    if(!p->lchild)
    {
        BiTree q=p;
        p=p->rchild;
        free(q);
        return true;
    }
    else if(!p->rchild)
    {
        BiTree q=p;
        p=p->lchild;
        free(q);
        return true;
    }
    else
    {
        BiTree q=p;
        BiTree s=p->lchild;
        while(s->rchild){q=s;s=s->rchild;}
        p->data=s->data;
        if(q!=p)q->rchild=s->lchild;
        if(q==p)p->lchild=s->lchild;
        free(s);
        return true;
    }
    return false;
}

bool DeleteBST(BiTree &T,int N)
{
    if(!T) return false;
    if(N==T->data){Delete(T);return true;}
    else if(N<T->data){DeleteBST(T->lchild,N);}
    else DeleteBST(T->rchild,N);
}

const int N=5;
int main()
{
    BiTree T=0;
    int a[N];
    for(int i=0;i<N;i++)cin>>a[i];
    for(int i=0;i<N;i++){InsertBST(T,a[i]);}
    InOrdTraverse(T);
    int b[N];
    for(int i=0;i<N;i++){
        cin>>b[i];
        DeleteBST(T,b[i]);
        InOrdTraverse(T);
    }
    return 0;
}

3. 哈希表

哈希表是以空间换时间的策略,重点有哈希函数的构建和如何解决冲突。

哈希函数有很多种,主要是需要保证元素能均匀分布在哈希表中。

如何解决冲突?

(1)开放定址法:

思想:当经过映射函数(哈希函数)找到的地址发生冲突时,在该地址的基础上寻找别的地址。

Hi= ( H(key)+di ) MOD m, i= 1,2, … k ( k ≤ m-1) m 为表长, di 的取法:
① di=1,2,3, …, m-1 称线性探测再散列;
② di=12,-12,22,-22,32,-32,…±k2 (k ≤ m/2), 称为二次探测再散列;
③ di=伪随机数系列; 伪随机探测再散列。

(2)再哈希法:

思想:当发生地址冲突时,使用别的哈希函数重新计算地址,直到地址不发生冲突为止(增加了计算时间)

(3)链表法:

思想:将所有关键字为同义词的记录存储在同一线性链表中

例子:已知一组关键字为:(19,14,23,01,68,20,84,27,55,11,10,79);哈希函数 H(key)=key MOD 13 ;用链地址法处理冲突所得哈希表如下图。

在这里插入图片描述

以上是关于查找专题的主要内容,如果未能解决你的问题,请参考以下文章

算法专题(01)二分查找(01) 简单LeetCode 704

算法专题(01)二分查找(03) 简单LeetCode 278

算法专题(01)二分查找(02) 简单LeetCode 35

查找专题

JSP编程专题1之JSP基础

二分查找专题总结 - 基础篇