2026/3/28 21:23:18
网站建设
项目流程
网站建设的价,建设网站的编程过程,关键路径,站长工具seo优化建议本类型博客会讲述一些比较重要的#xff0c;或者需要一定思考的算法#xff0c;与难度本身无关
一.日期处理专题#xff1a;从基础到进阶
对于日期相关的算法#xff0c;我将总结一下几部分内容作为模板#xff0c;它可以套用到与之相关的日期处理问题中#xff1a;
1. 日…本类型博客会讲述一些比较重要的或者需要一定思考的算法与难度本身无关一.日期处理专题从基础到进阶对于日期相关的算法我将总结一下几部分内容作为模板它可以套用到与之相关的日期处理问题中1. 日期计算的核心数据结构1.1 月份天数表的两种初始化方式平年/闰年intdayOfMonth[2][13]{// 每个月的天数{0,31,28,31,30,31,30,31,31,30,31,30,31},//平年{0,31,29,31,30,31,30,31,31,30,31,30,31}//闰年};1.2 闰年判断的完整规则与边界条件boolisLeapYear(intyear){// 闰年规则能被400整除或能被4整除但不能被100整除returnyear%4000||(year%40year%100!0);}1.3日期存储的最佳实践建议使用结构体或类封装日期便于操作structDate{intyear,month,day;Date(inty,intm,intd):year(y),month(m),day(d){}};2.日期基本详解2.1 日期加一天/减一天 - addOneDay/decreaseOneDay// 给当前日期加1天注意参数都用了引用这样对参数的修改可以同步到函数外voidaddOneDay(intyear,intmonth,intday){day;// 让day加1if(daydayOfMonth[isLeapYear(year)][month]){// 如果超过当前月的天数month;// 让month加1day1;// 重置day为1号}if(month12){// 如果月份大于12year;// 让year加1month1;// 重置month为1月}}voidsubOneDay(intyear,intmonth,intday){day--;// 日份-1if(day1){// 若小于1号month--;daydayOfMonth[isLeapYear(year)][month];// 月份-1}if(month1){// 若小于1月year--;// 年份-1month12;daydayOfMonth[isLeapYear(year)][month];// 月份置12}}假设要算日期的加法和减法通过for循环调用即可for(inti0;in;i){//year、month和day为当前日期subOneDay(year,month,day);//addOneDay(year,month,day);}2.2 日期差计算的两种策略①逐天迭代intdaysBetween(Date d1,Date d2){if(d1.yeard2.year||(d1.yeard2.yeard1.monthd2.month)||(d1.yeard2.yeard1.monthd2.monthd1.dayd2.day)){swap(d1,d2);}intdays0;while(!(d1.yeard2.yeard1.monthd2.monthd1.dayd2.day)){addOneDay(d1);days;}returndays;}②公式法这个方法更好intdaysFromStart(intyear,intmonth,intday){inttotal0;for(inty1;yyear;y){totalisLeapYear(y)?366:365;}intleapisLeapYear(year);for(intm1;mmonth;m){totaldayOfMonth[leap][m];}totalday;returntotal;}intdaysBetween(Date d1,Date d2){returnabs(daysFromStart(d2.year,d2.month,d2.day)-daysFromStart(d1.year,d1.month,d1.day));}3.日期比较与映射规则3.1 比较日期的先后// 比较日期1是否在日期2之前boolisBefore(inty1,intm1,intd1,inty2,intm2,intd2){if(y1!y2)returny1y2;// 年份先后if(m1!m2)returnm1m2;// 月份先后returnd1d2;// 日份先后}// 比较日期1是否在日期2之后boolisAfter(inty1,intm1,intd1,inty2,intm2,intd2){if(y1!y2)returny1y2;// 年份先后if(m1!m2)returnm1m2;// 月份先后returnd1d2;// 日份先后}也可以直接统一处理intcompareDate(Date d1,Date d2){if(d1.year!d2.year)returnd1.year-d2.year;if(d1.month!d2.month)returnd1.month-d2.month;returnd1.day-d2.day;}3.2日期映射规则处理由这道题可以知道星期一~星期六对应1-6而星期日对应的是0一般我们会根据样例给出的时间作为轴点根据和轴点对比来加或减少日期最后根据对应的更新规则得到对应的星期intmain(){intyear12021,month15,day11;intdayOfWeek6;// 参考日期2021-05-01为周六(6)intyear2,month2,day2;scanf(%d-%d-%d,year2,month2,day2);// 读取目标日期if(isBefore(year1,month1,day1,year2,month2,day2)){while(isBefore(year1,month1,day1,year2,month2,day2)){addOneDay(year1,month1,day1);dayOfWeek(dayOfWeek1)%7;// 向后推进星期}}elseif(isAfter(year1,month1,day1,year2,month2,day2)){while(isAfter(year1,month1,day1,year2,month2,day2)){subOneDay(year1,month1,day1);dayOfWeek(dayOfWeek-17)%7;// 向前退星期}}printf(%d,dayOfWeek);// 输出最终星期return0;}但是假设现在星期日代表为7那应该如何调整这个映射规则呢我们先看看当前题目的映射规则一个模7的循环系统0(日)→1(一)→2(二)→3(三)→4(四)→5(五)→6(六)→0(日)星期更新的数学原理为星期数字 1但如果超过 6 就回到 0星期数字 -1但如果小于 0 就回到 6。w(w1)%7例1今天是周六(6)明天是周日(0)(61)%70例2今天是周日(0)明天是周一(1)(01)%71w(w-17)%7//7 是为了避免负数取模结果不确定例今天是周一(1)昨天是周日(0)(1-17)%70例今天是周日(0)昨天是周六(6)(0-17)%76如果星期的映射改变后此时新映射下的规则// 向后一天dayOfWeek(dayOfWeek%7)1;// 向前一天dayOfWeek(dayOfWeek-27)%71;向后一天先用 %7 将 7 变成 0然后 1 实现循环向前一天先 -2因为模7系统下向前一天相当于数字-1但要处理7→6的转换实际上采用查表之类的操作其实更为直观但是掌握一些规则也可以帮助你训练一下思维。4. 实用函数封装4.1 计算一年中的第几天intSummaryYear(intyear,intmonth,intday){intsum0;inttempisLeapYear(year)?1:0;for(inti1;imonth;i)//除当月外的前几个月之和{sumdayOfMonth[temp][i]}sumday;//加上当月的天数returnsum;}4.2 日期合法性的验证boolisValidate(intyear,intmonth,intday){if(month1||month12)returnfalse;intleapisLeapYear(year);if(day1||daydayOfMonth[leap][month])returnfalse;returntrue;}2025-2-12: 先加1月份 - 31天再加当月 - 12天sum 1231 43二、进制转换专题深入理解数值表示的本质1.字符与数字的转换规则字符范围 转换公式 0-9 digit 字符 - 0 A-Z digit 字符 - A 10 a-z digit 字符 - a 10如果需要小写支持2.十进制-K进制除基取余法2.1 基础实现除数取余法通过反复除K取余数然后将余数逆序排列得到K进制表示下面用的是vector比较方便用普通的数组也是一样的思路的//1.未考虑负数可以用这种intmain(){intn,K;cinnK;vectorintsummary;if(n0)//输入为0的情况单独处理{coutn;return0;}while(n){summary.push_back(n%K);//n进制的转换n/K;}//逆向打印for(autoitsummary.rbegin();it!summary.rend();it){// 倒序输出if(*it9){cout*it;// 输出数字}else{coutchar(*it-10A);// 输出大写字母}}return0;}//2.需要考虑负数stringdecimalToK(intnum,intbase){if(num0)return0;//本身为0if(base2||base36)return;//控制进制的范围bool negativefalse;//处理负数的情况 - 先转换为正数最后在结尾加负号if(num0){negativetrue;num-num;}//num 10-1010 base 2string result;//下面的注释为第一次循环while(num0){intremaindernum%base;//10%2 0 -remainder 0//numToChar - 将整数转换为对应字符0 - 0resultnumToChar(remainder)result;// “” ‘0’ “0”num/base;// 10 / 2 5}if(negative)result-result;//负数最后记得加负号returnresult;}总结一下除数取余法的步骤就是①处理特殊情况if(n0){cout0;// 0在任何进制下都是0return0;}②反复除K取余vectorintdigits;// 存储余数从低位到高位while(n0){digits.push_back(n%K);// 获取当前余数当前最低位n/K;// 更新n为商继续下一轮}③余数逆序输出// 从最高位到最低位输出for(autoitdigits.rbegin();it!digits.rend();it){// 处理余数9的情况10进制以上if(*it10){cout*it;// 0-9直接输出数字}else{coutchar(*it-10A);// 10-A, 11-B, ... 35-Z}}2.2 大数处理这可能需要慢慢了解stringdecimalToKBig(string numStr,intbase){// 步骤1把字符串转换成数字数组// 1234 - [1, 2, 3, 4]vectorintdigits;for(charc:numStr){digits.push_back(c-0);// 1变成12变成2...}string result;// 存储最终的K进制结果// 步骤2反复除法直到数字变成0while(!digits.empty()){intremainder0;// 余数vectorintnext;// 存放商// 步骤3模拟一次除法核心for(intdigit:digits){// 当前值 上一位的余数×10 当前位intcurrentremainder*10digit;// 除法商存入next余数更新next.push_back(current/base);remaindercurrent%base;}// 步骤4这次的余数就是K进制的一位resultnumToChar(remainder)result;// 注意要逆序// 步骤5去除商的前导0digits.clear();bool leadingZerotrue;for(intnum:next){if(leadingZeronum0){continue;// 跳过前导0}leadingZerofalse;digits.push_back(num);}}// 如果结果是空说明原始数是0returnresult.empty()?0:result;}3.K进制转十进制乘权累加法3.1 基础实现乘权累加法将K进制数的每一位乘以对应的权重K的幂然后求和得到十进制值。#includeiostream#includestringusing namespace std;//1.普通范围intmain(){string str;//存放原始字符intK0;cinstrK;intsum0,base1;//负责计算十进制和基数intlenstr.length();//长度//从低位算到高位for(intilen-1;i0;i--){intthisway(str[i]0str[i]9?(str[i]-0):(str[i]-A10));sumthisway*base;base*K;}coutsumendl;return0;}//2.考虑溢出的情况longlongkToDecimal(string numStr,intbase){longlongresult0;longlongweight1;for(intinumStr.length()-1;i0;i--){intdigitcharToNum(numStr[i]);if(digit0||digitbase)return-1;// 非法字符resultdigit*weight;weight*base;}returnresult;}总结一下乘权累加法的步骤就是①从字符串读取K进制数string str;// K进制数如7BintK;// 进制基数如16cinstrK;②确定计算方向从低位到高位或从高位到低位计算从低位到高位权重 base 从 1 开始每次乘 K从高位到低位权重 base 从 K^(len-1) 开始每次除 K③遍历每一位并转换for(intilen-1;i0;i--){//字符转数字intthisway(str[i]0str[i]9?(str[i]-0):(str[i]-A10));sumthisway*base;//乘权累加base*K;//更新权重}3.2 处理负数和验证// 支持负数的K进制转十进制longlongkToDecimalWithSign(string numStr,intbase){// 1. 处理符号bool negativefalse;if(!numStr.empty()numStr[0]-){negativetrue;numStrnumStr.substr(1);// 去掉负号//substr(1) - 提取从下标1开始到字符串结尾的字符//string substr(size_t pos 0, size_t count npos) const;//substr(0,3) - 从下标为0的位置开始截取三个字符}// 2. 调用无符号版本进行转换longlongresultkToDecimal(numStr,base);// 3. 检查转换是否成功if(result-1)return-1;// 转换失败非法字符等// 4. 恢复符号returnnegative?-result:result;}4.任意进制间直接转换stringconvertBase(string numStr,intfromBase,inttoBase){// 先转十进制longlongdecimalkToDecimal(numStr,fromBase);if(decimal-1)returnERROR;// 再转目标进制returndecimalToK(decimal,toBase);}三、经典问题1. 日期类问题精选1.1 计算两个日期间的天数差// 使用公式法计算intmain(){inty1,m1,d1,y2,m2,d2;// 输入两个日期intdays1daysFromStart(y1,m1,d1);intdays2daysFromStart(y2,m2,d2);coutabs(days2-days1)endl;return0;}1.2 给定日期求星期几intmain(){intyear12021,month15,day11;intdayOfWeek6;// 参考日期2021-05-01为周六(6)intyear2,month2,day2;scanf(%d-%d-%d,year2,month2,day2);// 读取目标日期if(isBefore(year1,month1,day1,year2,month2,day2)){while(isBefore(year1,month1,day1,year2,month2,day2)){addOneDay(year1,month1,day1);dayOfWeek(dayOfWeek1)%7;// 向后推进星期}}elseif(isAfter(year1,month1,day1,year2,month2,day2)){while(isAfter(year1,month1,day1,year2,month2,day2)){subOneDay(year1,month1,day1);dayOfWeek(dayOfWeek-17)%7;// 向前退星期}}printf(%d,dayOfWeek);// 输出最终星期return0;}2. 进制转换问题2.1 十进制-十六进制stringtoHex(intnum){if(num0)return0;intnnum;// 处理负数string hex;//假设num 16 - 10(十六进制)while(n0){intdigitn%16;//0 0 1 0“ ”10“hex(digit10?char(0digit):char(adigit-10))hex;n/16;}returnhex;}