关于我、重生到500年前凭借C语言改变世界科技vlog.14——常见C语言算法

news2024/11/26 20:22:41

文章目录

  • 1.冒泡排序
  • 2.二分查找
  • 3.转移表
  • 希望读者们多多三连支持
  • 小编会继续更新
  • 你们的鼓励就是我前进的动力!

根据当前所学C语言知识,对前面知识进行及时的总结巩固,出了这么一篇 vlog 介绍当前所学知识能遇到的常见算法,这些算法是在C数据结构初阶常用的一些算法,重要性不言而喻,本章将用简单易懂的语言带领读者深入理解

1.冒泡排序

冒泡排序(Bubble Sort)是一种简单的排序算法,它重复地走访要排序的数列,一次比较两个数据元素,如果顺序不对则进行交换,并一直重复这样的走访操作,直到没有要交换的数据元素为止

核心思想:两两元素进行比较交换

基本原理
1.比较相邻的元素,如果第一个比第二个大(假设是按照升序排序,若为降序则相反),就交换它们两个
2.对每一对相邻元素作同样的操作,从开始第一对到结尾的最后一对,这样在经过第一轮比较后,最大的元素就会 “浮” 到数列的末尾
3.针对所有的元素重复以上的步骤,除了最后已经排好序的元素(因为每一轮都会把当前未排序部分的最大元素移到最后,所以每轮结束后末尾的元素数量会增加,这些元素就不需要再参与后续排序了)
4.持续重复上述过程,直到整个数列都按照要求的顺序排列好

理论知识介绍完,举个例子或许你就完全明白了
假设我们有一个数组 [5, 4, 3, 2, 1] 要进行升序排序:

第一轮排序
1.比较第 1 个元素 5 和第 2 个元素 4,因为 5 > 4,所以交换它们,数组变为 [4, 5, 3, 2, 1]
2.接着比较第 2 个元素 5 和第 3 个元素 3,因为 5 > 3,交换后数组变为 [4, 3, 5, 2, 1]
3.再比较第 3 个元素 5 和第 4 个元素 2,交换得到 [4, 3, 2, 5, 1]
4.最后比较第 4 个元素 5 和第 5 个元素 1,交换后数组变为 [4, 3, 2, 1, 5]。此时第一轮排序结束,最大的元素 5 已经 “浮” 到了数组的末尾

第二轮排序
1.对除了最后一个元素 5 之外的数组部分 [4, 3, 2, 1] 进行同样操作
2.先比较第 1 个元素 4 和第 2 个元素 3,交换得 [3, 4, 2, 1]
3.再比较第 4 个元素 4 和第 3 个元素 2,交换得 [3, 2, 4, 1]
4.最后比较第 3 个元素 4 和第 4 个元素 1,交换得 [3, 2, 1, 4]。第二轮排序结束,此时未排序部分的最大元素 4 也 “浮” 到了合适位置

…以此类推

可以发现每次都将最大的那个数送到最右边,假设有 n 个数,那么需要运送的趟数就为 (n-1)
每一轮,每两个数都要进行比较,已经被运送到最右边的就不需要比较
那么需要比较 (n-1-已经运送到右边的数的个数)

所以交换的函数可以这么写

void bubble_sort(int arr[], int sz)//参数接收数组元素个数
{
      int i = 0;
      for(i=0; i<sz-1; i++)
      {
           int j = 0;
           for(j=0; j<sz-i-1; j++)
           {
                if(arr[j] > arr[j+1])
                {
                     int tmp = arr[j];
                     arr[j] = arr[j+1];
                     arr[j+1] = tmp;
                }
           }
       }
 }

int main()
{
       int arr[] = {3,1,7,5,8,9,0,2,4,6};
       int sz = sizeof(arr)/sizeof(arr[0]);
       bubble_sort(arr, sz);
       int i = 0;
       for(i=0; i<sz; i++)
       {
              printf("%d ", arr[i]);
       }
       return 0;
}

这里简单说明一下时间复杂度的概念,这里只需要理解概念即可,如何计算到了C数据结构会讲解

1.时间复杂度是用来衡量算法运行时间随着输入规模增长而增长的趋势的一个重要指标
2. 时间复杂度通常用大 O 表示法来表示,大 O 表示法描述的是算法在最坏情况下的时间复杂度,也就是当输入对算法运行造成最大困难时的运行时间增长趋势

对于该冒泡排序
最坏情况:当输入的数组是完全逆序时,第一轮需要比较 n-1 次,第二轮需要比较 n-2 次,以此类推,总共需要比较的次数为 (n-1)+(n-2)+…+1,这个和等于 n (n-1)/2,所以需要进行 n(n-1)/2 次比较和交换操作,所以时间复杂度为 O(n^2),其中 n 是数组的元素个数

最好情况:当输入的数组已经是有序的,只需要进行一轮比较(每个元素都和它相邻的元素比较一次),此时时间复杂度为 O(n)

假设有多个序列需要排列,所以为了提高效率,对最好情况进行快速排序,对代码可进行如下优化

void bubble_sort(int arr[], int sz)//参数接收数组元素个数
{
      int i = 0;
      for(i=0; i<sz-1; i++)
      {
           int flag = 1;//假设这⼀趟已经有序了
           int j = 0;
           for(j=0; j<sz-i-1; j++)
           {
                if(arr[j] > arr[j+1])
                {
                     flag = 0;//发⽣交换就说明,⽆序
                     int tmp = arr[j];
                     arr[j] = arr[j+1];
                     arr[j+1] = tmp;
                }
           }
           if(falg == 1)//这⼀趟没交换就说明已经有序,后续⽆序排序了
                break;
       }
 }

int main()
{
       int arr[] = {3,1,7,5,8,9,0,2,4,6};
       int sz = sizeof(arr)/sizeof(arr[0]);
       bubble_sort(arr, sz);
       int i = 0;
       for(i=0; i<sz; i++)
       {
              printf("%d ", arr[i]);
       }
       return 0;
}

冒泡排序虽然简单易懂,但由于其时间复杂度较高,在处理大规模数据排序时效率相对较低,通常会被更高效的排序算法如快速排序、归并排序等所替代,但在一些数据量较小且对排序效率要求不是特别高的场景下,仍然可以使用冒泡排序

2.二分查找

二分查找(Binary Search),也叫折半查找,是一种用于在有序数组(或其他有序数据结构)中快速查找特定元素的高效查找算法

核心思想:不断对半查找

基本原理
1.每次查找时都将待查找的区间分成两部分
2.然后根据要查找元素与中间元素的比较结果,确定下一次查找应该在左半部分还是右半部分继续进行
3.如此反复,直到找到目标元素或者确定目标元素不存在为止

还是通过举例来说明,假设我们有一个有序数组 [1, 3, 5, 7, 9, 11, 13, 15],要查找元素 7

首先,确定整个数组为待查找区间,计算中间元素的索引,对于长度为 n 的数组,中间元素索引 mid 计算公式为:mid = (left + right) / 2(这里 left 表示区间的左端点,right 表示区间的右端点,在初始时 left = 0,right = n - 1)。在这个例子中,n = 8,所以 mid = (0 + 7) / 2 = 3,中间元素就是 7

然后,将目标元素 7 与中间元素 7 进行比较,发现它们相等,这就找到了目标元素,查找过程结束

再假设要查找元素 4

1.同样先确定整个数组为待查找区间,计算中间元素索引 mid = (0 + 7) / 2 = 3,中间元素是 7
2.将目标元素 4 与中间元素 7 进行比较,因为 4 < 7,所以目标元素如果存在,一定在左半部分。此时更新待查找区间为左半部分,即 left = 0,right = mid - 1 = 2
3.再次计算新的中间元素索引 mid = (0 + 2) / 2 = 1,新的中间元素是 3
将目标元素 4 与新的中间元素 3 进行比较,因为 4 > 3,所以目标元素如果存在,一定在右半部分,更新待查找区间为右半部分,即 left = mid + 1 = 2,right = 2
4.第三次计算中间元素索引 mid = (2 + 2) / 2 = 2,中间元素是 5
5.将目标元素 4 与中间元素 5 进行比较,因为 4 < 5,所以目标元素如果存在,一定在左半部分,更新待查找区间为左半部分,即 left = 2,right = mid - 1 = 1,此时 left > right,说明目标元素在这个有序数组中不存在,查找过程结束

可以发现每次折中查找,然后和目标元素比较,以此往复完成二分查找

对于二分查找
二分查找每次查找都会将搜索范围缩小一半,最多需要查找的次数为以 2 为底,n(数组元素个数)的对数次
log 2^n ,所以二分查找的时间复杂度为 O(log n),这使得它在处理较大规模的有序数组时,查找速度比顺序查找(时间复杂度为 O(n))快得多

例如,当数组有 1024 个元素时,二分查找最多只需要查找 10 次(因为 )就可以确定目标元素是否存在
如果是顺序查找就要找 1024 次,往往查找的数目还不止这么多,甚至更加庞大


int binary_search(int arr[], int n, int target)
{
    int left = 0;
    int right = n - 1;

    while (left <= right)
     {
        // 计算中间元素的索引
        int mid = left + (right - left) / 2;
        if (arr[mid] == target) 
        {
            return mid;
        } else if (arr[mid] > target) 
        {
            right = mid - 1;
        } else
         {
            left = mid + 1;
        }
    }
    return -1;
}

int main()
{
    int arr[] = {1, 3, 5, 7, 9, 11, 13, 15};
    int n = sizeof(arr) / sizeof(arr[0]);
    int target = 7;
    int result = binary_search(arr, n, target);
    if (result!= -1) 
    {
        printf("目标元素 %d 在数组中的索引为 %d\n", target, result);
    } 
    else 
    {
        printf("目标元素 %d 在数组中不存在\n", target);
    }
    return 0;
}

二分查找是一种非常高效的查找算法,但它的前提是数组必须是有序的。如果数组未排序,则需要先对数组进行排序,再使用二分查找

3.转移表

转移表是一种数据结构和编程技巧,用于实现根据不同的条件或输入值快速跳转到相应的代码段执行

例如:写一个简单的计算器程序,它可以执行加、减、乘、除四种运算。你可以创建一个转移表,表中包含四个元素,分别对应四种运算的处理函数的指针,当用户输入一个运算符号后,程序可以根据这个符号在转移表中快速找到对应的处理函数并执行,而不需要使用一长串的 if-else 或 switch 语句来逐个判断运算符号并调用相应函数

根据前面所学的 switch 结构和 加法函数,可以写出一个简易的四则运算计算器

int add(int a, int b)
{
     return a + b;
}
int sub(int a, int b)
{
     return a - b;
}
int mul(int a, int b)
{
     return a * b;
}
int div(int a, int b)
{
     return a / b;
}

int main()
{
     int x, y;
     int input = 1;
     int ret = 0;
     do
     {
         printf("*************************\n");
         printf("    1:add          2:sub \n");
         printf("    3:mul          4:div \n");
         printf("    0:exit               \n");
         printf("*************************\n");
         printf("请选择:");
         scanf("%d", &input);
         switch (input)
         {
         case 1:
              printf("输⼊操作数:");
              scanf("%d %d", &x, &y);
              ret = add(x, y);
              printf("ret = %d\n", ret);
              break;
         case 2:
              printf("输⼊操作数:");
              scanf("%d %d", &x, &y);
              ret = sub(x, y);
              printf("ret = %d\n", ret);
              break;
         case 3:
              printf("输⼊操作数:");
              scanf("%d %d", &x, &y);
              ret = mul(x, y);
              printf("ret = %d\n", ret);
              break;
         case 4:
              printf("输⼊操作数:");
              scanf("%d %d", &x, &y);
              ret = div(x, y);
              printf("ret = %d\n", ret);
              break;
         case 0:
              printf("退出程序\n");
              break;
        default:
              printf("选择错误\n");
              break;
         }
    } while (input);
    return 0;
}

但是这种写法过于重复啰嗦,可以运用函数数组指针来简化代码

int add(int a, int b)
{
     return a + b;
}
int sub(int a, int b)
{
     return a - b;
}
int mul(int a, int b)
{
     return a * b;
}
int div(int a, int b)
{
     return a / b;
}

int main()
{
     int x, y;
     int input = 1;
     int ret = 0;
     int(*p[5])(int x, int y) = {0, add, sub, mul, div };//转移表
      do
     {
         printf("*************************\n");
         printf("    1:add          2:sub \n");
         printf("    3:mul          4:div \n");
         printf("    0:exit               \n");
         printf("*************************\n");
         printf("请选择:");
         scanf("%d", &input);
         if ((input <= 4 && input >= 1))
         {
               printf( "输⼊操作数:" );
               scanf( "%d %d", &x, &y);
               ret = (*p[input])(x, y);
               printf( "ret = %d\n", ret);
         }
         else if(input == 0)
         {
               printf("退出计算器\n");
         }
         else
         {
               printf( "输⼊有误\n" ); 
         }
      }while (input);
      return 0;
}

这样就通过函数指针数组实现了根据不同的索引值灵活调用不同函数的功能,这在很多需要根据条件或情况动态选择执行不同函数的场景中非常有用

如果还有不懂可以私信博主或回顾往期 vlog 查缺补漏

主页传送门:DARLING Zero two♡ 的 blog

希望读者们多多三连支持

小编会继续更新

你们的鼓励就是我前进的动力!

在这里插入图片描述

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

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

相关文章

特定领域的Embeddings模型微调全面指南

假设你正在为医学领域构建一个问答系统。你希望确保当用户提出问题时&#xff0c;系统能够准确地检索相关的医学文章。但是通用的嵌入模型可能在处理医学术语的高度专业化词汇和细微差别时会遇到困难。 这时候&#xff0c;微调就能派上用场了&#xff01;&#xff01;&#xf…

视频推荐的算法(字节青训)

题目&#xff1a; 西瓜视频 正在开发一个新功能&#xff0c;旨在将访问量达到80百分位数以上的视频展示在首页的推荐列表中。实现一个程序&#xff0c;计算给定数据中的80百分位数。 例如&#xff1a;假设有一个包含从1到100的整数数组&#xff0c;80百分位数的值为80&#…

安卓13默认连接wifi热点 android13默认连接wifi

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.编译6.彩蛋1.前言 有时候我们需要让固件里面内置好,相关的wifi的ssid和密码,让固件起来就可以连接wifi,不用在手动操作。 2.问题分析 这个功能,使用普通的安卓代码就可以实现了。 3.代…

详解RabbitMQ三种队列类型

RabbitMQ 是一个强大的消息队列系统&#xff0c;它提供了多种队列类型以满足不同的使用需求。本文将探讨三种主要队列类型&#xff1a;经典队列、仲裁队列和流式队列&#xff0c;并讨论它们的区别和选型建议。 经典队列&#xff08;Classic Queues&#xff09; 简介&#xff…

CytoSPACE·空转和单细胞数据的高分辨率比对

1. 准备输入文件&#xff0c;需要四个文件&#xff0c;所有文件都应以制表符分隔的表格输入格式 (.txt) 提供。 a. scRNA-seq 基因表达文件 矩阵必须是基因&#xff08;行&#xff09;乘以细胞&#xff08;列&#xff09;。 第一行必须包含单个细胞 ID&#xff0c;第一列必须…

react使用Fullcalendar

前言&#xff1a; 最近在做项目时&#xff0c;遇到了需要用日历的项目。一开始考虑使用antd的日历组件。后来 调研技术库&#xff0c;发现了fullcalendar 库。经过对比 fullcalendar 更强大&#xff0c;更灵活。 其实 antd的日历组件 也不错&#xff0c;简单的需求用他也行。…

LabVIEW过程控制实验平台

A3000实验平台通过LabVIEW开发&#xff0c;实现了过程控制的虚拟仿真与实时通信&#xff0c;显著提高了教学与实验的互动性和效率。该平台采用模块化设计&#xff0c;支持多种控制策略的实验教学&#xff0c;克服了传统实验设备的不足。项目背景 目前高校过程控制实验设备普遍…

腾讯会议pc端3.29.11开启悬浮窗口

之前是&#xff1a;pc端每次最小化&#xff0c;它就自动收回到任务栏里了 版本&#xff1a;3.29.11 解决办法&#xff1a; 打开腾讯会议&#xff0c;点击左上角的【头像】。 单击【设置】。 选择【显示当前说话者】来管理麦克风浮窗。 再进入会议&#xff0c;点击最小化一哈&…

聊一聊:ChatGPT搜索引擎会取代谷歌和百度吗?

当地时间 10 月 31 日&#xff0c;OpenAI 正式推出了 ChatGPT 搜索功能&#xff0c;能实时、快速获取附带相关网页来源链接的答案。这一重大升级标志着其正式向谷歌的搜索引擎霸主地位发起挑战。 本周五我们聊一聊&#xff1a; 欢迎在评论区畅所欲言&#xff0c;分享你的观点~ …

贪心算法习题其四【力扣】【算法学习day.21】

前言 ###我做这类文档一个重要的目的还是给正在学习的大家提供方向&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非常非常高滴&am…

python实战项目52:Selenium爬取steam黑神话悟空评论

python实战项目52:Selenium爬取steam黑神话悟空评论 一、思路分析二、完整代码一、思路分析 Selenium爬取steam游戏评论的思路非常简单,初始化Chromedriver,然后打开评论页面,循环下拉滚动条,每下拉一次滚动条获取一次页面源代码,使用xpath解析数据并保存数据。本文的主…

Claude 3.5 新功能 支持对 100 页的PDF 图像、图表和图形进行可视化分析

Claude 3.5 Sonnet发布PDF图像预览新功能&#xff0c;允许用户分析长度不超过100页的PDF中的视觉内容。 此功能使用户能够轻松上传文档并提取信息&#xff0c;特别适用于包含图表、图形和其他视觉元素的研究论文和技术文档。 视觉PDF分析&#xff1a;用户现在可以从包含各种视觉…

【Qt c++】Qt内置图标

Qt内置图标 前言简例示例 前言 Qt内置图标封装在QStyle中&#xff0c;大概七十多个图标&#xff0c;可以直接拿来用。图标的大小&#xff1a;我认为 size 30 还是可以的. 简例 SP_TitleBarMenuButton, SP_TitleBarMinButton, SP_TitleBarMaxButton, SP_TitleBarCloseButton…

Redis 的使⽤和原理

第一章:初识 Redis 1.1盛赞 Redis Redis 是⼀种基于键值对&#xff08;key-value&#xff09;的 NoSQL 数据库&#xff0c;与很多键值对数据库不同的是&#xff0c;Redis 中的值可以是由 string&#xff08;字符串&#xff09;、hash&#xff08;哈希&#xff09;、list&…

【Clikhouse 探秘】ClickHouse 物化视图:加速大数据分析的新利器

&#x1f449;博主介绍&#xff1a; 博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家&#xff0c;WEB架构师&#xff0c;阿里云专家博主&#xff0c;华为云云享专家&#xff0c;51CTO 专家博主 ⛪️ 个人社区&#x…

atest v0.0.18 提供了强大、灵活的 HTTP API Mock 功能

atest 发布 v0.0.18 atest 是致力于帮助开发者持续保持高质量 API 的开源接口工具。 你可以在命令行终端或者容器中启动&#xff1a; docker run -p 8080:8080 ghcr.io/linuxsuren/api-testing:v0.0.18 亮点 在开源之夏 2024 中 atest 增加了基于 MySQL 的测试用例历史的支持HT…

深度了解flink(十) JobManager(4) ResourceManager HA

ResourceManager&#xff08;ZK模式&#xff09;的高可用启动流程 ResourceManager启动流程在DefaultDispatcherResourceManagerComponentFactory#create中 public DispatcherResourceManagerComponent create(Configuration configuration,ResourceID resourceId,Executor i…

Linux系统编程——信号的基本概念(信号产生于处理、可靠信号、可重入函数、SIGCHLD)

一、什么是信号 1、信号的定义 信号是UNIX和Linux系统响应某些条件而产生的一个事件&#xff0c;接收到该信号的进程会相应地采取一些行动。信号是软中断&#xff0c;通常信号是由一个错误产生的。但它们还可以作为进程间通信或修改行为的一种方式&#xff0c;明确地由一个进程…

节省50%人工录入时间!免费开源AI工具让法律文件数据提取更高效

法律行业痛点&#xff1a;处理大量的合同、诉讼材料和财务报告等文件是一项繁琐且耗时的工作。这些文件中的表格常包含关键信息&#xff0c;如费用清单、时效统计和条款列表等&#xff0c;手动录入和整理这些数据不仅效率低下&#xff0c;而且容易出错。表格识别技术&#xff0…

单智能体carla强化学习实战工程介绍

有三个工程&#xff1a; Ray_Carla: 因为有的论文用多进程训练强化学习&#xff0c;包括ray分布式框架等&#xff0c;这里直接放了一个ray框架的示例代码&#xff0c;是用sac搭建的&#xff0c;obs没用图像&#xff0c;是数值状态向量值&#xff08;速度那些&#xff09;。 …