【C/C++刷题集】类和对象算法题(一)

🫧个人主页:小年糕是糕手

💫个人专栏:《C++》《C++同步练习》《数据结构》《C语言》

🎨你不能左右天气,但你可以改变心情;你不能改变过去,但你可以决定未来!



目录

一、求1+2+3+...+n

[思路1:通过静态成员变量统计累加结果 + 变长数组触发多次类构造,替代循环与条件判断,满足题目禁用关键字的要求。](#思路1:通过静态成员变量统计累加结果 + 变长数组触发多次类构造,替代循环与条件判断,满足题目禁用关键字的要求。)

1.1、先明确代码整体结构

1.2、逐个拆解核心组件(重点!)

[组件 1:辅助累加类 Sum------ 真正实现 "累加" 的工具](#组件 1:辅助累加类 Sum—— 真正实现 “累加” 的工具)

[组件 2:静态成员初始化 ------ 必须在类外执行](#组件 2:静态成员初始化 —— 必须在类外执行)

[组件 3:求解类 Solution------ 控制求和范围的 "总指挥"](#组件 3:求解类 Solution—— 控制求和范围的 “总指挥”)

[1.3、完整执行流程(以 n=3 为例,求 1+2+3)](#1.3、完整执行流程(以 n=3 为例,求 1+2+3))

[步骤 1:调用求和接口 Sum_Solution(3)](#步骤 1:调用求和接口 Sum_Solution(3))

[步骤 2:创建第一个对象 arr[0]------ 触发第一次构造函数](#步骤 2:创建第一个对象 arr[0]—— 触发第一次构造函数)

[步骤 3:创建第二个对象 arr[1]------ 触发第二次构造函数](#步骤 3:创建第二个对象 arr[1]—— 触发第二次构造函数)

[步骤 4:创建第三个对象 arr[2]------ 触发第三次构造函数](#步骤 4:创建第三个对象 arr[2]—— 触发第三次构造函数)

[步骤 5:返回结果](#步骤 5:返回结果)

代码改造:采用内部类

二、打印日期

思路:想想我们之前写得日期类,主要就是判断一个月的天数(注意闰年的2月),然后用我们的天数n去依次减去对应月份天数,当n小于对应月份天数时,找对对应月份,最后打印出来即可

三、日期累加

[思路:将待累加的天数直接加到日期的 "日" 字段上,若 "日" 超过当月总天数,则不断减去当月天数、月份加 1(月份超 12 则年份加 1、月份重置为 1),直到 "日" 落在当月合法范围内。](#思路:将待累加的天数直接加到日期的 “日” 字段上,若 “日” 超过当月总天数,则不断减去当月天数、月份加 1(月份超 12 则年份加 1、月份重置为 1),直到 “日” 落在当月合法范围内。)

四、计算日期到天数转换

思路1:这也是一个日期类的简单应用,我们只需要找到每个月的天数,然后使用一个累加函数,不断加日期,如果日期<当月天数直接+上去即可

思路2:通过枚举每个月的1号是这一年的第几天,从而进行累加求和即可,其中注意闰年的处理

五、日期差值

思路1:我们先看看俩个日期是否连续,连续的我们直接返回2即可,如果不是连续,我们分别计算这俩个日期与1年1月1日的差值,然后对应相减取绝对值即可

思路2:我们也可以用上一题所说的暴力破解方法的基础上来简化代码


一、求1+2+3+...+n

求1+2+3+...+n

思路1:通过静态成员变量统计累加结果 + 变长数组触发多次类构造,替代循环与条件判断,满足题目禁用关键字的要求。
cpp 复制代码
class Sum
{
public:
	Sum()
	{
		_ret += _i;
		++_i;
	}
	static int GetRet()
	{
		return _ret;
	}
private:
	static int _i;
	static int _ret;
};


int Sum::_i = 1;
int Sum::_ret = 0;

//C++规定对象实例化必须调用构造
class Solution {
public:
	//成员函数
	int Sum_Solution(int n) {
		// 变⻓数组 
		Sum arr[n];
		return Sum::GetRet();
	}
};
1.1、先明确代码整体结构

这段代码由 3 个核心部分 组成,分工明确:

cpp 复制代码
// 1. 辅助累加类:负责实现「每次创建对象就累加一个数」的逻辑
class Sum { ... };

// 2. 静态成员初始化:静态变量必须在类外初始化(不属于任何对象)
int Sum::_i = 1;
int Sum::_ret = 0;

// 3. 求解类:提供对外的求和接口,控制累加的次数(即 1~n 的范围)
class Solution {
public:
    int Sum_Solution(int n) { ... } // 求和核心接口
};
1.2、逐个拆解核心组件(重点!)
组件 1:辅助累加类 Sum------ 真正实现 "累加" 的工具

Sum 类的作用是「通过构造函数自动累加」,每个成员都有明确分工

cpp 复制代码
class Sum
{
public:
    // (1)构造函数:名字和类名相同,无返回值,创建对象时自动调用
    Sum()
    {
        _ret += _i;  // 核心操作1:把当前要加的数(_i)累加到总和(_ret)
        ++_i;        // 核心操作2:让下一个要加的数自增1(比如这次加1,下次加2)
    }

    // (2)静态成员函数:获取累加结果的"接口"
    static int GetRet()
    {
        return _ret; // 返回已经累加好的总和
    } 

private:
    // (3)静态成员变量:所有对象共享,生命周期和程序一致(存在静态区)
    static int _i;    // 记录「当前要累加的数字」,初始值1(从1开始加)
    static int _ret;  // 记录「累加的总和」,初始值0(一开始总和为0)
};

关键说明(为什么用「静态成员」?)

  • _i_ret 是普通成员变量:每个 Sum 对象会有独立的 _i_ret 副本(比如对象 1 的 _i=1,对象 2 的 _i 也是 1),无法实现 "1→2→3→...→n" 的连续累加;
  • 若设为 static 静态成员:所有 Sum 对象共享同一个 _i_ret(比如对象 1 改了 _i=2,对象 2 用的就是 2),才能实现 "累加传递"。
组件 2:静态成员初始化 ------ 必须在类外执行
cpp 复制代码
int Sum::_i = 1;  // 初始化「当前要累加的数」为1(从1开始求和)
int Sum::_ret = 0; // 初始化「累加总和」为0(初始无任何累加)
  • 为什么要在类外初始化?静态成员变量不属于任何对象,不会走类的构造函数初始化列表,必须在类外单独分配内存并赋值(否则编译报错)。
组件 3:求解类 Solution------ 控制求和范围的 "总指挥"
cpp 复制代码
class Solution {
public:
    int Sum_Solution(int n) { // n是求和上限(比如n=5就是求1+2+3+4+5)
        Sum arr[n]; // 核心操作:创建一个长度为n的Sum类型数组(变长数组)
        return Sum::GetRet(); // 返回累加好的总和
    }
};

关键说明(Sum arr[n] 到底做了什么?)

  • 这是「变长数组」(GCC 编译器扩展特性),作用是「创建 n 个 Sum 类的对象」;
  • 数组的每个元素都是一个 Sum 对象,创建数组时会依次创建这 n 个对象;
  • 每创建一个 Sum 对象,就会自动调用一次 Sum 类的构造函数(也就是执行 _ret += _i; ++_i;);
  • 相当于通过 "创建 n 个对象",间接实现了 "循环 n 次累加"(替代了 for/while 循环,满足题目禁用关键字的要求)。
1.3、完整执行流程(以 n=3 为例,求 1+2+3)

我们模拟调用 Solution().Sum_Solution(3) 后的每一步,看代码如何一步步算出结果:

步骤 1:调用求和接口 Sum_Solution(3)
  • 传入参数 n=3,表示要计算 1+2+3;
  • 执行 Sum arr[3]:创建长度为 3 的数组,需要依次创建 3 个 Sum 对象(arr [0]、arr [1]、arr [2])。
步骤 2:创建第一个对象 arr[0]------ 触发第一次构造函数
  • 构造函数执行:
    1. _ret += _i_ret = 0 + 1 = 1(总和现在是 1);
    2. ++_i_i = 1 + 1 = 2(下一个要加的数是 2);
  • 此时状态:_ret=1_i=2
步骤 3:创建第二个对象 arr[1]------ 触发第二次构造函数
  • 构造函数执行:
    1. _ret += _i_ret = 1 + 2 = 3(总和现在是 3);
    2. ++_i_i = 2 + 1 = 3(下一个要加的数是 3);
  • 此时状态:_ret=3_i=3
步骤 4:创建第三个对象 arr[2]------ 触发第三次构造函数
  • 构造函数执行:
    1. _ret += _i_ret = 3 + 3 = 6(总和现在是 6);
    2. ++_i_i = 3 + 1 = 4(下一个要加的数是 4,但已经用不到了);
  • 此时状态:_ret=6_i=4
步骤 5:返回结果
  • 调用 Sum::GetRet(),获取静态成员 _ret 的值(6);
  • Sum_Solution(3) 函数返回 6,即 1+2+3 的结果。
代码改造:采用内部类
cpp 复制代码
class Solution {
// 内部类
    class Sum {
      public:
        Sum() {
            _ret += _i;
            ++_i;
        }
    };
    static int _i;
    static int _ret;
  public:
    int Sum_Solution(int n) {
// 变⻓数组
        Sum arr[n];
        return _ret;
    }
};
int Solution::_i = 1;
int Solution::_ret = 0;

二、打印日期

打印日期

思路:想想我们之前写得日期类,主要就是判断一个月的天数(注意闰年的2月),然后用我们的天数n去依次减去对应月份天数,当n小于对应月份天数时,找对对应月份,最后打印出来即可
cpp 复制代码
#include<iostream>
#include<assert.h>

using namespace std;

class Date
{
public:
    Date(int year = 1, int month = 1, int day = 1)
        :_year(year)
        ,_month(month)
        ,_day(day)
    { }

    // 得到当月天数(处理闰年二月)
    int GetMonthDay(int year, int month)
    {
        assert(month > 0 && month < 13);
        // 月份天数数组(下标0不用,下标1对应1月)
        static int monthDayArray[13] = { -1, 31,28,31,30,31,30,31,31,30,31,30,31 };
        // 闰年二月特殊处理
        if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
        {
            return 29;
        }
        else
        {
            return monthDayArray[month];
        }
    }

    //检查日期是否合法
    bool CheckDate()
    {
        if (_month < 1 || _month>12)
            return false;

        if (_day<1 || _day>GetMonthDay(_year, _month))
            return false;

        return true;
    }

    // 根据"年+第n天"计算对应的月和日
    void SetDateByDay(int year, int n) 
    {
        _year = year;
        _month = 1;
        // 循环减去每个月的天数,直到n小于当月天数
        while (n > GetMonthDay(_year, _month)) 
        {
            n = n - GetMonthDay(_year, _month);
            _month++;
        }
        _day = n;
    }

    // 格式化输出日期(yyyy-mm-dd)
    void Print() 
    {
        // 输出年份(直接输出,年份是4位不用补0)
        cout << _year << "-";

        // 月份:小于10则补0,否则直接输出
        if (_month < 10)
        {
            cout << "0" << _month;
        }
        else
        {
            cout << _month;
        }
        cout << "-";

        // 日期:小于10则补0,否则直接输出
        if (_day < 10) 
        {
            cout << "0" << _day;
        }
        else 
        {
            cout << _day;
        }

        // 换行
        cout << endl;
    }


private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int year, n;
    // 多组输入:年 + 第n天
    while (cin >> year >> n) {
        Date d;
        d.SetDateByDay(year, n); // 计算对应日期
        d.Print(); // 格式化输出
    }
    return 0;
}

三、日期累加

日期累加

思路:将待累加的天数直接加到日期的 "日" 字段上,若 "日" 超过当月总天数,则不断减去当月天数、月份加 1(月份超 12 则年份加 1、月份重置为 1),直到 "日" 落在当月合法范围内。
cpp 复制代码
//累加天数
#include<iostream>
#include<assert.h>

using namespace std;

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{ }

	//得到每个月的天数
	int GetMonthDay(int year, int month)
	{
		assert(month >= 1 && month <= 12);
		int arr[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
		if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
		{
			return 29;
		}
		else
		{
			return arr[month];
		}
	}

	//检查日期是否合法
	bool CheckDate()
	{
		if (_month >= 13 || _month < 1)
		{
			return false;
		}
					
		if (_day < 1 || _day > GetMonthDay(_year, _month))
		{
			return false;
		}
		
		return true;
	}

	// 日期累加days天
	void AddDays(int days) 
	{
		_day += days;
		while (true)
		{
			if (_day <= GetMonthDay(_year, _month))
			{
				break; // 天数在当月范围内,结束
			}
			// 天数超过当月,减去当月天数,月份+1
			_day -= GetMonthDay(_year, _month);
			_month++;
			if (_month > 12) { // 月份超过12,年份+1,月份重置为1
				_year++;
				_month = 1;
			}
		}
	}


	//格式化输出
	void Print()
	{
		cout << _year << "-";

		if (_month < 10)
		{
			cout << "0" << _month;
		}
		else
		{
			cout << _month;
		}
		cout << "-";

		if (_day < 10)
		{
			cout << "0" << _day;
		}
		else
		{
			cout << _day;
		}

		cout << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int m;
	cin >> m;
	while (m--)
	{
		int year, month, day, AddDay;
		cin >> year >> month >> day >> AddDay;
		Date d(year, month, day);
		d.AddDays(AddDay);
		d.Print();
	}

	return 0;
}

四、计算日期到天数转换

计算日期到天数转换

思路1:这也是一个日期类的简单应用,我们只需要找到每个月的天数,然后使用一个累加函数,不断加日期,如果日期<当月天数直接+上去即可
cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
#include<assert.h>
using namespace std;

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{ }

	//得到每个月的天数
	int GetMonthDay(int year, int month)
	{
		assert(month >= 1 && month <= 12);
		int arr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
		if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
		{
			return 29;
		}
		else
		{
			return arr[month];
		}
	}

	//判断日期是否在正确区间
	bool CheckDate()
	{
		if (_month > 12 || _month < 1)
			return false;
		if (_day<1 || _day>GetMonthDay(_year, _month))
			return false;

		return true;
	}

	//日期加法
	int add(int sum)
	{
		sum = _day; // 先算当月的天数(核心:先加_day,再累加之前的月份)
		_month--; // 从上个月开始累加(因为当月的day已经算过了)
		while (_month > 0) // 只要月份还大于0,就继续累加
		{
			sum += GetMonthDay(_year, _month); // 累加前一个月的天数
			_month--; // 月份继续减,直到1月
		}
		return sum; // 最终返回的就是"当年第几天
	}

	void Print()
	{
		cout << add(0) << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	int year, month, day;
	while (cin >> year >> month >> day)
	{
		//别忘记传值,不然会调用默认构造
		Date d(year, month, day);
		d.Print();
	}
	return 0;
}

当然这里的add是有优化空间的,我们可以创建一个临时变量就可以保留下_month的值,方便后续的二次使用(本题没有二次使用,所以使用哪个都可以):

cpp 复制代码
//日期加法
int add(int sum)
{
	int tmp_month = _month;
	sum = _day;
	tmp_month--;
	while (tmp_month > 0)
	{
		sum += GetMonthDay(_year, tmp_month);
		tmp_month--;
	}
	return sum;
}

这题还可以使用枚举是个非常暴力破解的方法:

思路2:通过枚举每个月的1号是这一年的第几天,从而进行累加求和即可,其中注意闰年的处理
cpp 复制代码
#include<iostream>
using namespace std;
int main()
{
    int year, month, day;
    while (cin >> year >> month >> day)
    {
        int monthDays[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
        int nday = monthDays[month - 1] + day;
        if (month > 2 && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0))
        {
            nday += 1;
        }
        cout << nday << endl;
    }
    return 0;
}

五、日期差值

日期差值

思路1:我们先看看俩个日期是否连续,连续的我们直接返回2即可,如果不是连续,我们分别计算这俩个日期与1年1月1日的差值,然后对应相减取绝对值即可
cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1


//有两个日期,求两个日期之间的天数,如果两个日期是连续的我们规定他们之间的天数为两天
//有多组数据,每组数据有两行,分别表示两个日期,形式为YYYYMMDD
//每组数据输出一行,即日期差值

#include<iostream>
#include<assert.h>
#include<string>
#include<cmath> // 用于abs()取绝对值
using namespace std;

class Date
{
public:
    Date(int year = 1, int month = 1, int day = 1)
        :_year(year)
        , _month(month)
        , _day(day)
    {
    }

    // 判断是否为闰年
    bool isLeap() const {
        return ((_year % 4 == 0 && _year % 100 != 0) || (_year % 400 == 0));
    }

    // 获取当月天数
    int getMonthDays() const {
        int days[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
        if (_month == 2 && isLeap()) {
            return 29;
        }
        return days[_month];
    }

    // 计算当前日期距离基准年(1年)的总天数
    long long getTotalDays() const {
        long long total = 0;
        // 累加之前所有年份的天数
        for (int y = 1; y < _year; ++y) {
            total += ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) ? 366 : 365;
        }
        // 累加当年的天数
        for (int m = 1; m < _month; ++m) {
            int d[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
            if (m == 2 && isLeap()) {
                total += 29;
            }
            else {
                total += d[m];
            }
        }
        total += _day;
        return total;
    }

private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    string dateStr1;
    string dateStr2;
    while (cin >> dateStr1 >> dateStr2) // 读取每一行的日期字符串(比如"20110412")
    {
        //这里是连续输入字符串我们需要调用string类中的substr来辅助我们实现
        //string的substr是C++中用于截取字符串子串的成员函数,作用是从原字符串中提取一部分内容,返回一个新的字符串
        // 拆分年、月、日:前4位→年,中间2位→月,最后2位→日
        int year1 = stoi(dateStr1.substr(0, 4));  // 从索引0开始取4个字符→年
        int month1 = stoi(dateStr1.substr(4, 2)); // 从索引4开始取2个字符→月
        int day1 = stoi(dateStr1.substr(6, 2));   // 从索引6开始取2个字符→日

        int year2 = stoi(dateStr2.substr(0, 4));  // 从索引0开始取4个字符→年
        int month2 = stoi(dateStr2.substr(4, 2)); // 从索引4开始取2个字符→月
        int day2 = stoi(dateStr2.substr(6, 2));   // 从索引6开始取2个字符→日
        //stoi是C++11引入的标准库函数
        //全称是string to integer核心作用是把字符串类型的数字(如 "2011"、"04")转换成整数类型(int)
        //是处理字符串数字拆分(比如日期拆分)的核心函数

        Date d1(year1, month1, day1);
        Date d2(year2, month2, day2);

        // 计算差值(题目规则:连续日期计为2天,即差值+1)
        long long diff = abs(d1.getTotalDays() - d2.getTotalDays()) + 1;
        cout << diff << endl;
    }
    return 0;
}
思路2:我们也可以用上一题所说的暴力破解方法的基础上来简化代码
cpp 复制代码
#include <iostream>
using namespace std;
//思路:1.分别求出每一个日期与0000年0月1日距离的天数
//     2.两个距离天数相减即可得到两个日期相差的天数

//平年从1月到n月的天数
int mon[12] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
//给出年月日,计算距离0000年0月1日的天数和
int CountDay(int y, int m, int d)
{
    // 计算0-y年的天数
    int yearDay = y * 365 + y / 4 - y / 100 + y / 400;
    // 计算到0-m月的天数
    int monthDay = mon[m - 1];
    if (m > 2 && ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0))
        monthDay += 1;
    return yearDay + monthDay + d;
}
int main()
{
    int year1, month1, day1;
    scanf("%4d%2d%2d", &year1, &month1, &day1);
    int n1 = CountDay(year1, month1, day1);
    int year2, month2, day2;
    scanf("%4d%2d%2d", &year2, &month2, &day2);
    int n2 = CountDay(year2, month2, day2);
    cout << abs(n1 - n2) + 1 << endl;
}

核心优势

  1. 效率高:用数学公式替代循环,计算大年份(比如 10000 年)也瞬间出结果;
  2. 逻辑简洁:把 "闰年、月份天数" 的复杂规则浓缩成公式,代码量少;
  3. 适配题目规则:+1 操作直接满足 "连续日期计为 2 天" 的要求。

相关推荐
博语小屋6 小时前
Socket UDP 网络编程V2 版本- 简单聊天室
linux·网络·c++·网络协议·udp
野生风长6 小时前
从零开始的C语言: 指针深入理解从入门到实践(中)指针与数组的结合
c语言·数据结构·算法
一叶之秋14127 小时前
从零掌握 List:高效操作与性能优化实战
数据结构·c++·list
张人玉7 小时前
Halcon条码技术详解(含 Halcon 应用示例)
大数据·人工智能·算法·halcon
跨境猫小妹7 小时前
告别 0 曝光!TikTok 冷启动在算法丛林建信任营地
大数据·人工智能·算法·产品运营·亚马逊
副露のmagic7 小时前
更弱智的算法学习 day12
数据结构·学习·算法
fie88897 小时前
C++实现D星 Lite算法
c++·算法·哈希算法
筏.k7 小时前
C++ 设计模式系列:单例模式
c++·单例模式·设计模式
liu****7 小时前
二.protobuf的使用
c++·c·protobuf·企业级组件