[数据结构与算法] 输入当前是一周的第几天, 输出今天直到三天后分别都是星期几

算法之路

本系列随缘更新

第一章 [数据结构与算法] 邂逅数组与队列
第二章 [数据结构与算法] 邂逅链表
第三章 [数据结构与算法] 邂逅栈
第四章 [数据结构与算法] 排序算法
第五章 [数据结构与算法] 排序算法之冒泡排序与快速排序(快排)
第六章 [数据结构与算法] 排序算法之选择排序和堆排序
第七章 [数据结构与算法] 排序算法之直接插入排序与希尔排序
第八章 [数据结构与算法] 排序算法之归并排序与基数排序
第九章 [数据结构与算法] 查找算法
第十章 [数据结构与算法] 树结构之二叉树
第十一章 [数据结构与算法] 树结构之二叉排序树、平衡二叉树、多路查找树
第十二章 [数据结构与算法]赫夫曼树与赫夫曼编码
第十三章 [数据结构与算法] 图结构
第十四章 [数据结构与算法] 盘点工作中常用的算法
第十五章 [数据结构与算法] 输入当前是一周的第几天, 返回今天直到三天后分别都是星期几


输入当前是一周的第几天, 输出今天直到三天后分别都是星期几


一. 前言

对该问题进行抽象, 实际上就是是: 输入当前是星期几, 输出从今到几天后所有的星期数( 都是星期几 )
这个算法一种情况就是用于前端 在下拉选择时间框的时候, 设置几天内可预约时间
我们可以先将问题具体化: 输入当前是一周的第几天, 返回今天到3天之后分别都是星期几, 最后再进行抽象化
通过对问题的梳理, 来推敲简单算法实现的过程, 并举一反三对问题进行多方位思考


二. 分析

我们可以先将可能的情况写下来, 便于观察规律

//要求: 输入周几, 返回该天至该天后3天都是星期几
周一 1 2 3 4 
周二 2 3 4 5
周三 3 4 5 6
周四 4 5 6 7
周五 5 6 7 1
周六 6 7 1 2
周日 7 1 2 3

注意:

  1. 本例因为可能性较少因此进行了穷举
  2. 如果元素较少, 可以考虑穷举; 如果元素较多, 则按照自己的想法去列举, 直至能发现其中的规律

根据上面规律, 我们很容易发现

  • 当天在周一~周四, 天数介于 当天~当天+3 之间
  • 当天在周五~周日, 天数介于 当天~周末周一 ~ 当天-4 之间
//周一 ~ 周四很好理解
//周五 ~ 周日的情况需要想想办法了
周五 5 6 7 1      
周六 6 7 1 2		 
周日 7 1 2 3		

//首尾对应看下, 可以看到下面对应关系 并不能在数组 或者 list中 通过连续遍历 获得
5-->1
6-->2
7-->3
//因此, 我们需要人为的去构建这种连续, 比如
8-7=1
9-7=2
10-7=3
//而8,9,10 和5,6,7 有什么关系呢?
那就是前者等于后者+3!!! 而这个3 正好对应的是几天后(时间段)

根据上面的猜想, 我们能够较为快速的想到

  • 去构建一个 1 - 10连续的, 并且存放10个元素的数组
  • 然后去按顺序遍历这些数组, 当数组元素大于7时, 减去7 即可( 这里的思路很关键 )
    遍历的 开始是day-1 (作用是将当前星期几与上面数组建立联系, 数组下标从0开始)
    遍历的 结束是: day+2. (因为 day-1, day, day+1, day+2 正好是当前天数到3天后的星期数)
    判断遍历的结果, 小于7不变, 大于7 则直接 - 7 即可!!!

三. 实现

以上思路代码实现如下

    /**
     * 输入当前星期几, 输出当前到几天后的值
     * @return
     */
    public static List<Integer> getDayOfThreeDayAfter(int day) {
        //初始化数据
        Integer[] week = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        //动态初始化list
        List<Integer> list = new ArrayList<>();
        //四个时间段 d-1, d, d+1. d-2
        for (int i = day-1; i <= day+ 2; i++) {
            //判断遍历的结果, 小于7不变, 大于7 则直接 - 7
            list.add(week[i] > 7 ? week[i] - 7 : week[i]);
        }
        return list;
    }

    public static void main(String[] args) {
        /**
         * 要求: 输入周几, 返回该天至该天后3天都是星期几
         * 周一 1 2 3 4
         * 周二 2 3 4 5
         * 周三 3 4 5 6
         * 周四 4 5 6 7
         * 周五 5 6 7 1
         * 周六 6 7 1 2
         * 周日 7 1 2 3
         */
        for (int i = 1; i <= 7; i++) {
            System.out.println("getDayOfThreeDayAfter("+i+") = " + getDayOfThreeDayAfter(i));
        }

    }

测试结果
在这里插入图片描述

拓展

将当前方法进行抽象, 使其效果达到: 输入当前星期几, 以及时间间隔, 输出从当前到几天后的星期数(分别都是星期几)

核心注意点:

  • 数组初始后的容量设置和动态赋值
  • 数组遍历的初始值和结束值的设计
   /**
     * 输入当前星期几, 输出当前到几天后的值
     * @param today  今天星期几
     * @param days  几天后
     * @return
     */
    public static List<Integer> getDaysOfThreeDayAfter(int today, int days) {
        //初始化数组
        Integer[] week = new Integer[7+days];
        for (int j = 0; j < 7 + days ; j++) {
            week[j] = j + 1;
        }
        //动态初始化list
        List<Integer> list = new ArrayList<>();
        //四个时间段 d-1, d, d+1. d-2
        for (int i = today - 1; i <= today + days-1; i++) {
            //判断遍历的结果, 小于7不变, 大于7 则直接 - 7
            list.add(week[i] > 7 ? week[i] - 7 : week[i]);
        }
        return list;
    }

    public static void main(String[] args) {
        for (int i = 1; i <= 7; i++) {
            System.out.println("getDaysOfThreeDayAfter(" + i + ") = " + getDaysOfThreeDayAfter(i, 4));
        }

测试结果
在这里插入图片描述
我们回顾下上面代码的实现思路

1.去构建一个 1 - 10 ,连续的, 存放10个元素的数组
2. 然后去按顺序遍历这些数组, 当数组元素大于7时, 减去7 即可( 这里的思路很关键 )
遍历的 开始是day-1 (作用是将当前星期几与上面数组简历联系, 数组下标从0开始)
遍历的 结束是: day+2. (因为 day-1, day, day+1, day+2 正好是当前天数到3天后的星期数)
判断遍历的结果, 小于7不变, 大于7 则直接 - 7 即可!!!

灵光一闪

反向思考
既然上面的方式核心是在list 初始化后遍历取值的时候, 值的变化(小于7不变, 大于7 则直接 - 7)
那么我们为什么不可以将核心转移到 数组初始化后动态赋值的时候呢.
这样我们后面仅需要确定遍历的起始下班和结束下标即可

以上思考的思路如下:

  • 构建一个包含1-10连续的, 存放10个元素的数组, 判断每个元素的值是否大于7, 大于7则-7, 小于7则不变
  • 然后去按顺序遍历这些数组,
    遍历的 开始是day-1 (作用是将当前星期几与上面数组简历联系, 数组下标从0开始)
    遍历的 结束是day+2. (因为 day-1, day, day+1, day+2 正好是当前天数到3天后的星期数)

推广到该天到任意天数之间的星期数

  • 构建一个包含1-7+days (days为时间段) 连续7+days 个元素的数组, 判断每个元素的值是否大于7, 大于7则-7, 小于7则不变
  • 然后去按顺序遍历这些数组,
    遍历的 开始是day-1 (作用是将当前星期几与上面数组简历联系, 数组下标从0开始)
    因为 days=3, 结束坐标=today+3 -1 ;
    days=4 ,结束坐标=today+4-1;
    days=5, 结束坐标=today+5-1
    遍历的 结束坐标是: today + days-1.
    /**
     * 拓展方式的另一种变种, 就是在初始化时, 就将数据初始好, 在list动态初始化时直接遍历即可
     * 输入当前星期几, 输出当前到几天后的值
     * @param today  今天星期几
     * @param days  几天后
     * @return
     */
    public static List<Integer> getDaysOfThreeDayAfter2(int today, int days) {
        //初始化数组
        Integer[] week = new Integer[7+days];
        for (int j = 0; j < 7 + days ; j++) {
            //在数组初始化时, 判断j+1的值和7的关系: 小于7不变, 大于7 则直接 - 7
            week[j] = (j + 1) > 7 ? (j + 1) - 7 : j + 1;
        }
        //动态初始化list
        List<Integer> list = new ArrayList<>();
        //四个时间段 d-1, d, d+1. d-2
        for (int i = today - 1; i <= today + days-1; i++) {
            list.add(week[i]);
        }
        return list;
    }

    public static void main(String[] args) {
        for (int i = 1; i <= 7; i++) {
            System.out.println("getDaysOfThreeDayAfter2(" + i + ") = " + getDaysOfThreeDayAfter2(i, 4));
        }

    }

在这里插入图片描述


补充: 建立星期和日期的映射, 用于在进行遍历时, 根据所属星期几设置当前时间

    /**
     * 输入当前星期几, 返回几天后的星期数与对应日期数
     * 注意: 不能超过7天, 即days不能 >=6
     * @param today
     * @param days
     * @return
     */
    public static Map<Integer, String> getDaysOfThreeDayAfterAndDate(int today, int days) {
        //初始化数组
        Integer[] week = new Integer[7 + days];
        for (int j = 0; j < 7 + days; j++) {
            //在数组初始化时, 判断j+1的值和7的关系: 小于7不变, 大于7 则直接 - 7
            week[j] = (j + 1) > 7 ? (j + 1) - 7 : j + 1;
        }
        //动态初始化list
        List<Integer> list = new ArrayList<>();
        //初始化Map 用户存放当前日期
        Map<Integer, String> dateMap = new LinkedHashMap<>(16);
        //四个时间段 d-1, d, d+1. d-2
        for (int i = today - 1, k = 0; i <= today + days - 1; i++, k++) {
            list.add(week[i]);
            dateMap.put(week[i], LocalDateTime.now().toLocalDate().plusDays(k).toString());
        }
        return dateMap;
    }

总结

  • 个人认为, 相比拓展中 getDaysOfThreeDayAfter对list 初始化后动态赋值并进行判断,
    灵光一闪中 getDaysOfThreeDayAfter2 对数组初始化后动态赋值更容易接受一些.
    因为这样做将最难的问题, 赋值问题在一开始就解决, 剩下我们只需注意遍历时的坐标即可
  • 从之前学习算法的经验以及自己造轮子(虽然比较简单)的经历可以体会到.
    最核心问题是算法设计思路, 其次就是对数据进行赋值(数组初始化容量, 动态赋值)遍历(起始和结束下标) .
    只要这些点能够掌握, 算法就可能没有想象中的那么简单
  • 之前从某位大佬那里学习到. 要从战略上藐视技术, 从战术上重视技术.
    究其根本原因就是, 其实技术本来就是由繁入简.
    最为难以搞定的机器语言, 汇编语言已经由业界前辈们为我们封装成黑盒代码, 我们直接调用对应api就可以使用了.
    并且通过各种语言成熟的框架借以让更多人去轻松掌握技术, 从事技术, 进而推动业务发展, 适应更加复杂的业务情况
    但是如果不去多多学习新技术, 去提高我们的开发效率, 我们就只能故步自封. 很可能无法应对今后的工作环境
  • 因此无论多难的问题, 对其进行拆分, 然后对其各个击破, 最后几乎都能将其解决.
    这也正契合大数据技术中 分而治之 的思想.
  • 私以为, 之所以都推荐学习底层源码和原理或者算法等原因是:
    方便我们更好的理解技术/框架, 解决平时无法通过常规方式来解决的问题

万变不离其宗, 多多反思, 努力学习和体会技术发展的本质!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时间静止不是简史

感谢你的肯定, 我将继续努力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值