408数据结构学习笔记——顺序查找、折半查找、分块查找

目录 

1.顺序查找  

1.1.顺序查找的概念

1.2.顺序查找的代码

1.3.顺序查找的查找效率

1.4.顺序查找的优化

1.5.顺序表的时间复杂度

2.折半查找

2.1.折半查找的概念

2.2.折半查找的代码

2.3.折半查找的查找效率​

2.4.折半查找判定树的构造

3.分块查找

3.1.分块查找的概念

3.2.分块查找手算过程

3.2.1.顺序查找索引表

3.2.2.折半查找索引表

3.3.分块查找效率分析

4.王道课后题


1.顺序查找  

1.1.顺序查找的概念

通常用于线性表,从表中第一个元素开始,逐一检查当前元素是否满足条件

1.2.顺序查找的代码

typedef struct{
    elemtype *elem;    //动态数组的首地址
    int tableLen;    //表长
}SSTable;

//顺序查找
int Search_Seq(SSTable ST, elemtype key){
    int i;
    //从头逐一查找表中元素
    for (i = 0; i < ST.tableLen && ST[i] != key; i++);
    //判断i是否等于表长
    if (i == St.tableLen) return -1;
    else return i;
}

1.3.顺序查找的查找效率

查找成功时,平均查找长度为ASL = n - i + 1

查找失败时,平均查找长度为ASL = n + i

1.4.顺序查找的优化

1.在有序表中若是从小到大排列,当发现第 i 个元素的大小已比key更大时,就可以跳出循环,return -1

2.顺序表中给个元素的被查概率不相等,则按被查概率依次存放

1.5.顺序表的时间复杂度

O(N)

2.折半查找

2.1.折半查找的概念

仅使用于有序的线性表(只能以顺序存储)

若各个元素并非等概率出现,折半查找性能并不一定优于按出现概率降序排列的顺序查找

算法思想:

1.申明三个指针low, high, mid,low指向数组的第一个元素,high指向数组的最后一个元素,mid指向(high + mid) /2 (向下取整)

2.通过mid和key的对比判断key在数组中的位置,如果mid更大,则key在mid的左边,且key在数组的范围为(low, mid - 1);如果key更大,则key在mid的右边,且key在数组的范围为(mid + 1, high)

3.修改low和high的指针,分别指向范围的下限和范围的上限(缩小范围),继续查找,直到找到为止

2.2.折半查找的代码

int Binary_Search(elemtype arr[], elemtype key, int length){
    //初始化high为数组最后一个元素的数组下标,low为0
    int high = length - 1, low = 0, mid;
    while (low <= high){
        //每次循环开始的时候,重置mid指针为high和low的中间值
        mid = (high + low) / 2;
        //当前mid指向的元素为key时,查找成功,返回mid
        if (arr[mid] == key) return mid;
        //mid比key小,去mid的右半区查
        else if (arr[mid] < key) low = mid + 1;
        //mid比key大,去mid的左半区查
        else high = mid - 1;
    }
    //结束循环时,说明数组中没有key值,查找失败,返回-1
    return -1;
}

2.3.折半查找的查找效率

查找成功(到绿色点):最多查找4次 ASL = (1 * 1 + 2 * 2 + 3 * 4 + 4 * 4) / 11 = 3

因为折半查找只有最后一层未满的特性,n - 1层都是满的(满二叉树),因此,在计算查找成功时,可以利用这一特性方便计算

计算时:

1 ~ n - 1:层 同结构满二叉树同层结点数 * 当前层数

n 层:(总结点数 - (n - 1)层总结点数)* 当前层数

查找失败(到紫色点):最多查找5次 ASL = (3 *  4 + 4 * 8) / 12 = 11 / 3

查找失败时只需查找到绿色结点,不需要到紫色结点再比较一次

计算时:最后一层的结点数 * 层数 + (同结构满二叉树 - 最后一层结点数) * (层数 - 1)

2.4.折半查找判定树的构造

当前若low和high之间的个数为奇数个,则mid分割后,左右子树的个数相等

当前若low和high之间的个数为偶数个,则mid分割后,右子树的个数比左子树大1

1.折半查找判定树中,对于任何一个结点,则都有左子树结点数 - 右子树结点数 = 0 或 1 (平衡二叉树)(构造判定树中,仅需根据这一特性每次找到当前子树的根节点划分左右子树)

2.折半查找判定树中,只有最下面一层是不满的,且树高为h = log(n+1)向上取整

3.失败结点为n - 1个(成功结点的空链域的数量)

4.结点关键字中,左 < 中 < 右(排序二叉树类似,但是排序二叉树可能会出现单支的情况,最坏时间复杂度为O(n);而折半查找判定树一定是平衡树)

5.折半查找的查找成功和查找失败的次数均不超过树高,因此,时间复杂度为O(logn)

6.折半查找的时间复杂度虽然比顺序查找低,但是并非在任何情况下其查找效率都更高

7.最少查找次数每次都去结点最少的子树,最多查找次数每次都去结点最多的子树

3.分块查找

3.1.分块查找的概念

为数组建立一个索引表,并将数组分割成为若干个块,索引表中一次存放每个块内的最大值和块内元素的存储区间(块内无序,块间有序)

分块查找的过程:

1.在索引表中确定key所属的分块(索引表可以采用顺序查找,也可以采用折半查找)

2.在块内查找(非有序,只能顺序查找)

3.2.分块查找手算过程

3.2.1.顺序查找索引表

以查找36为例

1.在索引表中依次遍历找到第一个大于它的值,即40

2.移动到40指向的数组区间的第一个元素,即数组下标9

3.从数组下标9开始遍历,40→36 找到36

以查找17为例

1.在索引表中依次遍历找到第一个大于它的值,即20

2.移动到20指向的数组区间的第一个元素,即数组下标2

3.从数组下标2开始遍历,13→19→16→20

4.此时移动到20,数组下标为5,到索引表中17的最后一个元素,仍匹配失败,说明表中没有17

3.2.2.折半查找索引表

索引表只是保存元素的数组区间,方便在数组中寻找元素(相对位置),就算在索引表上找到相应元素,还是得再去数组中重新再找一次

以查找30为例

1.low指向10,high指向50,mid = (low + high) / 2 指向 2,即指向30

2.到30所指向的数组区间依次查找

以查找19为例

1.low指向10,high指向50→mid指向30,mid > key , high = mid - 1

2.low指向10,high指向20→mid指向10,mid < key,  low = mid + 1

3.low指向20,high指向20→mid指向20,mid > key,  high = mid - 1

4.low指向20,high指向10→low > high 折半查找结束

5.到low所指向的元素区间进行逐一遍历查找

去low指向区间查找的原因:最后low的左边一定小于key,而分块查找的索引表存放的是每块的最大值

除了能在key能在索引表上直接找到,否则都要执行到low > high,才能确定key元素所可能存在的数组区间范围

3.3.分块查找效率分析

分块查找的平均查找长度分为索引表查找L1和块内查找L2

设所有元素被均匀的分为b块,每块内有s个元素

索引表顺序查找:L1 =  \frac{b+1}{2}  L2 = \frac{S+1}{2} →ASL = \frac{S^{2}+2S+n}{2S}

当块内元素为\sqrt{n}时,ASL取最小值为\sqrt{n}+1

索引表折半查找:L1 = log(b + 1) 向上取整 L2 = \frac{S+1}{2}(一般不考)

可以对分块查找的索引表和块内元素都采用折半查找

4.王道课后题

1.平均查找长度不同。设查找元素为k,有序顺序表若找到第一个大于k的元素,就可以结束函数,返回查找失败;而无序表每次都需要遍历完整个表

2.平均查找长度相同。两个表中都是找到k就停止,返回查找成功

3.平均查找长度不同。有序表仅需找到第一个等于k的元素后,设该元素位序为p,再往后遍历找到第一个不等于k的元素,设该元素位序为q,则p ~ q - 1为表中所有值为k的元素,无需遍历整个表;而无序表中每次都需要遍历表中所有元素

2.275:509→154→275        684:509→677→897→765

3.查找成功的平均查找长度:(1 * 1 + 2 * 2 + 4 * 3 + 7 * 4) / 14 = 45 / 14

查找失败的平均查找长度:(1 * 3 + 14 * 4) = 59 / 14

int k_search(int arr[], int n, int k, int key){
    int left = 0, right = n, mid = k / n;
    while (left <= right){
        mid = k / n;
        if (arr[mid] == key) return arr[mid];
        else if (arr[mid] < key) right = k - 1;
        else left = k + 1;
    }
    return -1;
}

参考折半查找,O(log_k{n})

2.每8个元素进行一轮顺序查找,确定元素所在的区间,顺序查找的平均查找长度为 (n + 1) / 2 

进入8k ~ 8(k+1) - 1的区间内,分为两种:

(1)顺序查找(每轮八个元素的最后一个元素)的平均查找长度:1

(2)折半查找(前七个元素构成的折半查找判定树):(1 * 1 + 2 * 2 + 4 * 3) = 16

每个关键字查找成功的概率为1 / 8

因此,结果为: (n + 1) / 2 +(1 + 16) / 8

int Binary_Search(int arr[], int left, int right, int key) {
    //temp变量保存当前结果
    int temp = -2;
    //当前没有结果且还未遍历完数组
    if (left <= right) {
        //重置mid
        int mid = (left + right) / 2;
        //mid = key,返回mid的下标
        if (key == arr[mid]) temp = mid;
        //mid比key大,去右子树
        else if (key < arr[mid]) temp = Binary_Search(arr, left, mid - 1, key);
        //mid比key小,去左子树
        else temp = Binary_Search(arr, mid + 1, right, key);
    }
    //当前没有结果遍历完数组,返回-1表示查找失败
    else temp = -1;
    //返回结果
    return temp;
}

int SearchEntrance(int arr[], int n, int key) {
    int left = 0, right = n - 1;
    //用res保存key元素的数组下标
    int res = Binary_Search(arr, left, right, key);
    return res;
}

//顺序结构
int Search_Exchange(int arr[], int key, int n){
    int res = -1, i = 0;
    //第一个元素为key时
    if (arr[0] == key) {
        res = 0;
        return res;
    }
    //从第二个元素开始遍历数组
    for (i = 1; i < n; i++){
        if (arr[i] == key) {
            //将第i个元素前移一位
            int temp = arr[i - 1];
            arr[i - 1] = arr[i];
            arr[i] = temp;
            res = i;
        }//if
    }//for
    //返回key元素更改后的数组下标
    return res;
}            
//链式结构
typedef struct LNode{
    struct LNode *next;
    elemtype data;
}LNode, *LinkList;

LNode *Search_Exchange(LinkList &L, elemtype e){
    LNode *p = L, *pre = L = NULL;
    while(p->next){
        //e元素是链表的一个元素,无需修改前驱,返回其地址
        if (p->next->data == e){
            if (p == L) return p->next;
            else {
                //修改pq的相对位置
                LNode *q = p->next;
                pre->next = q;
                p->next = q->next;
                q->next = p;
        }//if
        else{
            pre = p;
            p = p->next;
        }
    }//while
    //表中没有指定元素
    return NULL;
}

 

1、2都采用概率降序依次存储元素,平均查找长度为2.1

若元素出现非等概率,折半查找并非一定优于顺序查找(概率降序)

  • 8
    点赞
  • 34
    收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:游动-白 设计师:我叫白小胖 返回首页
评论 2

打赏作者

江南江南江南丶

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值