【C语言督学训练营 第十五天】常用的查找算法----顺序查找、二分查找、二叉排序树

news2024/11/25 12:24:13

文章目录

  • 前言
  • 一、顺序查找
    • 1,思想
    • 2.代码实战
  • 二、二分查找
    • 1.思想
    • 2.代码实战
  • 三、二叉排序树
    • 1.建树思想
    • 2.删除节点思想
    • 3.代码实战
  • 考研真题实战

前言

本篇博客会介绍到数据结构中常用到的查找算法,合理的使用查找算法可以让我们很轻松找到自己想要的答案。本小节必须掌握的是顺序查找、二分查找、二叉排序树的建立,有剩余时间可以实现二叉排序树的节点删除,以及2011年真题实战42题的代码。

算法思想如下:


  • 顺序查找:思想非常简单,直接遍历一遍列表即可!
  • 二分查找:针对有序数组非常实用,搜索范围每次直接折半,搜索效率大大提升。
  • 二叉排序树:Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树。最大查询次数为树的深度。
  • 真题实战:二分查找的升级版,实用二分查找的思想实现查找有序列表的中位数。

目前还没有对各种算法的时间复杂度空间复杂度进行分析!后面数据结构会详细整理!

先看两道真题预热一下:
在这里插入图片描述
在这里插入图片描述

一、顺序查找

1,思想

顺序查找又称线性查找,它对于顺序表和链表都是适用的。对于顺序表,可通过数组下标递增来顺序扫描每个元素;对于链表,则通过指针next来依次扫描每个元素

2.代码实战

在这里插入图片描述

//顺序查找
bool search(int *a,int len,int e){
    for(int i=0;i<len;i++){
        if(a[i]==e){
            return true;
        }
    }
    return false;
}

二、二分查找

1.思想

折半查找又称二分查找,它仅适用于有序的顺序表。
折半查找的基本思想: 首先将给定值 key 与表中中间位置的元素比较,若相等,则查找成功,返回该元素的存储位置;若不等,则所需查找的元素只能在中间元素以外的前半部分或后半部分(例如,在查找表升序排列时,若给定值 key
大于中间元素,则所L找刻扮到为止.或确定表中没有所需要查找的兀素,进行同样的查找,如此重复,直到找到为止,或确定表中没有所需要查找的元素,则查找不成功,返回查找失败的信息。
这里需要注意的一点就是,中间的值如果不是我们想要的,我们的midde指针要移向他的前一个或者后一个!
在这里插入图片描述

2.代码实战

//二分查找
bool Bin_Search(int *a,int len,int e){
    int l,r,middle;
    l=0;
    r=len-1;
    while (l<=r){

        middle=(l+r)/2;
        if(a[middle]==e){
            return true;
        } else if(a[middle]>e){
            // 这里之所以这么确定,是因为middle指向的地方不是我们所要的值
            r=middle-1;
        }else{
            l=middle+1;
        }
    }
    return false;
}

三、二叉排序树

1.建树思想

二叉排序树(也称二叉查找树)或者是一棵空树,或者是具有下列特性的二叉树:

  • 1)若左子树非空,则左子树上所有结点的值均小于根结点的值。
  • 2)若右子树非空,则右子树上所有结点的值均大于根结点的值。
  • 3)左、右子树也分别是一棵二叉排序树。
    在这里插入图片描述

在这里插入图片描述
我们在建树的时候可以使用递归方法也可以使用循环方法,将要插入的元素与当前节点比较,大的话向右走,小的话向左走,直到找到合适的位置插入,寻找对应元素依旧如此!

2.删除节点思想

代码实战中有二叉排序树的建立,有删除所找节点两个功能;
建树算法思想是:遍历所要建树节点,每次取出一个节点,与根节点进行比较,如果比根节点小就向根节点左子树移动,否则向右子树,然后以根节点子树做新的根节点,重复上述过程!直到找到合适的位置进行插入;
删除节点思想:遍历二叉搜索树,找到要删除的节点之后,先记录下该节点与该节点前驱,如果该节点有一个子树或者没有子树那么直接将该节点删除,否则:先向左走一步,之后一直向右走,直到走到右子树为空的节点,将该节点的值填充到要删除的节点,然后将该节点删除(本质是找与要删除节点数值相邻最近的点将其位置顶替)。
注:在二叉排序树中删除节点时删除只有一个子树的节点或者没有子树的节点比较好删除,只需将子树向上移动一下即可。
在这里插入图片描述

3.代码实战

BitStruct.h文件内容如下:

//
// Created by 123 on 2023/2/20.
//

#ifndef MYTEST_BITSTRUCT_H
#define MYTEST_BITSTRUCT_H
//基础元素类型
#define  ElementData int
#define ElementDataEnd -1
//二叉树的节点
struct BiTNode{
    ElementData data;
    struct BiTNode *leftNode;
    struct BiTNode *rightNode;
};
typedef struct BiTNode BiTNode;
//辅助队列(通过这个队列层次建树)
struct auxiliary{
    // 指向二叉树中的节点
    struct BiTNode* p;
    struct auxiliary *next;
};
typedef struct auxiliary tagQueue;
#endif //MYTEST_BITSTRUCT_H

真正的业务代码如下:

//
// Created by Zhu Shichong on 2023/1/9.
//
#include <stdio.h>
#include <stdlib.h>
#include "BitStruct.h"
#define bool int
#define true 1
#define false 0
#define MaxSize 10
//初始化二叉排序树----------循环实现方式
BiTNode *Creat_BST(ElementData *a,int len){
    BiTNode *tree=(BiTNode*)malloc(sizeof (BiTNode));
    tree->data=a[0];
    tree->rightNode=tree->leftNode=NULL;
    for(int i=1;i<len;i++){
        BiTNode *p=tree;
        BiTNode *q=(BiTNode*) malloc(sizeof (BiTNode));
        q->data=a[i];
        q->leftNode=q->rightNode=NULL;
        // 用于保留p指针的位置
        BiTNode *prep=NULL;
        while (p!=NULL){
            prep=p;
            if(p->data>q->data){
                p=p->leftNode;
            }else{
                p=p->rightNode;
            }
        }
        // 将值插入找出的位置;
        prep->data > q->data?(prep->leftNode = q):(prep->rightNode = q);
        // 下面这种写法是错误的
        // prep->data > q->data?prep->leftNode = q:prep->rightNode = q;
    }
    return tree;
}
//查找元素--------查找次数为树的深度
bool BST(BiTNode* tree,ElementData e){
    while(tree!=NULL){
        if(tree->data>e){
            tree=tree->leftNode;
        }else if(tree->data<e){
            tree=tree->rightNode;
        }else{
            return true;
        }
    }
    return false;
}
//删除树中的节点
//实现思路:二叉搜索树有特点就是,比当前节点小的最大元素在左子树的最右侧,比当前节点大的最小元素在右子树最左侧
//想要删除叶子节点或者只有一个子树的节点,只需用子树取代原节点的位置,为了简化计算,我们可以先采用相近元素取代要删除
//的节点,然后删除那个只有一个子树或者没有子树的节点。

bool Delete_BST(BiTNode** tree,ElementData e){
    BiTNode *head=NULL;
    BiTNode *temp_tree=*tree;
    // 这样写head,只要是找到了目标e,那么head就作为值为e节点的前驱节点
    while(temp_tree!=NULL){
        // 只有判断temp才会往下走,只有temp往下走,temp才记录他的前驱
        //(如果分支不这样写,那么要删除的是最后一个元素时,没法操作前驱指针)
        if(temp_tree->data>e){
            head=temp_tree;
            temp_tree=temp_tree->leftNode;
        }else if(temp_tree->data<e){
            head=temp_tree;
            temp_tree=temp_tree->rightNode;
        }else{
            break;
        }
    }
    // 如果一路走到头没有找到要删除的节点,那么返回false
    if(NULL==temp_tree){
        return false;
    }
    // 判断要删除的节点什么特征(左右子树是否为空,不为空需要根据二叉搜索树的特征向下找!)
    if(temp_tree->leftNode==NULL){
        if(head->leftNode&&head->leftNode->data==temp_tree->data){
            head->leftNode=temp_tree->rightNode;
        }else{
            head->rightNode=temp_tree->rightNode;
        }
    }else if(temp_tree->rightNode==NULL){
        if(head->leftNode&&head->leftNode->data==temp_tree->data){
            head->leftNode=temp_tree->leftNode;
        }else {
            head->rightNode = temp_tree->leftNode;
        }
    }else{
        BiTNode *p=temp_tree->leftNode;
        BiTNode *prep=temp_tree;
        // 向下找,直到找到左子树最右边的节点(中序遍历可以看出,该节点可以取代要删除节点的位置)
        // 并且所找节点一定是只有一个子树或者没有子树
        // prep为符合条件节点的前驱。
        while(p->rightNode!=NULL){
            prep=p;
            p=p->rightNode;
        }
        // 真正的删除(将找到的节点的值填充到要删除的节点)然后释放找到的节点
        // 也就是找一个替死鬼!!!或者说让符合条件的节点拆迁
        // if分支是一个交接处,需要先向左走一步然后再一直向右走
        // 所以在该点进行特殊处理!
        if(prep->data==temp_tree->data){
            temp_tree->data=p->data;
            prep->leftNode=NULL;
            free(p);
        }else{
            temp_tree->data=p->data;
            if (p->leftNode == NULL) {
                prep->rightNode=NULL;
            } else {
                prep->rightNode=p->leftNode;
            }
            free(p);
        }
    }
    return true;
}
void PrintBST(BiTNode *tree){
    if(NULL==tree){
        return;
    }
    PrintBST(tree->leftNode);
    printf("%d ",tree->data);
    PrintBST(tree->rightNode);
}
int main() {
    BiTNode *tree=NULL;
    ElementData a[MaxSize]={22,1,33,13,45,31,65,221,857,3};
    // 构建二叉排序树
    tree=Creat_BST(a,MaxSize);
    PrintBST(tree);
    printf("\n");
    // 利用二叉排序树查找元素
    printf("%d \n",BST(tree,888));
    printf("%d \n", BST(tree,857));
    // 删除二叉排序树中的元素--------测试1
    Delete_BST(&tree,33);
    PrintBST(tree);
    printf("\n");
    // 删除二叉排序树中的元素--------测试2
    Delete_BST(&tree,1);
    PrintBST(tree);
    printf("\n");
    // 删除二叉排序树中的元素--------测试3
    Delete_BST(&tree,857);
    PrintBST(tree);
    printf("\n");
    return 0;
}

考研真题实战

先看看题目:
在这里插入图片描述
这个题目所考察的内容是二分查找,但是有两个数组,是双数组的二分查找,是一道非常经典的题目。因为空间尽可能高效,因此我们不能够去再搞一个大数组,把两个数组合并到一起,这样得分会非常低。
(1)算法的基本设计思想如下:

分别求出序列A和B的中位数,设为a 和 b,求序列A和 B
的中位数过程如下:

  • 1)若a=b,则a 或b 即为所求中位数,算法结束。
    1. 若a<b,则舍弃序列A中较小的一半,同时舍弃序列B中较大的一半,要求舍弃的长度相等;
    1. 若 a>b,则舍弃序列A中较大的一半,同时舍弃序列B中较小的一半,要求舍弃的长度相等;
      在保留的两个升序序列中,重复过程1)、2)、3),直到两个序列中均只含一个元素时为止,较小者即为所求的中位数。

代码实战如下:
抓住问题的本质,分奇偶数讨论,分析数值与位次之间的关系!

//
// Created by Zhu Shichong on 2023/1/9.
//
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 7
#define bool int
#define true 1
#define false 0
//查找两个升序列表合并后的中位数
//求解中位数的时候,如果有偶数个元素那么中位数处在
bool Bin_Search(int *a,int *b,int len){
    int ans=-1;
    int al,ar,bl,br,am,bm;
    al=bl=0;
    ar=br=len-1;
    while(al!=ar){
        am=(al+ar)/2;
        bm=(bl+br)/2;
        if((al+ar)%2==0){
            if(a[am]>b[bm]){
                ar=am;
                bl=bm;
            }else if(a[am]<b[bm]){
                br=bm;
                al=am;
            }else{
                return a[am];
            }
        }else{
            if(a[am]>b[bm]){
                ar=am;
                bl=bm+1;
            }else if(a[am]<b[bm]){
                br=bm;
                al=am+1;
            }else {
                return a[am];
            }
        }
    }
    ans=a[al]>b[bl]?b[bl]:a[al];
    return ans;
}
int main() {
    //    22,1,33,13,45,31,65,221,857,3
    int a[MaxSize]={0,1,2,3,4,4,9};
    int b[MaxSize]={0,5,5,6,7,8,10};
    printf("result: %d",Bin_Search(a,b,MaxSize));
    return 0;
}

测试数据如下:

int a[MaxSize]={0,1,2,3,4,4,9};
int b[MaxSize]={0,5,5,6,7,8,10};

结果如下:

result: 4
进程已结束,退出代码为 0

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/684748.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java异常面试题

什么是Java异常机制 Java异常机制是java语言为我们提供一种异常处理机制&#xff0c;在java语言中&#xff0c;异常本身是一个类&#xff0c;产生异常就是创建异常对象并抛出这个异常对象&#xff0c;程序发生异常情况之后程序会抛出封装了错误信息的异常对象&#xff0c;程序…

python-线性规划

线性规划&#xff1a;定义&#xff1a;1 线性规划&#xff08;Linear programming,简称LP&#xff09;&#xff0c;是运筹学中研究较早、发展较快、应用广泛、方法较成熟的一个重要分支&#xff0c;是辅助人们进行科学管理的一种数学方法&#xff0c;是研究线性约束条件下线性…

WPF 零基础入门笔记(2):控件模板+数据模版

文章目录 文章合集地址WPF控价模版解决重复嵌套标签书写的问题实战 WPF数据绑定解决界面和业务数据沟通的问题 WPF数据模版数据模板解决数据的样式设置&#xff08;以CellTemplate为例&#xff09;数据模板和控件模板的区别ItemTemplate 元素模板ItemTemplate是用于绝大部分控件…

CAT1模块 EC800M HTTP使用总结记录

分享记录一下 CAT1 模块EC800 HTTP 协议使用流程 ...... by 矜辰所致目录 前言一、基础说明1.1 CAT1 与 4G1.2 EC800M 模块1.3 HTTP 二、开始使用2.1 硬件设计部分2.2 模块上电流程2.3 PDP 上下文2.3.1 什么是 SGSN 和 GGSN &#xff1f; 三、 HTTP 流程3.1 客户端3.1.1 PDP 上…

UFS 13 - Logical Unit Management

UFS 13 - Logical Unit Management 1 Introduction2 Logical Unit features3 Logical Unit Configuration 基于UFS 3.1 标准文档阐述 UFS 1-UFS架构简介1 UFS 2 -UFS架构简介2 UFS 3 - UFS RPMB UFS 4 - UFS Boot UFS 5 - UFS UIC Layer: MIPI M-PHY UFS 6 - UAP – SCSI Comma…

机器人工程创新类课程补充说明-2023-2

仅供参考 之前一篇&#xff1a;机器人工程创新类课程补充说明-2023- 自主学习→自主研究→自主创新&#xff0c;这是一个循序渐进的过程&#xff0c;着急没用的&#xff0c;大部分学生&#xff0c;都卡在第一个阶段&#xff0c;自主学习的效率比较低&#xff0c;并且成果不突出…

【C++ 程序设计】第 6 章:多态与虚函数

目录 一、多态的基本概念 &#xff08;1&#xff09;多态 &#xff08;2&#xff09;虚函数 &#xff08;3&#xff09;通过基类指针实现多态 &#xff08;4&#xff09;通过基类引用实现多态 &#xff08;5&#xff09;* 多态的实现原理 二、多态实例 三、多态的使用 …

docker启动的jenkins拉取gitee项目构建并部署

docker启动的jenkins拉取gitee项目构建并部署 docker 拉取jenkins https://hub.docker.com/r可以search不同的jenkins版本docker pull jenkins/jenkins创建挂载目录 /home/jenkins_home加权限chmod 777 /home/jenkins_homedocker run -di --namejenkins -p 9999:8080 -v /hom…

【json-server】centos线上环境搭建全攻略

▒ 目录 ▒ &#x1f6eb; 问题描述环境 1️⃣ 安装json-server安装nvm安装node安装json-server 2️⃣ json-server 使用创建json数据踩坑&#xff1a;指定host关闭防火墙云后台修改安全组规则最终命令行 &#x1f6ec; 结论&#x1f4d6; 参考资料 &#x1f6eb; 问题 描述 开…

【虚拟机搭建-VMware设置固定IP】VMWare中CentOS如何设置固定IP【不成功手把手教学】

背景 在日常工作学习中&#xff08;比如博主在之前学习k8s过程中&#xff0c;windows本地搭建虚拟机&#xff0c;重启windows后&#xff09;虚拟机的IP会发生变化&#xff0c;所以该篇文章详细记录VMWare中CentOS如何设置固定IP 虚拟机安装 参考&#xff1a; https://rundr…

尚硅谷Docker2022版学习笔记(基础篇 上)

目录 一、Docker简介 1.1、是什么&#xff1f; 问题&#xff1a;为什么会有Docker出现&#xff1f; Docker理念 一句话 1.2、容器与虚拟机比较 传统虚拟机技术 容器虚拟机技术 容器虚拟机技术 1.3、去哪下&#xff1f; 二、Docker安装 2.1、前提说明 2.2、Docker的…

Rust语言从入门到入坑——(8)Rust泛型与特性

文章目录 0、引入1、泛型1.1、在函数中使用1.2、在结构体中使用 2、特性2.1 默认特性2.2 特性做参数2.3 特性做返回值2.4 有条件实现方法 3、总结 0、引入 泛型是一个编程语言不可或缺的机制。 C 语言中用"模板"来实现泛型&#xff0c;而 C 语言中没有泛型的机制&am…

MySQL数据库——备份与恢复

MySQL数据库——备份与恢复 一、数据备份的重要性二 、数据库备份的分类和备份策略1 数据库备份的分类2 数据库的备份策略3常见的备份方法 三、MySQL完全备份1 什么是完全备份2 完全备份的优缺点3 完全备份的方法 四、完全备份的操作1.物理冷备份与恢复2.mysqldump 备份与恢复3…

Arduino IDE 2.1.0 下安装ESP32

很久没玩Arduino了。新装了一台电脑&#xff0c;想重新捡起ESP32-WROOM-32开发板玩玩。官网上下了Arduino IDE 2.1.0&#xff0c;然后配置ESP32开发环境。 下载及安装Arduino IDE略过不谈。装完后还是老规矩&#xff0c;文件 -> 首选项&#xff0c;先把自定义板管理地址填入…

如何与西门子200 PLC建立连接

更多关于西门子S7-200PLC内容请查看&#xff1a;西门子200系列PLC学习课程大纲 与西门子200 PLC建立连接很简单&#xff0c;分为以下几步&#xff1a; 1.给S7-200PLC通电 按下图1-1所示连接PLC端口&#xff0c;然后给PLC供电&#xff0c;有直流24V和交流220V两种供电方式&am…

力扣----环形链表

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 金句分享:…

9i物联网浏览器(cef_114.2.110114.2.100支持H264视频)WinForm-CefSharp114(5735)视频版本体验

更新:2023.6.25 版本:Cef_114.2.110和114.2.100+chromium-114.0.5735.134的32位和64位 说明:支持图片,mp3,mp4(H264)多媒体 测试环境:windows server 2019 测试网址:www.html5test.com 1.包下载地址 1.1 https://www.nuget.org/packages/CefSharp.Common/ 1.2 https…

SpringCloud Alibaba学习笔记

SpringCloud Alibaba学习笔记 1、项目搭建 版本依赖关系&#xff1a;https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E 1.1、搭建父工程 spring-initializr构建项目 清理父工程中不必要的文件/文件夹 修改pom.xml文件 <…

Linux进程信号【信号产生】

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; Linux学习之旅 &#x1f383;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 文章目录 &#x1f307;前言&#x1f3d9;️正文1、进程信号基本概念1.1、什么是信号&#xff1f;1.2、信号的作用1.3、信号的…

十四、go语言的指针

一、指针 1.1 指针的概念 指针是存储另一个变量的内存地址的变量。 我们都知道&#xff0c;变量是一种使用方便的占位符&#xff0c;用于引用计算机内存地址。 一个指针变量可以指向任何一个值的内存地址它指向那个值的内存地址。 在上面的图中&#xff0c;变量b的值为156&…