数据结构(七)递归、快速排序

文章目录

  • 一、递归
    • (一)使用递归实现1~n求和
      • 1. 代码实现:
      • 2. 调用过程:
      • 3. 输出结果:
    • (二)青蛙跳台阶问题
      • 1. 问题分析
      • 2. 代码实现
      • 3. 输出结果
      • 4. 代码效率优化
      • 5. 优化后的输出结果
  • 二、快速排序
    • (一)概念
    • (二)基本思想
    • (三)排序流程
    • (三)代码实现

一、递归

递归就是在函数内部继续调用自身的逻辑。

注:

  1. 递归的本质就是函数调用,他并不是C语言程序结构的一种。
    C语言的程序结构只有三种:循序结构、分支(选择)结构、循环结构
  2. 每个递归必须有一个出口,否则函数无限次调用下去,会把内存资源耗尽,导致堆栈溢出,出现段错误

(一)使用递归实现1~n求和

1. 代码实现:

#include <stdio.h>//计算 1~n 的求和结果 并返回
int my_sum(int n){if(1 == n) return 1;return my_sum(n-1) + n;
}int main(int argc, char const *argv[])
{int ret = my_sum(100);printf("sum = %d\n", ret);return 0;
}

2. 调用过程:

以n=5为例,
首先在main函数中调用my_sum(5)
my_sum(5)的返回值是my_sum(4)+n,
因此会继续调用my_sum(4),my_sum(4)的返回值是my_sum(3)
继续调用my_sum(3),my_sum(3)的返回值是my_sum(2)
继续调用my_sum(2),my_sum(2)的返回值是my_sum(1)
当n==1时,即是递归函数的出口,返回1给my_sum(2),
my_sum(2)的返回值即是2+1=3,返回3给my_sum(3),
my_sum(3)的返回值是3+3=6,返回6给my_sum(4),
my_sum(4)的返回值就是6+4=10,返回10给my_sum(5),
最后得到my_sum(5)的结果就是10+5=15,返回15给main函数。

3. 输出结果:

在这里插入图片描述

(二)青蛙跳台阶问题

共有10阶台阶,青蛙每次只能跳1阶或者2阶
问:青蛙跳上这10阶台阶,共有多少种跳法?

1. 问题分析

跳上10阶台阶的方法等于从第8阶台阶跳上10阶和从第9阶台阶跳上10阶的方法之和,跳上9阶台阶的方法等于从第8阶台阶和从第7阶台阶跳上9阶的方法之和,然后依次类推…
当计算跳上1阶台阶的方法时,就是1种,跳上2阶台阶的方法就是2种,这时就是递归函数的出口

2. 代码实现

#include <stdio.h>//定义一个全局变量用于记录函数调用次数
int count = 0;
//功能:计算跳上n阶台阶的跳法之和 返回计算的结果
int func(int n){count++;//递归的出口if(1 == n) return 1;if(2 == n) return 2;return func(n-1) + func(n-2);
}int main(int argc, const char *argv[])
{int ret = 0;ret = func(10);printf("ret = %d\n", ret);printf("count=%d\n",count);return 0;
}

3. 输出结果

在这里插入图片描述

4. 代码效率优化

如果只是用递归,会有大量重复运算,效率会比较低
可以以空间换时间,保存已经计算过的值来提高效率

#include <stdio.h>#define NUM_JUMP 10 //台阶数量
//定义一个全局变量用于记录函数调用次数
int count = 0;
//定义一个数组用于保存调用函数的值,保存跳上每阶台阶的方法
int sum[NUM_JUMP]={0};//数组的值全部初始化为0int my_jump(int n){count++;if(n==1) return 1;if(n==2) return 2;if(sum[n]) return sum[n]; //当sum[n]不为空时,说明已经计算过该值,可以直接返回//如果sum[n]==0时,说明该值没计算过,计算该值并保存到sum数组中sum[n]=my_jump(n-1)+my_jump(n-2);return sum[n];
}int main(int argc, char const *argv[])
{int ret = my_jump(10);printf("jump = %d\n", ret);//89printf("count = %d\n",count);return 0;
}

5. 优化后的输出结果

在这里插入图片描述
可见优化后的函数调用次数远远小于优化前的函数调用次数,牺牲部分空间换来效率的提高

二、快速排序

(一)概念

快速排序相当于对冒泡排序的一个优化,也是基于交换的排序算法
时间复杂度 O(nlogn)

(二)基本思想

通过一趟排序,将待排序数据分成两部分,其中一部分数据都比另一部分小,而这两部分数据的内部不要求有序;
然后再对这两部分数据做同样的操作,也各自分成一大一小两部分,依次类推,直到整个数据组有序

(三)排序流程

定义两个下标变量,分别指向待排序的数组段的头和尾
以下图为例:
在这里插入图片描述
定义一个flag变量记录左侧第一个元素flag=arr[low](为了编程方便,也可以记录右侧第一个元素),然后从右向左开始依次比较flag和数组元素的大小
注:此处不要写成flag=arr[0]; 因为之后要对左右半边分别递归使用该函数,当对右半边排序时,应该使flag等于右半边的最左侧的数
如果flag<arr[high],就执行high- -,即下标high向左移动
在这里插入图片描述
如果flag>arr[high],就执行arr[low]=arr[high],low++,但是前提条件是low<high
在这里插入图片描述
此时再从左往右依次比较flag和arr[low]的大小
如果flag>arr[low],就执行low++
在这里插入图片描述
如果flag<arr[low],就执行arr[high]=arr[low],high- -,但是前提条件是low<high
在这里插入图片描述

循环上述两个步骤直到low==high时,将arr[low]=flag或者arr[high]=flag
此时flag左边都是比flag小的数,flag右边都是比flag大的数,但是两侧无序
在这里插入图片描述
之后再对左半边和右半边分别排序
此处仅以右半边为例,左半边同理,只是low和high的值不同。
右半边:low=5,high=9(左半边:low=0,high=3)
在这里插入图片描述
首先使flag=arr[low],此处为7
然后判断flag<arr[high]? 如果小于,就执行high- -
在这里插入图片描述

如果大于就执行arr[low]=arr[high],low++
在这里插入图片描述
之后判断flag>arr[low]? 如果大于,就执行low++
如果小于就执行arr[high]=arr[low],high- -
在这里插入图片描述
直到low==high时,arr[low]=flag或者arr[high]=flag
在这里插入图片描述
左半边同理,此处不再赘述。

(三)代码实现

#include <stdio.h>int print(int *arr,int len){if(NULL==arr) return -1;for(int i=0;i<len;i++){printf("%d ",arr[i]);}putchar(10);return 0;
}int my_sort(int *arr,int low,int high){if(NULL==arr) return -1;int flag=arr[low];while(low < high){while(flag<arr[high]&&low<high){high--;}if(low<high){arr[low]=arr[high];low++;}while(flag>arr[low]&&low<high){low++;}if(low<high){arr[high]=arr[low];high--;}}arr[low]=flag;return low;
}int quick_sort(int *arr,int low,int high){if(NULL==arr) return -1;if(low < high){int ret=my_sort(arr,low,high);quick_sort(arr,low,ret-1);quick_sort(arr,ret+1,high);}return 0;
}int main(int argc, char const *argv[]){int arr[10]={5,2,3,10,7,1,4,9,6,8};printf("排序前:\n");print(arr,10);printf("排序后:\n");quick_sort(arr,0,9);print(arr,10);return 0;
}

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

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

相关文章

Euler 欧拉系统介绍

Euler 欧拉系统介绍 1 简介重要节点与版本EulerOS 特色EulerOS 与 openEuler 区别联系Euler 与 HarmonyOS 区别联系 2 openEuler特色支持 ARM&#xff0c;x86&#xff0c;RISC-V 等全部主流通用计算架构融入 AI 生态嵌入式实时能力提升引入 OpenHarmony 一些突出功能 参考 1 简…

将 KNX 接入 Home Assistant 之二 准备软件

写在前面&#xff1a; 在KNX官网也有关于 Home Assistant 的教程&#xff0c;地址是 Get started with Home Assistant x KNX 需要的东西是 a KNX IP Interface or Routera Raspberry Pian SD Card at least 32 GB 安装 Home Assistant 系统 下载镜像&#xff1a; 地址&…

EfficientNet结构的特点

EfficientNet是一种高效的卷积神经网络架构&#xff0c;它通过系统化的方法来提升模型的性能和效率。由Google AI提出&#xff0c;EfficientNet的设计理念是通过网络的复合缩放&#xff08;compound scaling&#xff09;来均衡地扩展网络的深度&#xff08;depth&#xff09;、…

idea中git检出失败

之前clone好好的&#xff0c;今天突然就拉取不下来了。很多时候是用户凭证的信息没更新的问题。由于window对同一个地址都存储了会话。如果是新的会话&#xff0c;必须要更新window下的凭证。 然后根据你的仓库找到你对应的账户&#xff0c;更新信息即可。

反射器和联邦实验

拓扑 要求 IP配置 [R1-GigabitEthernet0/0/0]ip add 12.0.0.1 24 [R1-LoopBack0]ip add 192.168.1.1 24 [R1-LoopBack1]ip add 10.0.0.1 24[R2-GigabitEthernet0/0/0]ip add 12.0.0.2 24 [R2-GigabitEthernet0/0/1]ip add 172.16.0.1 21 [R2-GigabitEthernet0/0/2]ip add 17…

(IDEA修改Java版本)java: 警告: 源发行版 X 需要目标发行版 X

搜索关键词&#xff1a;一致、发行 错误信息 其他错误&#xff1a; java: 错误: 不支持发行版本 6 java: -source 1.5 中不支持 lambda 表达式 (请使用 -source 8 或更高版本以启用 lambda 表达式) 思路 有两个地方要检查&#xff0c;JDK版本保持一致即可。 比如统一用JDK8或…

用 Python 编写自动发送每日电子邮件报告的脚本

第一步&#xff1a;安装必要的库 你需要安装 smtplib&#xff08;Python 自带&#xff09;&#xff0c;但你需要安装 schedule 和 email 库。你可以使用以下命令安装这些库&#xff1a; pip install schedule第二步&#xff1a;编写发送邮件的脚本 这里是一个完整的 Python …

前端基础入门三大核心之网络安全篇:TLS/SSL的魔法之旅

前端基础入门三大核心之网络安全篇&#xff1a;TLS/SSL的魔法之旅 TLS/SSL&#xff1a;数据安全的守护神工作原理&#xff1a;四步走&#xff0c;安全到家1. 握手&#xff0c;你好吗&#xff1f;代码示例&#xff08;伪代码&#xff09;&#xff1a;客户端发起握手请求 2. 身份…

FM1800隧道广播插播控制器

隧道广播插播控制器是一款群载波&应急广播插播控制器采用SDR软件无线电技术&#xff0c;产生独立的插播信号与“群载波”信号&#xff0c;本设备可通过软件无线电技术将音频信号调制成调频载波或“群载波”信号&#xff0c;分别送入插播主机&#xff0c;实现隧道广播远端机…

20240528解决飞凌的OK3588-C的核心板可以刷机不能连接ADB的问题

20240528解决飞凌的OK3588-C的核心板可以刷机不能连接ADB的问题 2024/5/28 16:34 OS&#xff1a;Linux R4/Buildroot 硬件接了3条线出来&#xff0c;一直可以刷机&#xff0c;但是链接ADB异常。 【总是链接不上】 Z:\OK3588_Linux_fs\kernel\arch\arm64\boot\dts\rockchip\OK3…

nacos需要开放的端口

Nacos部署需要开放以下四个端口以确保其正常运行&#xff1a; 主端口&#xff1a;默认为8848&#xff0c;用于客户端、控制台及OpenAPI的HTTP通信。客户端gRPC请求服务端端口&#xff1a;默认为9848&#xff0c;用于客户端向服务端发起gRPC连接和请求。服务端gRPC请求服务端端…

代码随想录算法训练营Day53 | 1143.最长公共子序列 1035.不相交的线 53. 最大子序和 动态规划

代码随想录算法训练营Day53 | 1143.最长公共子序列 1035.不相交的线 53. 最大子序和 动态规划 LeetCode 1143.最长公共子序列 题目链接&#xff1a;LeetCode 1143.最长公共子序列 思路&#xff1a; 三种情况得到dp[i][j] class Solution { public:int longestCommonSubseq…

[ISCC 2024] pwn

记得去年的ISCC&#xff0c;热闹。今年的逆向啥的都不会作了&#xff0c;就作了点PWN&#xff0c;有两个是师傅给的WP。也算凑齐了。 这网站比赛结束后&#xff0c;居然上不去了&#xff0c;弄得连几个题都不清楚了。 练武题 chao 这题比较搞&#xff0c;在edit有可读入0x2…

Android11 事件分发流程

在Android 11 输入系统之InputDispatcher和应用窗口建立联系一文中介绍到&#xff0c;当InputDispatcher写入数据后&#xff0c;客户端这边就会调用handleEvent方法接收数据 //frameworks\base\core\jni\android_view_InputEventReceiver.cpp int NativeInputEventReceiver::h…

学校机房搞机、摸鱼

平时去机房上课&#xff0c;老师讲得很无趣&#xff0c;自己又不感兴趣&#xff0c;想干其他的&#xff0c;但是老师控制了屏幕&#xff0c;没法摸鱼&#xff0c;这时就需要学习搞机技术了。 解除还原&#xff1f; 学校机房的电脑都装有自动还原功能&#xff0c;每次关机或重…

炒黄金怎么追单?-融知财经网

在黄金投资领域,当市场行情呈现出有利的走势时,许多交易者会选择追加下单以扩大盈利。追单作为一种投资策略,旨在利用市场波动获取额外收益。然而,要想在追单中取得成功,需要掌握一定的技巧和策略。融知财经网给介绍黄金交易中追单的一些关键技巧,帮助投资者理智追单,稳健获利。…

线性插值的频域特性

1、抽取和插值的简单说明 抽取和插值是变采样过程中常用的两种手段&#xff0c;其中抽取的目的是降低数据的采样率&#xff0c;以降低对系统存储深度或计算量的要求。插值的目的是提高数据的采样率&#xff0c;以提高系统的计算精度。 M M M倍抽取通常是通过每隔 M M M…

Docker安装Nginx 并实现通过nginx部署静态网址

Docker镜像就是一个只读的模板&#xff0c;可以用来创建Docker容器。 例如&#xff1a;一个镜像可以包含一个完整的centos操作系统环境&#xff0c;里面仅安装了mysql、nginx等或用户需要的其他应用程序。 Docker提供了一个非常简单的机制来创建镜像或者更新现有的镜像&#…

GTD时间管理法

Part 1. What is GTD? | 什么是GTD&#xff1f; GTD is a framework that enhances focus and productivity. Through techniques such as capturing all tasks in a trusted system and breaking down complex projects into actionable items, GTD allows individuals to co…

美业系统SaaS收银系统源码-顾客在系统付款了但系统未显示怎么办?美业系统实测

美业SaaS系统 连锁多门店美业收银系统源码 多门店管理 / 会员管理 / 预约管理 / 排班管理 / 商品管理 / 活动促销 PC管理后台、手机APP、iPad APP、微信小程序 1. 提供门店名称、付款凭证和会员手机号 2. 到订单明细查询&#xff0c; 按门店名称和会员手机号查询看是否有相…