【C++】 —— 笔试刷题day_28

【C++】 —— 笔试刷题day_28

一、游游的重组偶数

题目解析

在这里插入图片描述
这道题,有q组数据,每一次输入一个正整数x,让我们将这个数进行重排,变成一个偶数,然后返回(如果x本身就是一个偶数那可以直接返回x);

如果不存在合法解,就是x通过重排后,无法变成一个偶数,就输出-1

算法思路

这道题,总体来说还是比较简单的;

对于正整数x,我们可以把它当作一个字符串进行输入;(如果按照整数输入,我们还要将这个数x的每一位变换成对应数组)

我们知道,如果一个数是偶数,那最低位一定是一个偶数,这样我们只需判断字符串的最后一位即可知道这个数是否是偶数;如果这个数是偶数,那就直接输出即可;如果最后一位不是偶数,那就从第一位开始向后找,找到一位是偶数,然后把它交换到最后一位;然后输出即可;如果遍历完这个字符串,还没找到一位是偶数的,那就表示这个数x通过重拍无法变成偶数,输出-1即可。

题目解析

#include<iostream>usingnamespace std; string func(){ string str; cin >> str;int n = str.size();if((str[n -1]-'0')%2==0)return str;for(int i =0; i < n -1; i++){if((str[i]-'0')%2==0){swap(str[i], str[n -1]);return str;}}return"-1";}intmain(){int q; cin >> q;while(q--){ cout <<func()<< endl;}return0;}

二、体操队形

题目解析

在这里插入图片描述
dd作为体操队长,现在要对体操队的n名同学(1 ~ n)进行排队;对于第i号队员,会有一个诉求a[i],表示i要排在a[i]的前面;如果a[i] == i,那就表示当前i号队员没有任何诉求,排在哪里都行。

现在dd想知道一共有多少种排队方案。

算法思路

初看这道题,可以说毫无头绪,根本无从下手;

我们要对n个队员排序;一部分队员还要求排在某个队员的前面,对于这个要求,我们很容易就可以解决了:

我们从左到右依次排队即可,排第i个队员时,只需判断它的诉求a[i]有没有排过就OK了。

那对于第i个位置,我们怎们知道某一个队员,能否在这个位置呢?

这就非常简单了,我们只需要记录一下每一个队员是否已经排过队了即可;

我们只需遍历每一个队员,看这一个队员是否能够放到该位置即可;

而我们一个队员不能放到该位置,有两种情况:一是这个队员已经排过了,我们接着遍历下一个队员即可;另一种则是,当前队员的诉求a[i]已经排过了,也就是该队员的诉求[i]在该队员i的前面;那此时无论我们怎们排,那肯定是不满足条件的;那我们就无需向后进行排了。

看到这里,相信大致思路以及能够理清楚了,我们先来绘制一下这道题的一个决策树:

在这里插入图片描述

代码实现

#include<iostream>usingnamespace std;int arr[11];//每一个队员的诉求bool vis[11];//记录每一个队员是否被排过int ret =0;//最终结果int n;voidbfs(int pos){if(pos == n +1){//表示找到一种排序方案 ret++;return;}for(int i =1;i<=n;i++){if(vis[i]==true)//i队员已经被排过continue;if(vis[arr[i]]==true)//i队员的诉求已经排过,也就是arr[i] 在 i的前面了,无论怎么排都不满足条件return; vis[i]=true;//当前位置被排过bfs(pos +1);//排下一个位置 vis[i]=false;//修改,回溯}}intmain(){ cin>>n;for(int i =1;i<=n;i++){ cin>>arr[i];}bfs(1);//从第一个位置开始填 cout<<ret<<endl;return0;}

三、二叉树中的最大路径和

题目解析

在这里插入图片描述
这道题还是比较难理解的,我们来看:

题目给定一个二叉树,让我们求出最大路径和;

路径:我们可以从二叉树中的任意节点出发,通过父 -> 子或者子 -> 父,到达任意节点的序列。同一个节点在一个二叉树路径中最多只能出现一次,简单来说就是同一个节点只能经过一次;一个路径至少包含一个节点,而且不一定要包含跟节点。

就如示例中给的1 , 2 , 3,最优路径就是2 -> 1 -> 3或者3 -> 1 -> 2

算法思路

OK啊,初看这道题,如果题目读的似懂非懂的,那可以说一点思路没有(博主第一次看到这道题就是这样的)

仔细思考一下,这道题不就是一个递归/dfs问题吗;

这里分享一下博主做题时的思路:

对于题目描述,我们要找的路径,它可以是从一个节点先一直向上走,然后在某一个节点,开始再走向子节点;当然也可以是从一个节点一直向上走,不再向下走向子节点。

所以对于二叉树的每一个节点我们可以求一下,在这一个节点为转折点时的最大路径和;

我们要求某一个节点为转折点的最大路径和,那我们就要知道左子树单路径的最大路径和和右子树单路径的最大路径和;我们就只能通过递归的返回值来获得,所以我们返回值就表示从一个节点向下走到nullptr的单路径的最大路径和。

所以在遍历到某一个节点时,要做的就是:求以这个节点为转折点时的最大路径和;然后返回从这个节点向下走到nullptr的单路径的最大路径和。

如果左子树和右子树的最大单路径和是<0的,我们可以不向下走(因为我们要求的是最大路径和);

这样我们只需要考虑最大路径和的第一种情况,就是先向上走,但某一个节点再向下走;对于第二种情况,不就是我们返回值的情况吗。

但是上述中存在一个问题,也是博主在写这道题时没有注意到的一个问题:

这里博主直接判断的是左子树和右子树的最大单路径和都>0时,才计算左右子树最大单路径的和;博主没有考虑到如果左子树和右子树的最大单路径和一个<0,一个小于>0时的情况;

因为博主的疏忽,这也导致博主的思路有一个漏洞:如果考虑我们以某一个节点为转折点的情况,那最大路径和是第二章情况时,我们求出来的就是错误的答案;就比如10 , 5 , -9,正确结果是15而我们求出来的就是10

因为5 -> 10这一种情况在函数最后的返回值当中,而这里没有考虑这一个返回值。(但是这里,是可以通过这道题目的,可以是这道题没有这种情况的测试用例)。

无论如何,博主感觉自己的思路还是存在漏洞的,要在最后考虑一个递归函数的返回值。

OK啊,现在来看这道题的思路:

其实上述就是这道题的大致思路了,哈哈!

我们要求二叉树中的最大路径和,我们要采用递归,bfs;(递归肯定是后序遍历)

那遍历到一个节点时,我们要求什么?

在博主的思路中,求的是以这个节点为转折点的路径和的最大值;

但是如果左子树和右子树某个子树中的最大单路径和<0时,那此时的最大路径和就不是上述中的第一种情况了,而是第二种情况;那我们就漏掉了一直情况。

所以这里我们在遍历到一个节点时,要求的是在该子树中,经过该节点的路径的最大路径和。

那要求经过该节点的最大路径和,我们就要知道左子树和右子树的最大单路径的和

所以我们的返回值就表示该子树中的最大单路径的和,当然要经过当前节点。

OK我们知道了要求什么,和返回值是什么,现在来看如何去求:

经过该节点的最大路径和,那当左右子树最大单路径和可能是小于0的,而<0时,我们肯定不会向下走的,所以我们可以对左右子树的最大单路径和与0求一个最大值;

也就是说当左右子树的最大单路径和是<0时,我们求经过该节点的最大路径和时,左leftright子树最大单路径和是=0的;我们当前节点就表示一个路径。

**返回值:**因为我们这里的leftright都是>=0的,我们直接返回当前节点的值val + max(left,right)即可。

这样最后,我们就无需再对递归函数的返回值就行判断,直接返回结果即可。

代码实现

博主自己的思路:

classSolution{public:int ret =-1000*100000;intfunc(TreeNode* root){if(root ==nullptr)return0;int left =func(root->left);int right =func(root->right);//求if(left >=0&& right >=0) ret =max(ret, left + right + root->val);else ret =max(ret,root->val);returnmax(left,right)+ root->val;}intmaxPathSum(TreeNode* root){returnmax(ret,func(root));}};

优化后的思路代码:

classSolution{public:int ret =-1010;intbfs(TreeNode* root){if(root ==nullptr)return0;int left =max(bfs(root->left),0);int right =max(bfs(root->right),0); ret =max(ret, root->val + left + right);return root->val +max(left, right);}intmaxPathSum(TreeNode* root){// write code herebfs(root);return ret;}};

到这里,本篇文章内容就结束了,感谢各位支持

Read more

大学计算机专业面向对象程序设计教程(C++)——基于电子工业出版社《面向对象程序设计教程——C++》(西安理工大学教材)——期末看完心里有底!

大学计算机专业面向对象程序设计教程(C++)——基于电子工业出版社《面向对象程序设计教程——C++》(西安理工大学教材)——期末看完心里有底!

面向对象程序设计教程——C++ 知识文档 第1章 面向对象方法学 1.1 面向对象方法学的发展 面向对象方法学是一种软件开发方法,它将现实世界中的事物抽象为对象,通过对象之间的交互来模拟现实世界的行为。面向对象方法学的发展经历了以下几个阶段: 1. 萌芽阶段(20世纪60年代):Simula语言的出现,首次引入了类和对象的概念。 2. 形成阶段(20世纪70年代):Smalltalk语言的开发,完善了面向对象的基本概念和方法。 3. 成熟阶段(20世纪80年代至今):C++、Java、C#等面向对象语言的出现和广泛应用,面向对象方法学成为主流的软件开发方法。 1.2 面向对象方法学的概述 面向对象方法学包括三个主要阶段:面向对象分析(OOA)、面向对象设计(OOD)和面向对象实现(OOI)。 1.2.1 面向对象分析 面向对象分析的主要任务是分析用户需求,识别问题域中的对象,确定对象的属性和行为,以及对象之间的关系。主要步骤包括: 1.

By Ne0inhk
《C++ 递归、搜索与回溯》第1题:汉诺塔问题

《C++ 递归、搜索与回溯》第1题:汉诺塔问题

🔥个人主页:Cx330🌸 ❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》 《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔 《Git深度解析》:版本管理实战全解 🌟心向往之行必能至 🎥Cx330🌸的简介: 前言: 聚焦算法题实战,系统讲解三大核心板块:“精准定位最优解”——优选算法,“简化逻辑表达,系统性探索与剪枝优化”——递归与回溯,“以局部最优换全局高效”——贪心算法,讲解思路与代码实现,帮助大家快速提升代码能力 目录 前言: 递归,搜索与回溯算法前置知识 1. 汉诺塔 算法原理(递归): 思路: 算法流程: 解法代码(C++): 博主手记(字体还请见谅哈): 结尾: 递归,搜索与回溯算法前置知识 1. 汉诺塔 题目链接: 面试题 08.

By Ne0inhk
C++ 虚函数与纯虚函数:多态的核心实现基石

C++ 虚函数与纯虚函数:多态的核心实现基石

C++ 虚函数与纯虚函数:多态的核心实现基石 💡 学习目标:深度理解虚函数与纯虚函数的本质区别,掌握虚函数表的底层原理,能够灵活运用二者设计具备多态特性的类结构。 💡 学习重点:虚函数的声明与重写规则、纯虚函数与抽象类的使用场景、虚函数表的工作机制、虚函数的常见陷阱与解决方案。 一、虚函数的本质与定义 ✅ 结论:虚函数是 C++ 实现动态多态的核心,通过在基类成员函数前添加 virtual 关键字,允许派生类重写该函数,并在运行时根据对象的实际类型调用对应版本。 1.1 虚函数的声明语法 虚函数的声明必须在基类中进行,语法格式如下: class 基类名 {public:virtual 返回值类型 函数名(参数列表){// 函数体}}; 1.2 虚函数的核心特性 1. 运行时绑定:函数调用关系在程序运行时确定,而非编译时。 2. 重写规则:派生类重写的函数必须与基类虚函数的函数名、参数列表、返回值类型完全一致(协变类型除外)。 3.

By Ne0inhk
C++可变参数队列与压栈顺序:从模板语法到汇编调用约定的深度解析

C++可变参数队列与压栈顺序:从模板语法到汇编调用约定的深度解析

C++可变参数队列与压栈顺序:从模板语法到汇编调用约定的深度解析 本文聚焦一个具体而关键的技术主题:C++ 可变参数模板(Variadic Templates)。我们将从现代 C++ 的优雅写法出发,深入剖析其在 x86-64 架构下的真实行为,特别澄清一个长期被误解的核心问题——可变参数是否“从右向左压栈”?它们在寄存器和栈中究竟是如何排布的? 如果你正在实现一个类型安全的消息队列、日志系统或任务调度器,并希望理解 enqueue(1, "hello", 3.14) 这行代码在 CPU 层面到底发生了什么,那么这篇文章就是为你量身打造的。 一、引言:可变参数 ≠ va_list —— 一场范式革命 很多初学者将 C++ 的可变参数模板与 C 语言的 va_list 混为一谈。这是重大误区,甚至会导致错误的性能假设和安全漏洞。 1.1

By Ne0inhk