動態

詳情 返回 返回

算法的應用場景之尋找最近數&時間線組件錨點跳轉對應位置 - 動態 詳情

需求描述

  • 前兩天和以前公司的同事聊天,他提到在刷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...
user avatar guochenglong 頭像 yishidemeihao_5b9ce075877c9 頭像 witersen 頭像 chenychenyu 頭像 aresn 頭像 ysxq 頭像 minghuajiwu 頭像 yansudehai_ty9er 頭像 hello888 頭像 niumingxin 頭像 chenchaoyang666 頭像 chenqiwen 頭像
點贊 15 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.