【C/C++】查找(一):静态查找表

{静态查找表 + 动态查找表}

所谓动态,就是,找的时候没有则添加,或者能删除



关键字:primary key:用来表示查找表中的一条记录

{主关键字 + 次关键字}

主关键字是唯一的,用来唯一的标识查找表中的一条记录


(一)静态查找表



一、顺序表

类似于数组,顺序存储,在表中有位置,查找即给定关键字,遍历这个表,找到其位置或给出整条记录;

可以设置“哨兵” ,即标记0号元素就是要找的关键字:这样可以减去每次判断是否查找结束的麻烦,平均时间几乎减少一半

优化:

若访问频度不同,则设置频度域,根据需要调整位置,如每次将刚刚访问过得节点放在最后。



intsearch_Seq(SSTable ST,KeyType key){
//在顺序表ST中 找key 
    ST.elem[0].key = key;
    for(int i = ST.Length;!EQ(ST.elem[i].key,key),--i);
    return i; //找不到返回0 
}


算法复杂度分析:



最坏条件下:O(n)

平均:

(1+2+...+n) / n (等概率被查找到的情况下)

O((1+n)/2)

若考虑查找不成功,则查找不成功的情况都是查找n+1次。

那么:

O((n+1)*3/4)

def:

平均查找长度:为确定就在查找表中的位置,需和给定值进行比较的关键字的个数的期望值称为查找成功时的平均查找长度

即:

ASL = 求和(pi * Ci)

Ci为第i元素查找的步数

二、 有序表的查找

主要讲三种,第一是经典的二分直接贴代码



int Search_Bin(SSTable ST,KeyType key) {
    int low = 1;
    int high = ST.Length;
    int mid;
    while(low<=high){
        mid = (low+high)/2;
        if(EQ(key,ST.elem[mid].key)){
            return min;
        }else if(LT(key,ST.elem[mid].key)){
            high = mid - 1;
        }else{
            low = mid + 1;    
        }
    }
    return 0;
}


二分法的复杂度分析:(面试等常问的)

将查找过程构建成一棵树,树的节点是每次访问的mid,这叫判定树

由于树的深度h,定点数n = 2^h-1

同一深度上的节点被查找需要查找走的步数就是深度h

那么:

平均查找,在等概率下:

ASL = (求和i从1到最高层h(i*2^(i-1)))/n

因为,第i层上要比较i次,第i层有2^(i-1)个结点



当每个节点被查找到的概率不同时,二分查找可能并不是最优的,最优的是:

PH = 求和(p*c)的查找,即判定树的带权内路径长度之和最小

但这样做很复杂,可以以一种求折中的方法:求次优查找树



思想:

求一个结点,它的左右两边的点被查找到的概率最大可能性的相等, 则该点设置为mid

递归,查找左子树、右子树



实现:

为了方便,设置权值之和SWi:即从开始到第i点的权值之和

则每次查找一个点,只要计算该点右边之和减去左边的(各自区间内) ,取最小点



细节注意:

这并没有考察单个关键字的权值,有的时候,被选作根(mid)的关键字的权值比与他相邻的关键字的权值小

此时应做调整



复杂度分析:

构造一棵次优查找树,复杂度是:

O(nlogn)

算法实现:



//建立树T,根节点T,使用:R是查找表,sw是其权值,low-high是当前区间
    int i = low;
    float min = fabs(R[high] - R[low]);
    float dw = sw[high] + sw[low-1];
    for(int j = low+1;j<=high;j++){
        if(fabs(dw - sw[j] - sw[j-1]) < min){
            min = dw - sw[j] - sw[j-1];
            i = j;
        }
    }
    //i作为mid 
    T = (BiTree)malloc(sizeof(BiTNode));
    T->data = R[i];
    if(i == low){
        T->lchild = NULL;
    }else{
        SecondOptimal(T->lchild,R,sw,low,i-1);
    }

    if(i == high){
        T->rchild = NULL;
    }else{
        SecondOptimal(T->rchild,R,sw,i+1,high);
    }
}

综合如下:

typedef BiTree SOSTree;
Status CreateSOSTree(SOSTree &T,SSTable ST){
    if(ST.Length == 0){
        T = NULL;
    }
    else{
        findSW(sw,ST);//建立sw数组,权值和
        SecondOptimal(T,ST.elem,sw,1,ST.Length);
    }
    return OK;
}

静态查找还有:斐波那契、插值查找

斐波那契:

Fn = Fn-1 + Fn-2;

F1 = 1;

F0 = 0;

斐波那契查找就是:

假设表长我某个斐波那契值-1,即n = Fu-1;

则在F(u-1)与key比较

找不到就:

if key<F(u-1).key:

在1..F(u-1)中找

else:

在F(u-1)+1 ... F(u) 中找

分析:

平均复杂度比二分好,但最坏情况下的一次查找不如二分


而插值查找:

每次比较的i(相当于二分中的mid),i是有公式算的。

平均性能比二分好。




原文链接: https://www.cnblogs.com/duye/p/9118872.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/275031

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月15日 上午12:43
下一篇 2023年2月15日 上午12:44

相关推荐