需求描述

  • 前两天和以前公司的同事聊天,他提到在刷leetcode,并提到算法感觉不接地气
  • 似乎也没有什么应用场景
  • 于是乎,笔者翻出一个案例需求给到他
  • 看完以后,他感叹道:
  • 还是自己见得少了...

需求:假设有一个时间线组件,按照时间顺序展示对应内容,如下图

  • 这个时间线可能很长,能有好几个月
  • 为了提高用户体验,当用户进入这个页面的时候
  • 我们需要看看当天是哪一天,同时滚动到,距离时间线节点的最近的那一天
  • 省的用户再去滑动了
  • 这个问题需求,可以抽象成为一个算法题目
  • 数组中寻找与某个数最近的那一项

效果图

假设今天是25年1月16日,所以,应该滚动到 内容666、2025-01-16日哪一项

  • 滚动我们知道,使用锚点即可
  • 但是我们需要知道如何滚动到最近的一天
  • 所以,题目抽象成一道简单的入门算法题
  • 查找最近的项

问题简化算法

  • 假设有一组数组,为let arr = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
  • 现在出来一个任意数字,假设是51
  • 我们需要找距离这个数字最近的一项是哪一项

解法一 循环求差值数组,并排序取最小的那一项

let arr = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
let an = 51

function findRecentNumInfo(arr, an) {
    // 定义数组,求出每个值与目标值的差值,并存储起来
    let infoArr = []
    for (let i = 0; i < arr.length; i++) {
        // 这里存储的是对象,包含索引、值、差值
        infoArr.push({
            index: i,
            value: arr[i],
            // 差值可能是负数,所以取绝对值
            diff: Math.abs(arr[i] - an)
        })
    }
    // 再排序一下,找到差值最小的,即为第0项,第一个
    infoArr.sort((a, b) => a.diff - b.diff)
    // 正序排序完毕,把第一项给返回出去即可
    return infoArr[0]
}
console.log(findRecentNumInfo(arr, an));
  • 上述解法,相当于循环了两次
  • 取差值一次,排序一次
  • 事实上,我们还有更优解
  • 只要循环一次即可

解法二 单次循环比较差值大小,并直接取最小项

let arr = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
let an = 51

function findRecentNumInfo(arr, an) {
    // 这里存储的是对象,包含索引、值、差值
    let info = {
        index: +Infinity,
        value: +Infinity,
        diff: +Infinity,
    };
    // 通过对比,若差值小于之前的差值,则更新差值和索引对应项
    for (let i = 0; i < arr.length; i++) {
        if (Math.abs(arr[i] - an) < info.diff) {
            info.diff = Math.abs(arr[i] - an);
            info.index = i
            info.value = arr[i]
        }
    }
    // 一次遍历完毕,即可找到距离最近的对应项,返回出去即可
    return info
}
console.log(findRecentNumInfo(arr, an));

毫无疑问,这种方式为更优解

代码示例

html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        let arr = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
        let an = 51

        /**
         * 方式一:直接对比,找到最接近的值
         * */
        function findRecentNumInfo(arr, an) {
            // 这里存储的是对象,包含索引、值、差值
            let info = {
                index: +Infinity,
                value: +Infinity,
                diff: +Infinity,
            };
            // 通过对比,若差值小于之前的差值,则更新差值和索引对应项
            for (let i = 0; i < arr.length; i++) {
                if (Math.abs(arr[i] - an) < info.diff) {
                    info.diff = Math.abs(arr[i] - an);
                    info.index = i
                    info.value = arr[i]
                }
            }
            // 一次遍历完毕,即可找到距离最近的对应项,返回出去即可
            return info
        }

        /**
         *  方式二:先存储差值数组,再找到最近的值
         * */
        // function findRecentNumInfo(arr, an) {
        //     // 定义数组,求出每个值与目标值的差值,并存储起来
        //     let infoArr = []
        //     for (let i = 0; i < arr.length; i++) {
        //         // 这里存储的是对象,包含索引、值、差值
        //         infoArr.push({
        //             index: i,
        //             value: arr[i],
        //             // 差值可能是负数,所以取绝对值
        //             diff: Math.abs(arr[i] - an)
        //         })
        //     }
        //     // 再排序一下,找到差值最小的,即为第0项,第一个
        //     infoArr.sort((a, b) => a.diff - b.diff)
        //     // 正序排序完毕,把第一项给返回出去即可
        //     return infoArr[0]
        // }

        console.log(findRecentNumInfo(arr, an));
    </script>
</body>

</html>

vue

  • 应用到需求上就是
  • 找到最近的时间线组件的dom节点
  • 执行el.scrollIntoView({ behavior: "smooth" });即可
  • 以下的{ content: "内容111", timestamp: "2025-01-11", }
  • 大家可以自行更改时间即可
  • 复制粘贴即用
<template>
  <div class="box">
    <el-timeline style="max-width: 600px">
      <el-timeline-item
        v-for="(activity, index) in activities"
        :key="index"
        :timestamp="activity.timestamp"
      >
        {{ activity.content }}
      </el-timeline-item>
    </el-timeline>
  </div>
</template>

<script setup>
import { ref, onMounted } from "vue";
const activities = ref([
  {
    content: "内容111",
    timestamp: "2025-01-11",
  },
  {
    content: "内容222",
    timestamp: "2025-01-12",
  },
  {
    content: "内容333",
    timestamp: "2025-01-13",
  },
  {
    content: "内容444",
    timestamp: "2025-01-14",
  },
  {
    content: "内容555",
    timestamp: "2025-01-15",
  },
  {
    content: "内容666",
    timestamp: "2025-01-16",
  },
  {
    content: "内容777",
    timestamp: "2025-01-17",
  },
  {
    content: "内容888",
    timestamp: "2025-01-18",
  },
  {
    content: "内容999",
    timestamp: "2025-01-19",
  },
  {
    content: "内容10",
    timestamp: "2025-01-20",
  },
  {
    content: "内容11",
    timestamp: "2025-01-21",
  },
  {
    content: "内容12",
    timestamp: "2025-01-22",
  },
  {
    content: "内容13",
    timestamp: "2025-01-23",
  },
]);

onMounted(() => {
  scrollFn(1);
  addTimestamp();
});

const addTimestamp = () => {
  // 给每一项都添加时间戳
  activities.value.forEach((item) => {
    item.s = new Date(item.timestamp).getTime();
  });
  calRecent();
};

const calRecent = () => {
  let now = new Date().getTime();
  let res = findRecentInfo(activities.value, now);
  let n = res.index + 1;
  setTimeout(() => {
    scrollFn(n);
  }, 2000);
};

const findRecentInfo = (arr, an) => {
  // 存一下差值,看谁的距离近一些
  let info = {
    index: +Infinity,
    value: +Infinity,
    diff: +Infinity,
  };
  // 通过对比,若差值小于之前的差值,则更新差值和索引(直接找到差值最小的)
  for (let i = 0; i < arr.length; i++) {
    if (Math.abs(arr[i].s - an) < info.diff) {
      info.diff = Math.abs(arr[i].s - an);
      info.index = i;
      info.value = arr[i];
    }
  }
  return info;
};

const scrollFn = (n) => {
  const el = document.querySelector(`.el-timeline-item:nth-child(${n})`);
  el.scrollIntoView({ behavior: "smooth" });
};
</script>

<style>
.box {
  box-sizing: border-box;
  zoom: 2;
  padding: 12px 0px;
}
</style>
A good memory is better than a bad pen. Record it down...

水冗水孚
1.1k 声望595 粉丝

每一个不曾起舞的日子,都是对生命的辜负