0Pricing
Coding Interview Prep · บทเรียน

ตรวจจับวันที่ในปฏิทินที่ต่อเนื่องกัน

ใช้การคำนวณวันที่และหมายเลขแถวเพื่อค้นหาช่วงวันที่ที่ไม่ขาดตอน

ตรวจจับวันที่ในปฏิทินที่ต่อเนื่องกัน เป็นบทเรียน Coding Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Coding Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน

โจทย์การสัมภาษณ์

ผู้สัมภาษณ์ชอบคำถามเกี่ยวกับช่วงต่อเนื่อง เพราะคำถามเหล่านี้แสดงให้เห็นว่าคุณเข้าใจ ฟังก์ชันหน้าต่างและการคำนวณวันที่อย่างแท้จริงหรือไม่ โจทย์ทั่วไปคือ "จากตารางวันที่ผู้ใช้เข้าสู่ระบบ ให้หาช่วงวันที่ในปฏิทินที่ต่อเนื่องกันโดยไม่มีวันขาด"

สัญชาตญาณแรกอาจเป็นการเชื่อมตารางกับตัวเองเพื่อเปรียบเทียบแต่ละแถวกับแถวถัดไป แต่วิธีนี้ทำงานได้แย่ลงอย่างมากเมื่อตารางมีขนาดใหญ่และเขียนได้ไม่สะดวก คำตอบแบบมืออาชีพใช้เทคนิค ช่องว่างและกลุ่มช่วงต่อเนื่อง ในบทเรียนนี้คุณจะเรียนรู้วิธีตรวจหาวันที่ต่อเนื่องอย่างเป็นระเบียบด้วยหมายเลขแถวและการลบวันที่

ข้อมูลตัวอย่าง

ตลอดบทเรียนนี้ เราใช้ตาราง logins ที่มีหนึ่งแถวต่อผู้ใช้ต่อวันที่ผู้ใช้นั้นใช้งานอยู่ โดยถือว่ากำจัดรายการซ้ำไว้แล้ว (หนึ่งครั้งที่เข้าสู่ระบบต่อหนึ่งวันในปฏิทิน)

  • user_id — ผู้ที่เข้าสู่ระบบ
  • login_date — ค่าประเภท DATE

สำหรับผู้ใช้ 1 วันที่คือ 1, 2, 3 มกราคม จากนั้นมีช่องว่าง แล้วจึงเป็น 6, 7 มกราคม เราคาดว่าจะได้สองช่วงต่อเนื่อง: ช่วงยาว 3 วันและช่วงยาว 2 วัน

SELECT * FROM logins ORDER BY user_id, login_date;
-- user_id | login_date
--    1    | 2024-01-01
--    1    | 2024-01-02
--    1    | 2024-01-03
--    1    | 2024-01-06
--    1    | 2024-01-07

แนวคิดหลัก

นี่คือเคล็ดลับที่ช่วยไขปัญหาเรื่องวันที่ต่อเนื่องทุกแบบ หากคุณเรียงแถวตามวันที่และกำหนด หมายเลขแถว ให้แต่ละแถวตามลำดับ สำหรับช่วงวันที่ต่อเนื่องใด ๆ ผลต่างระหว่างวันที่กับหมายเลขแถวจะคงที่

เพราะทั้งวันที่และหมายเลขแถวเพิ่มขึ้นทีละ 1 ในแต่ละวันที่ต่อเนื่อง ผลต่างจึงไม่เปลี่ยนแปลง เมื่อเกิดช่องว่าง วันที่จะกระโดดไปข้างหน้า แต่หมายเลขแถวไม่เปลี่ยนตาม ทำให้ค่าคงที่สิ้นสุดลงและเริ่มกลุ่มใหม่

มองเห็นผลต่าง

ลองไล่ดูด้วยมือสำหรับผู้ใช้ 1 โดย ROW_NUMBER จะนับเป็น 1, 2, 3, 4, 5 ให้ลบหมายเลขแถวในฐานะจำนวนวันออกจากวันที่ แล้วสังเกตผลลัพธ์

  • 1 มกราคม − 1 = 31 ธันวาคม
  • 2 มกราคม − 2 = 31 ธันวาคม
  • 3 มกราคม − 3 = 31 ธันวาคม
  • 6 มกราคม − 4 = 2 มกราคม
  • 7 มกราคม − 5 = 2 มกราคม

สามแถวแรกมีค่า 31 ธันวาคมร่วมกัน ส่วนสองแถวสุดท้ายมีค่า 2 มกราคมร่วมกัน ค่าอ้างอิงร่วมนี้คือ คีย์กลุ่ม ของเรา

การเพิ่ม ROW_NUMBER

ขั้นตอนแรกที่ลงมือทำคือการเพิ่มหมายเลขแถว โดยแบ่งกลุ่มตามผู้ใช้ เพื่อไม่ให้ช่วงต่อเนื่องข้ามขอบเขตของผู้ใช้ และเรียงลำดับตามวันที่

PARTITION BY user_id จะเริ่มตัวนับใหม่สำหรับผู้ใช้แต่ละราย ส่วน ORDER BY login_date รับประกันว่าลำดับจะเป็นไปตามปฏิทิน

SELECT
  user_id,
  login_date,
  ROW_NUMBER() OVER (
    PARTITION BY user_id
    ORDER BY login_date
  ) AS rn
FROM logins;

การคำนวณค่าอ้างอิงของกลุ่ม

ตอนนี้ให้ลบจำนวนวันตามค่า rn ออกจาก login_date ใน PostgreSQL คุณสามารถลบจำนวนเต็มของวันออกจากวันที่ได้โดยตรง ผลลัพธ์คือค่าอ้างอิงคงที่ที่ใช้ระบุแต่ละกลุ่มช่วงต่อเนื่อง

โปรดสังเกตว่าเราไม่สามารถอ้างอิงชื่อแทน rn ใน SELECT เดียวกับที่กำหนดชื่อนั้นได้ ดังนั้นจึงต้องห่อคำสั่งก่อนหน้าด้วย CTE หรือแบบสอบถามย่อยก่อน

WITH numbered AS (
  SELECT
    user_id,
    login_date,
    ROW_NUMBER() OVER (
      PARTITION BY user_id ORDER BY login_date
    ) AS rn
  FROM logins
)
SELECT
  user_id,
  login_date,
  login_date - rn AS grp
FROM numbered;

การจัดกลุ่มช่วงต่อเนื่อง

เมื่อมีค่าอ้างอิงแล้ว ทุกช่วงต่อเนื่องจะมีค่า grp เดียวกัน ให้จัดกลุ่มตาม user_id และ grp จากนั้นรวมค่าเพื่อหาวันเริ่มต้น วันสิ้นสุด และความยาวของแต่ละช่วง

  • MIN(login_date) — วันแรกของช่วงต่อเนื่อง
  • MAX(login_date) — วันสุดท้ายของช่วงต่อเนื่อง
  • COUNT(*) — จำนวนวันในช่วงต่อเนื่อง
WITH numbered AS (
  SELECT user_id, login_date,
    ROW_NUMBER() OVER (
      PARTITION BY user_id ORDER BY login_date
    ) AS rn
  FROM logins
)
SELECT
  user_id,
  MIN(login_date) AS streak_start,
  MAX(login_date) AS streak_end,
  COUNT(*)        AS streak_len
FROM numbered
GROUP BY user_id, login_date - rn
ORDER BY user_id, streak_start;

ความแตกต่างระหว่างรูปแบบภาษา

ไวยากรณ์สำหรับการคำนวณวันที่แตกต่างกันไป ควรกล่าวถึงเรื่องนี้ในการสัมภาษณ์เพื่อแสดงให้เห็นว่าคุณมีความรู้ครอบคลุม

  • PostgreSQL: login_date - rn (ลบจำนวนเต็มของวันออกจากวันที่)
  • MySQL: DATE_SUB(login_date, INTERVAL rn DAY)
  • SQL Server: DATEADD(day, -rn, login_date)

ตรรกะเหมือนกันทั้งหมด มีเพียงชื่อฟังก์ชันที่เปลี่ยนไป แบบจำลองทางความคิดที่ใช้ได้กับทุกระบบคือ "เลื่อนวันที่แต่ละรายการย้อนกลับตามตำแหน่งของมัน เพื่อให้ช่วงต่อเนื่องที่สมบูรณ์รวมกันเป็นค่าคงที่เดียว"

-- SQL Server version of the anchor
DATEADD(day, -1 * rn, login_date) AS grp

เหตุใดจึงไม่เชื่อมตารางกับตัวเอง

ผู้สัมภาษณ์อาจถามว่าทำไมคุณจึงหลีกเลี่ยงการเชื่อมตารางกับตัวเอง เช่น l1.login_date = l2.login_date + 1 เหตุผลที่ควรตอบมีดังนี้:

  • การเชื่อมตารางกับตัวเองทดสอบได้เพียง ความติดกัน ไม่ใช่ช่วงต่อเนื่องทั้งหมด การประกอบช่วงต่อเนื่องให้ครบยังต้องจัดกลุ่มอยู่ดี
  • อาจทำให้จำนวนแถวเพิ่มเป็นทวีคูณ และมีความซับซ้อน O(n²) หากไม่มีดัชนีที่ดี
  • วิธีใช้หมายเลขแถวเป็นการประมวลผลแบบเรียงลำดับรอบเดียว จึงรองรับการขยายได้ดีกว่ามาก

ฟังก์ชันหน้าต่างคือคำตอบสมัยใหม่ที่คาดหวังสำหรับปัญหาเหล่านี้

การป้องกันรายการซ้ำ

เทคนิคทั้งหมดนี้ถือว่ามี หนึ่งแถวต่อผู้ใช้ต่อวัน หากแหล่งข้อมูลมีการเข้าสู่ระบบหลายครั้งในวันเดียวกัน สองแถวที่มีวันที่เดียวกันจะได้รับหมายเลขแถวต่างกัน ซึ่งทำให้ค่าอ้างอิงผิดพลาด

ป้องกันปัญหาด้วยการกำจัดรายการซ้ำก่อน โดยแปลงค่าประทับเวลาเป็นวันที่แล้วใช้ DISTINCT หรือใช้ DENSE_RANK กับวันที่แทน ROW_NUMBER เพื่อให้วันที่เท่ากันมีหมายเลขเดียวกัน

WITH days AS (
  SELECT DISTINCT user_id, login_ts::date AS login_date
  FROM raw_logins
)
SELECT * FROM days;

คำตอบฉบับสมบูรณ์

เมื่อนำทุกส่วนมารวมกัน จะได้คำตอบที่กระชับและพร้อมใช้ในการสัมภาษณ์ ซึ่งแสดงช่วงวันที่ต่อเนื่องแต่ละช่วงพร้อมวันเริ่มต้น วันสิ้นสุด และความยาว

โครงร่างเดียวกันนี้ — กำจัดรายการซ้ำ กำหนดหมายเลข ลบค่า และจัดกลุ่ม — แก้ปัญหาเกี่ยวกับ "ความต่อเนื่อง" ได้เกือบทุกแบบที่คุณอาจได้รับ

WITH days AS (
  SELECT DISTINCT user_id, login_ts::date AS login_date
  FROM raw_logins
),
numbered AS (
  SELECT user_id, login_date,
    ROW_NUMBER() OVER (
      PARTITION BY user_id ORDER BY login_date
    ) AS rn
  FROM days
)
SELECT user_id,
  MIN(login_date) AS streak_start,
  MAX(login_date) AS streak_end,
  COUNT(*)        AS streak_len
FROM numbered
GROUP BY user_id, login_date - rn
ORDER BY user_id, streak_start;

ตรวจสอบความเข้าใจ

ทดสอบความเข้าใจเคล็ดลับหลักของคุณ

สรุป

คุณได้เรียนรู้รูปแบบพื้นฐานสำหรับวันที่ต่อเนื่องแล้ว:

  • กำจัดรายการซ้ำให้เหลือหนึ่งแถวต่อผู้ใช้ต่อวัน
  • ROW_NUMBER เรียงตามวันที่และแบ่งกลุ่มตามผู้ใช้
  • ลบหมายเลขแถวออกจากวันที่เพื่อให้ได้ค่าอ้างอิงคงที่สำหรับแต่ละช่วงต่อเนื่อง
  • GROUP BY ค่าอ้างอิงแล้วรวมค่าเพื่อหาวันเริ่มต้น วันสิ้นสุด และความยาว

โครงร่างช่องว่างและกลุ่มช่วงต่อเนื่องนี้รองรับการประมวลผลรอบเดียวและมีประสิทธิภาพดีกว่าการเชื่อมตารางกับตัวเอง บทถัดไปคุณจะใช้โครงร่างนี้เพื่อคำนวณช่วงต่อเนื่องที่ยาวที่สุดสำหรับผู้ใช้แต่ละราย

คำถามที่พบบ่อย

บทเรียน “ตรวจจับวันที่ในปฏิทินที่ต่อเนื่องกัน” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ตรวจจับวันที่ในปฏิทินที่ต่อเนื่องกัน” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Coding Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ตรวจจับวันที่ในปฏิทินที่ต่อเนื่องกัน”

ใช้การคำนวณวันที่และหมายเลขแถวเพื่อค้นหาช่วงวันที่ที่ไม่ขาดตอน คุณปฏิบัติ Coding Interview Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Coding Interview Prep หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Coding Interview Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน

บทเรียน “ตรวจจับวันที่ในปฏิทินที่ต่อเนื่องกัน” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Coding Interview Prep นี้ได้ไหม

ได้ บทเรียน Coding Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. ตรวจจับวันที่ในปฏิทินที่ต่อเนื่องกัน
  2. ช่วงต่อเนื่องที่ยาวที่สุดของผู้ใช้แต่ละคน
  3. แถวต่อเนื่อง N แถวที่ตรงตามเงื่อนไข
  4. ช่วงต่อเนื่องที่ยังดำเนินอยู่จนถึงวันนี้
← กลับไปที่ Coding Interview Prep