0Pricing
Coding Interview Prep · درس

حيلة الفرق بين أرقام الصفوف

طرح ROW_NUMBER من تسلسل لتجميع القيم المتتالية في جزر

حيلة الفرق بين أرقام الصفوف درس مجاني في Coding Interview Prep على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Coding Interview Prep، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.

مفتاح الجزيرة الأكثر أناقة

تُعد حيلة الفرق بين رقم الصف والقيمة التقنية التي يرغب المحاورون أكثر في رؤيتها عند التعامل مع جزر الأعداد الصحيحة أو التواريخ المتتالية. فهي تنشئ مفتاح المجموعة بعملية طرح واحدة، من دون الحاجة إلى LAG أو المجموع التراكمي.

الفكرة كلها هي طرح ROW_NUMBER من القيمة نفسها. ففي أي سلسلة من القيم المتتالية، تزداد كل من القيمة ورقم الصف بمقدار 1 تمامًا في كل خطوة، ولذلك يكون الفرق ثابتًا على امتداد السلسلة كلها. وهذا الثابت هو مفتاح الجزيرة.

لماذا يبقى الفرق ثابتًا

فكر في صفين متجاورين ضمن سلسلة متتالية. عند الانتقال من أحدهما إلى الآخر، تزداد القيمة بمقدار 1، ويزداد رقم الصف بمقدار 1. وعند طرحهما، يلغي أحد الزيادتين الأخرى، لذلك لا تتغير قيمة value - row_number.

لكن بمجرد ظهور فجوة، تقفز القيمة بأكثر من 1، بينما لا يرتفع رقم الصف إلا بمقدار 1. عندها ينتقل الفرق إلى ثابت جديد. وهذا الانتقال هو ما يفصل جزيرة عن الجزيرة التالية.

رؤية ذلك في بياناتنا

تذكّر أيام تسجيل الدخول 1، 2، 3، 7، 8، 10. لنضع رقم الصف والفرق جنبًا إلى جنب:

  • day 1, rn 1, diff 0
  • day 2, rn 2, diff 0
  • day 3, rn 3, diff 0
  • day 7, rn 4, diff 3
  • day 8, rn 5, diff 3
  • day 10, rn 6, diff 4

تقسم الفروق (0،0،0،3،3،4) الصفوف تمامًا إلى الجزر الثلاث. والفرق نفسه يعني الجزيرة نفسها.

SELECT
  day_no,
  ROW_NUMBER() OVER (ORDER BY day_no) AS rn,
  day_no - ROW_NUMBER() OVER (ORDER BY day_no) AS grp
FROM logins
ORDER BY day_no;

اختزال الصفوف إلى جزر

بعد استخدام الفرق مفتاحًا للمجموعة، يصبح الاستعلام النهائي عملية الاختزال القياسية. ضع الفرق داخل CTE ثم طبّق GROUP BY عليه:

يعيد هذا الجزر الثلاث نفسها كما في السابق، لكن SQL أقصر وأوضح من إصدار LAG مع المجموع التراكمي. وبالنسبة إلى التسلسلات الصحيحة أو متساوية الخطوة، فهذا هو الخيار الأول الذي ينبغي اللجوء إليه.

WITH keyed AS (
  SELECT
    day_no,
    day_no - ROW_NUMBER() OVER (ORDER BY day_no) AS grp
  FROM logins
)
SELECT
  MIN(day_no) AS start_day,
  MAX(day_no) AS end_day,
  COUNT(*)    AS length
FROM keyed
GROUP BY grp
ORDER BY start_day;

الفخ: يجب أن تتزايد القيم بمقدار واحد

تفترض حيلة الفرق البسيطة أن المتتالية تزداد بمقدار 1 تمامًا في كل خطوة. ينطبق ذلك على الأعداد الصحيحة المتتابعة بلا فجوات وأيام التقويم المتتالية، لكنه لا يعمل إذا كانت قيمك تتقدم بمقدار ثابت آخر أو إذا وُجدت قيم مكررة.

  • حتى القيم الزوجية 2,4,6,8 ستبدو كأنها تحتوي على فجوات عند طرح رقم الصف من القيمة.
  • تُربك القيم المكررة المحاذاة، لأن رقم الصف يستمر في الارتفاع بينما لا تتغير القيمة.

إن فهم هذا القيد وكيفية إصلاحه هو ما يميز بين حفظ حيلة وفهم حقيقي.

إصلاح المتتاليات ذات الخطوة الثابتة

إذا كانت القيم تتقدم بمقدار ثابت معلوم k بدلًا من 1، فطبّعها أولًا: اقسم القيمة على k (أو استخدم value / k للأعداد الصحيحة) لكي تصبح كل خطوة مساوية لـ 1 مجددًا، ثم اطرح رقم الصف.

على سبيل المثال، بالنسبة إلى الأعداد الزوجية التي تتقدم بمقدار 2، استخدم day_no / 2 - ROW_NUMBER(). ترتفع القيمة المطبعّة الآن بمقدار 1 لكل عنصر متتالٍ، فتستعيد خاصية الفرق الثابت.

SELECT
  val,
  (val / 2) - ROW_NUMBER() OVER (ORDER BY val) AS grp
FROM even_series
ORDER BY val;

تطبيقها على التواريخ

التواريخ هي الشكل الواقعي الأكثر شيوعًا. لا يمكن طرح تواريخ التقويم مباشرةً من رقم الصف، لذا حوّل التاريخ أولًا إلى عدد أيام. في Postgres، اطرح تاريخًا مرجعيًا ثابتًا لتحصل على عدد صحيح من الأيام، ثم طبّق الحيلة نفسها.

وبما أن أيام التقويم المتتالية يختلف بعضها عن بعض بمقدار 1، فإن الفرق بين عدد الأيام ورقم الصف يصبح ثابتًا مرة أخرى داخل الجزيرة.

WITH keyed AS (
  SELECT
    login_date,
    (login_date - DATE '2000-01-01')
      - ROW_NUMBER() OVER (ORDER BY login_date) AS grp
  FROM daily_logins
)
SELECT MIN(login_date) AS start_date,
       MAX(login_date) AS end_date,
       COUNT(*)        AS days_in_run
FROM keyed GROUP BY grp ORDER BY start_date;

حساب فروق التواريخ عبر لهجات SQL

تختلف خطوة تحويل التاريخ إلى عدد صحيح باختلاف المحرك، ويقدّر القائمون بالمقابلات الإلمام بلهجات SQL المختلفة:

  • Postgres: اطرح ثابتًا تاريخيًا: login_date - DATE '2000-01-01'، لتحصل على عدد صحيح.
  • MySQL: استخدم DATEDIFF(login_date, '2000-01-01').
  • SQL Server: استخدم DATEDIFF(day, '2000-01-01', login_date).

وهناك طريقة أكثر أناقة في بعض المحركات: اطرح عدد أيام ROW_NUMBER مباشرةً من التاريخ باستخدام حساب الفواصل الزمنية، ثم نفّذ GROUP BY على التاريخ المرجعي الناتج.

SELECT
  login_date,
  login_date - (ROW_NUMBER() OVER (ORDER BY login_date)
               * INTERVAL '1 day') AS grp_date
FROM daily_logins;

إضافة تقسيمات لكل مجموعة

للعثور على الجزر لكل مستخدم، قسّم رقم الصف بحسب عمود المجموعة. ومن الأهمية بمكان أن يتضمن مفتاح المجموعة عمود التقسيم أيضًا، لأن مستخدمين مختلفين قد ينتجان بالصدفة قيمة الفرق نفسها.

لذلك نفّذ GROUP BY على كل من user_id والفرق المحسوب. إن نسيان user_id في GROUP BY النهائي خطأ دقيق يحب القائمون بالمقابلات اكتشافه.

WITH keyed AS (
  SELECT user_id, day_no,
    day_no - ROW_NUMBER()
      OVER (PARTITION BY user_id ORDER BY day_no) AS grp
  FROM logins
)
SELECT user_id, MIN(day_no) AS start_day,
       MAX(day_no) AS end_day, COUNT(*) AS len
FROM keyed
GROUP BY user_id, grp
ORDER BY user_id, start_day;

الحيلة أم LAG: أيهما تستخدم

أصبح لديك الآن أسلوبان قويان في مجموعة أدواتك. اختر بينهما عن قصد:

  • فرق رقم الصف: الأقصر والأوضح للتتابعات ذات القيم المتساوية الخطوة (الأعداد الصحيحة المتتابعة، والتواريخ المتتالية). وهو الخيار الأول عندما يعني التجاور «الاختلاف بمقدار ثابت».
  • LAG مع المجموع التراكمي: أكثر مرونة عندما لا يكون التجاور خطوة عددية ثابتة، مثل «الحالة نفسها كما في الصف السابق» أو القواعد المخصصة غير المنتظمة.

اذكر اختيارك وسببه في المقابلة؛ فالتفكير المنطقي يترك انطباعًا أقوى من الصياغة البرمجية.

التعامل مع التكرارات بحذر

إذا كان من الممكن تكرار قيمة ما، وكنت لا تزال تريد جزيرة واحدة لكل سلسلة متتالية، فأزل التكرارات أولًا باستخدام DISTINCT أو خطوة تجميع، حتى يتوافق رقم الصف مع القيم واحدًا لواحد. ويمكنك بدلًا من ذلك استخدام DENSE_RANK بدلًا من ROW_NUMBER لكي تشترك القيم المتساوية في الرتبة نفسها.

اسأل القائم بالمقابلة دائمًا عما إذا كان من الممكن وجود تكرارات؛ فطريقة المعالجة الصحيحة تعتمد على ما إذا كان ينبغي للتكرارات أن تمدد السلسلة أو يتم تجاهلها فيها.

WITH d AS (SELECT DISTINCT day_no FROM logins)
SELECT day_no,
  day_no - ROW_NUMBER() OVER (ORDER BY day_no) AS grp
FROM d;

تحقق سريع

تأكد من فهمك لسبب نجاح الحيلة.

مراجعة: حيلة الفرق

أصبح لديك الآن أنظف مفتاح للجزيرة:

  • الصيغة الأساسية: value - ROW_NUMBER() OVER (ORDER BY value) ثابتة لكل سلسلة متتالية.
  • استخدم GROUP BY على الفرق للحصول على البداية والنهاية والطول.
  • بالنسبة إلى المتتاليات ذات الخطوة الثابتة، نفّذ التطبيع أولًا (بالقسمة على مقدار الخطوة).
  • بالنسبة إلى التواريخ، حوّلها إلى عدد صحيح من الأيام باستخدام دالة الفرق الخاصة بلهجة SQL.
  • لكل مجموعة: استخدم PARTITION BY مع رقم الصف، وأدرج عمود المجموعة في GROUP BY النهائي.
  • تعامل مع التكرارات باستخدام DISTINCT أو DENSE_RANK.

سنحوّل تركيزنا بعد ذلك من الجزر إلى المساحات الفارغة: العثور على الفجوات.

الأسئلة الشائعة

هل درس «حيلة الفرق بين أرقام الصفوف» مجاني؟

نعم — نص درس «حيلة الفرق بين أرقام الصفوف» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Coding Interview Prep، انتقل إلى CoddyKit PRO. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.

ماذا ستتعلم في «حيلة الفرق بين أرقام الصفوف»؟

طرح ROW_NUMBER من تسلسل لتجميع القيم المتتالية في جزر تتمرن على Coding Interview Prep مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Coding Interview Prep؟

لا تُشترط خبرة سابقة. Coding Interview Prep على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.

كم من الوقت يستغرق درس «حيلة الفرق بين أرقام الصفوف»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Coding Interview Prep هذا؟

نعم. كل درس في Coding Interview Prep يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. التعرف على مسألة الفجوات والجزر
  2. حيلة الفرق بين أرقام الصفوف
  3. العثور على الفجوات في تسلسل
  4. الجزر مع تغيرات التاريخ والحالة
← العودة إلى Coding Interview Prep