דוקטור קוד

לצפייה
הסבה לתכנות אונלייןבוטקמאפ פרונטליקורסים בתכנותעובדים במיקור חוץייעוץ טכנולוגי לחברותבלוגצור קשר

הטעויות הנפוצות בראיונות עבודה למפתחי תוכנה

עודכן לאחרונה: דצמבר 30, 2024. נושאי המאמר: הכנה טכנית, שפת גוף, שאלות נפוצות, תרגול ליטקוד, התמודדות עם לחץ.

כותב: דוקטור קוד

interview mistakes

ראיון עבודה הוא כמו דייט ראשון. אתה מתרגש, רוצה להותיר רושם טוב, ואולי חושש לטעות שתהרוס הכול. מפתחי תוכנה, מוכשרים ככל שיהיו, נוטים לפעמים ליפול למלכודות קטנות שמורידות את הסיכויים שלהם לקבל את העבודה הנחשקת. חלקן נובעות מחוסר הכנה, אחרות מהתרגשות יתר, ולעיתים פשוט מחוסר הבנה של מה המראיין באמת מחפש.

אז בואו נצלול יחד לכמה מהטעויות הנפוצות ביותר ונראה איך להימנע מהן, תוך שימוש בדוגמאות ואף שאלות שמראיינים אוהבים לשאול:

10 טעויות הכי נפוצות בראיונות עבודה

1. איחור או הגעה מוקדמת מדי: האיזון המושלם

איחור לראיון עבודה הוא כמו לדפוק בדלת של לקוח בחליפה אחרי שהסרט נגמר. זה לא רק גורם לך להיראות לא מקצועי – זה משדר זלזול בזמן של המראיין. מצד שני, הגעה מוקדמת מדי, נניח חצי שעה לפני, עלולה לגרום למראיין אי-נוחות כי הוא לא מוכן עדיין, כמה פעמים מרואיינים הגיעו לראיון שקבעתי להם גם 45 דקות לפני הזמן, זה פשוט יוצר תחושה מעט לא נעימה, הרי אתה לא רוצה שהמרואיין יראה כבר על ההתחלה את כל הבלאגן שיש בחברה :).

אני זוכר מקרה שבו מועמד הגיע לראיון שלי באיחור של 15 דקות, ובמקום להתנצל, הוא אמר: "פקקים, אתה יודע איך זה". אמנם כולנו מבינים שפקקים קורים, אבל עדיף היה פשוט לשלוח הודעה בדרך?
יש פתרון פשוט חברים, אתם מאחרים? תרימו טלפון ותשלחו הודעה זה לא עולה כסף.

2. לא בודקים שום דבר על החברה לפני שמגיעים

תארו לכם שהוזמנתם לראיון עבודה, בתחום הפינטק למשל, ואתם מגיעים ואין לכם בכלל מושג מה זה פינטק או מה זה בכלל מניות וכסף.
המינימום שאתם צריכים לעשות שכבר הזמינו אתכם לראיון עבודה, זה לברר על המקום מספר פרטים, מי האנשים שעובדים שם, מי המנהלים, האם הכל בסדר עם החברה הזו? היא גייסה הון? היא בידיוק עברה פיטורים? כדאי לדעת את הדברים האלו.

פעם ראיינתי מועמד לתפקיד מפתח Backend בחברת SaaS, והוא פתח את הראיון בשאלה: "אז מה בעצם אתם עושים?". זה מיד הדליק נורה אדומה. התכוננות מינימלית, כמו לקרוא את דף ה"אודות" באתר החברה או לבדוק אילו טכנולוגיות הם משתמשים, יכולה לעשות הבדל עצום. מועמד שמתחיל את הראיון במשפט כמו: "ראיתי שאתם עובדים עם AWS ו-Docker, ואני אוהב לעבוד עם Kubernetes – יש מקום לשלב את זה אצלכם?" משדר רצינות והתעניינות.

יותר מזה, בעבר עבדתי בתחום ה fintech ואני זוכר אדם שהגיע לראיון עבודה, התחלתי לשוחח איתו, על התפקיד והטכנולוגיות ואחרי חצי שעה, שהסברתי לו על המוצר, הוא קם ואמר לי סליחה אני לא רוצה לעבוד בתחום הזה, זה כאילו, אנשים שפשוט נשרפים לא רק במקום העבודה הזה, זה מראה לך שהוא גם לא הקשיב שדיברה איתו המגייסת והסבירה על החברה וגם לא היה אכפת לו להכנס לבדוק שנייה בגוגל על מי מדובר, מעציב ומעליב.

3. דוגמאות ממוקדות: להפוך את ההצהרות שלכם למוחשיות

כשמועמדים אומרים דברים כלליים כמו "אני מאוד טוב בעבודת צוות", זה נשמע כמו קלישאה ריקה. לעומת זאת, מועמד שסיפר לי איך פעם הצוות שלו נכשל ב-Deployment חשוב והוא לקח על עצמו לכתוב סקריפט חדש שהציל את המצב – מיד תפס את תשומת ליבי.

סיפור כזה לא רק ממחיש כישורים טכניים, אלא גם מראה אופי יוזם ויכולת עבודה בתנאי לחץ. במקום להגיד "אני טוב בפתרון בעיות", ספרו על מקרה שבו פתרתם באג קשה במיוחד או עזרתם לצוות להתגבר על תקלה.

4. דיבור שלילי על מקומות עבודה קודמים!

interview mistakes

אל תעשו את זה!!

במהלך ראיון אחד, מועמד התחיל להסביר למה עזב את מקום העבודה הקודם: "היה לי מנהל שלא מבין כלום, והצוות היה כישלון". גם אם הדברים נכונים, הם מעמידים אתכם באור שלילי. דיבור כזה גורם למראיין לתהות איך תתארו את החברה שלהם בריאיון הבא שלכם.

במקום זה, נסו להסביר את המצב בצורה אובייקטיבית: "החברה לא הייתה ממוקדת טכנולוגית, ואני מחפש מקום עם יותר אתגרים בתחום הזה". כך תציגו את הסיבה לעזיבה בלי לשרוף גשרים.

ונסו גם לא להיות מתנשאים, זה לא נותן כלום גם אם יש לכם 19 שנות ניסיון ואתם חושבים שאתם החכמים הכי גדולים בעולם, תמיד יש מישהו אחד חכם יותר מכם, אז תחסכו התנשאויות ולכלוך על חברות קודמות, כן, כל חברה!

5. שפת גוף ודיבור: תקשורת היא הכול

שפת גוף היא כמו ה-UI של האדם. אפשר שיהיה לכם Backend חזק, אבל אם ה-UI מקרטע, המשתמשים (המראיינים) יינטשו.

פעם ראיינתי מתכנת מוכשר להפליא, אבל הוא דיבר בלחש, לא הסתכל בעיניים, ונראה מכונס. לא משנה כמה ידע טכני יש לכם, חשוב לשדר ביטחון ושפת גוף פתוחה. שבו זקופים, דברו בקול ברור ושמרו על קשר עין. אם אתם נוטים להילחץ, תרגלו עם חברים או מול מראה.

עכשיו אני לא בא לפגוע חלילה באף אחד, יש המון אנשים מכונסים וזה לא אומר עליכם כלום, אבל שמגייסים רוצים מעבר ליכולות הטכניות גם שחקני צוות טובים שיש להם יכולות תקשורת טובות ואנשים שיהיה כיף לעבוד סביבם.

6. קפיצה למסקנות: עצרו רגע להקשיב

דמיינו מישהו שמתקן אתכם באמצע משפט. זה מתסכל, נכון? זה קורה גם בראיונות. מועמדים רבים קופצים לענות לפני שהשאלה הסתיימה, מה שגורם להם לפספס פרטים חשובים.

במהלך ראיון למפתח Full Stack, ביקשתי ממועמד לחשוב על דרך לייעל קריאות API קיימות. עוד לפני שסיימתי, הוא התחיל להסביר לי על WebSockets. נכון, זה פתרון אפשרי – אבל הוא פספס שהשאלה התמקדה דווקא ב-Caching. עצרו, הקשיבו לשאלה במלואה, ואז ענו.

7. לא שואלים מספיק שאלות את המראיין

אין דבר מבאס יותר ממועמד שאומר: "אין לי שאלות". שאלות מראות שאתם באמת רוצים להבין את התפקיד והחברה.

מועמד ששאל אותי פעם: "איך נראה תהליך ה-Code Review אצלכם? ומה הכי חשוב לצוות בפיתוח משותף?" השאיר רושם מצוין. זה גרם לי לחשוב שהוא לא רק רוצה את העבודה, אלא גם רוצה להשתלב בצורה הכי טובה.

8. דיבור על שכר מוקדם מדי

שכר הוא חלק חשוב, אבל כשמועמדים מעלים את הנושא לפני שדיברנו על הטכנולוגיה, המוצר או האתגרים – זה מוריד.

אני זוכר מועמד שהתחיל ב: "כמה משלמים פה?". במקום זאת, עדיף להראות עניין אמיתי קודם.

מצידי דברו על טכנולוגיות ותשאלו שאלות בלי סוף, דוגמה טובה: "אני אוהב את השימוש שלכם ב-Microservices וב-AWS, ואני מאמין שאוכל לתרום לייעול הארכיטקטורה", תראו שאכפת לכם שוב ושוב ושוב זה רק יגרום לראיון להתקדם לעבר המטרה העליונה שלכם, שהיא המשכורת המוצעת.

9. היעדר ביטחון עצמי או הצגת יתר: מצאו את האיזון

בראיון אחד, מועמד אמר לי: "האמת, אני לא כזה טוב, אני רק מתחיל". זה היה עצוב לשמוע, כי הוא היה מוכשר אבל לא ידע לשווק את עצמו. לעומתו, מועמד אחר אמר: "אני תותח בכל דבר", אבל לא ידע לענות על שאלות בסיסיות.

האיזון הוא המפתח: היו כנים לגבי מה שאתם יודעים ומה שאתם לומדים, ותנו דוגמאות מעשיות שמראות את יכולותיכם.

לא מזמן ראיינתי מועמדים סניורים עם 5-15 שנות ניסיון, ואני זוכר הרבה אנשים שפשוט לא מפסיקים לדבר על כמה הם טובים.

ואז הם לא יודעים לענות על שאלה בסיסית עם Promise, צניעות, talk is cheap אל תשכחו.

10. חוסר התאמה בין קורות החיים למה שנאמר: שמרו על אמינות

מועמד כתב בקורות החיים שלו שהוא מומחה ב-React, אבל כשהתחלנו לשאול שאלות על Lifecycle Methods, הוא גמגם. זה לא רק מביך, אלא פוגע באמינות.

אם כתבתם משהו בקורות החיים – ודאו שאתם באמת שולטים בזה. ואם אינכם בטוחים, עדיף לא להכניס אותו למסמך.

ועכשיו אני אקדיש סקשיין שלם על:

הטעות שלא מדברים עליה: חוסר הכנה טכנית לראיונות

interview mistakes

בואו נדבר על הטעות הגדולה שאולי הכי כואבת למפתחי תוכנה – חוסר הכנה טכנית מספקת לקראת הראיון.

למה הכנה טכנית כל כך חשובה?

אם ראיון עבודה הוא כמו מבחן גדול, הרי שהחלק הטכני הוא השאלה שממנה כולם חוששים. למרות זאת, מועמדים רבים מגיעים לראיון בלי להתכונן מספיק, ואז נתקלים בבעיה שמכניסה אותם ללחץ.

למה זה קורה?

ולמה אתם לא מתכוננים לראיונות טכניים?

  • ביטחון עצמי מופרז: "אני כבר מתכנת X שנים, אני לא צריך להתאמן על שאלות ליט קוד". זו תפיסה מוטעית, כי ברוב הראיונות שואלים שאלות שמתמקדות בחשיבה אלגוריתמית, ולא רק בקוד שכתבתם בעבר.
  • חוסר זמן: העבודה היומיומית, חיי משפחה, ולפעמים סתם דחיינות, גורמים לנו לדלג על הכנה.
  • היעדר ידע מה לשנן: חלק מהמועמדים לא יודעים אילו נושאים כדאי לחזור עליהם – מבני נתונים, אלגוריתמים, או אולי תרגול בסביבה הטכנולוגית הרלוונטית (React, Node.js וכו').

מה קורה בראיון עצמו?

דמיינו שאתם יושבים מול הלוח או מול המחשב והמראיין אומר: "בוא נכתוב אלגוריתם שמוצא את תת-הסכום המקסימלי במערך."

אתם מתחילים לחשוב, אבל הראש פתאום ריק. אולי למדתם את זה פעם, אבל זה היה מזמן ואתם לא זוכרים איך לגשת לזה. ככל שהדקות עוברות, הלחץ עולה ואתם מתחילים לגמגם, לנסות פתרונות לא נכונים, ולהרגיש שהראיון הולך ומתדרדר.

אז לפני שזה מגיע לשם רבותיי:

איך מתכוננים נכון לראיונות טכניים?

  • 1. תרגלו שאלות ב-LeetCode או אתרים דומים

    פלטפורמות כמו LeetCode, HackerRank, ו-Codewars הן דרך מצוינת לתרגל שאלות ראיון קלאסיות.

    התחילו ברמות קלות: שאלות בסיסיות על מערכים, מחרוזות ולולאות.

    עברו לרמות בינוניות: שאלות שמשלבות מבני נתונים כמו HashMap, Stack, Queue וכו'.

    נסו גם רמות קשות: אם אתם מתמודדים על תפקידים בכירים או משרות בחברות כמו גוגל ואמזון.

  • 2. רעננו את הידע שלכם במבני נתונים ואלגוריתמים

    מבני נתונים: מערכים, רשימות מקושרות, עצים בינאריים, גרפים, Hash Tables.

    אלגוריתמים: מיון, חיפוש, BFS/DFS, תכנות דינמי, חמדנות (Greedy).

    למדו לא רק איך לכתוב את הקוד, אלא גם את המורכבות החישובית של כל אלגוריתם (Big O).

  • 3. התאמנו בסביבת Mock Interview

    מצאו חבר, קולגה, או מנטור שישחק את תפקיד המראיין. תרגול כזה מדמה את הלחץ של ראיון אמיתי ומלמד אתכם איך להסביר את המחשבות שלכם בקול רם.

    אם אין לכם פרטנר, אתרים כמו Pramp מאפשרים לכם להתאמן עם מועמדים אחרים.

  • 4. הכירו את ה-Tech Stack של החברה

    אם אתם מתראיינים לתפקיד מפתח Frontend, בדקו אילו פריימוורקים הם משתמשים (React, Angular, Vue). אם אתם מתראיינים לתפקיד Backend, בדקו את ה-Database שלהם (PostgreSQL, MongoDB, וכו') ואת הארכיטקטורה (Microservices, Monolith).

    תכינו את עצמכם לשאלות שיכולות לשלב אלגוריתמים עם הסטאק הטכנולוגי, לדוגמה:

    "איך תעצב מערכת שמבוססת על Redis?"

    "איך תייעל שאילתה ב-SQL?"

איך להתמודד עם הלחץ?

  • היו כנים אם אתם נתקעים: במקום לנסות לכתוב קוד בלי כיוון ברור, אמרו: "אני חושב להתחיל עם פתרון נאיבי ואז לייעל אותו". זה מראה על חשיבה לוגית.
  • שאלו שאלות: אם השאלה אינה ברורה, בקשו הבהרות. למשל: "האם הנתונים במערך תמיד ממוינים?"
  • פרקו את הבעיה לחלקים קטנים: התחילו בלכתוב פונקציות קטנות שנותנות מענה לחלקים מהשאלה, ולאט-לאט תבנו את הפתרון הכולל.
  • הכנה מוקדמת:

    תרגלו שאלות דומות מראש כדי להרגיש יותר בטוחים בזמן אמת.

  • שמרו על נשימות עמוקות:

    אם אתם מרגישים שהלחץ משתלט, קחו רגע לנשום עמוק ולהירגע לפני שאתם ממשיכים.

  • הסבירו את המחשבות שלכם:

    גם אם אינכם בטוחים, שיתוף התהליך המחשבתי שלכם יכול להראות למראיין את הגישה הלוגית שלכם.

  • בקשו זמן לחשוב:

    אם השאלה מורכבת, אין בושה לבקש רגע להתארגן ולהתחיל לפתור בצורה מסודרת.

  • שימוש בדוגמאות מוכרות:

    אם אתם מזהים שאלה דומה לתרגול שעשיתם, אל תהססו לציין זאת ולהשתמש במה שלמדתם.

דוגמאות לשאלות קלאסיות ולדרך פתרונן

דוגמה 1: שאלה בסיסית על מערכים

שאלה: כתוב פונקציה שמחזירה את המספר שחוזר על עצמו הכי הרבה פעמים במערך.

פתרון: ניתן להשתמש ב-HashMap כדי לספור את ההופעות של כל מספר, ואז למצוא את המפתח עם הערך הגבוה ביותר.

הסבר

  • יצירת Map לאחסון הופעות המספרים: אנו משתמשים ב-Map כדי לשמור את מספר ההופעות של כל מספר במערך. לדוגמה, עבור [1, 3, 2, 3], ה-Map יכיל: 1: 1, 3: 2, 2: 1.
  • לולאת forEach: עוברים על כל מספר במערך ומעדכנים את כמות ההופעות שלו ב-Map. אם המספר כבר קיים ב-Map, נעלה את הערך שלו ב-1. אחרת, נכניס אותו לראשונה עם ערך 1.
  • חיפוש המספר בעל כמות ההופעות הגבוהה ביותר: לולאת forEach על ה-Map עוברת על כל זוג (key, value). אנו משווים את הערך (כמות ההופעות) לערך המקסימלי שנמצא עד כה. אם הערך הנוכחי גבוה יותר, נעדכן את המספר ואת המקסימום.
  • החזרת התוצאה: בסוף התהליך, המספר שמופיע הכי הרבה פעמים יוחזר על ידי הפונקציה.

דוגמה 2: שאלה מתקדמת על עצים בינאריים

שאלה: איך בודקים אם עץ בינארי מאוזן?

פתרון: בדקו את ההבדל בגובה של כל תת-עץ. אם ההבדל גדול מ-1, העץ אינו מאוזן. ניתן לכתוב פתרון רקורסיבי או איטרטיבי.

  • בדיקת עץ בינארי מאוזן כוללת בדיקה שההבדל בגבהים של כל שני תת-עצים סמוכים אינו גדול מ-1.
  • הקוד משתמש בפונקציה רקורסיבית בשם checkHeight, שמחזירה את הגובה של תת-עץ נתון.
  • אם תת-עץ כלשהו אינו מאוזן (כלומר, ההבדל בין הגבהים גדול מ-1), הפונקציה מחזירה -1.
  • תת-עץ נחשב מאוזן אם:
    • תת-העצים שלו מאוזנים.
    • ההבדל בגבהים של תת-העצים אינו עולה על 1.
  • הפונקציה הראשית isBalanced משתמשת ב-checkHeight כדי לבדוק אם גובה העץ הראשי שונה מ--1. אם כן, העץ מאוזן.
  • המקרה הבסיסי הוא עץ ריק, שנחשב מאוזן ומחזיר גובה 0.

דוגמה 3: סיבוב מערך

שאלה: כתוב פונקציה שמבצעת סיבוב (rotation) של מערך ב-k מקומות ימינה.

פתרון:

  • השתמשו במודולו כדי לצמצם את ערך 𝑘 k: 𝑘 = 𝑘 % array.length k=k%array.length.
  • חתכו את המערך לשני חלקים: מהתחלה ועד 𝑛 − 𝑘 n−k, ומהשאר.
  • הדביקו את החלקים בסדר הפוך.
  • חישוב הערך של 𝑘:הערך 𝑘 עשוי להיות גדול מאורך המערך, ולכן יש לצמצם אותו באמצעות מודולו (k = k % arr.length). פעולה זו מוודאת שאנו לא מבצעים סיבוב מיותר.
  • חיתוך המערך לשני חלקים:המערך מחולק לשני חלקים באמצעות הפונקציה slice:
    • arr.slice(-k): מחזיר את האלמנטים מהסוף ועד 𝑘 מקומות לפני הסוף.
    • arr.slice(0, -k): מחזיר את כל האלמנטים מההתחלה ועד המקום שבו מתחיל החלק הראשון.
  • הדבקת החלקים:שני החלקים מחוברים יחדיו באמצעות האופרטור spread ([...part1, ...part2]), כך שהחלק שנחתך מהסוף מופיע ראשון.
  • שימוש בפונקציה: הפונקציה מחזירה מערך חדש שבו האלמנטים סובבו ב-𝑘 מקומות ימינה.
  • דוגמה:עבור הקלט arr = [1, 2, 3, 4, 5] ו-k = 2:
    • חלק ראשון: [4, 5] (שני האלמנטים האחרונים).
    • חלק שני: [1, 2, 3] (האלמנטים הנותרים).
    • תוצאה: [4, 5, 1, 2, 3].

דוגמה 4: בדיקת אנגרמות

שאלה: כתוב פונקציה שבודקת האם שתי מחרוזות הן אנגרמות.

פתרון:

מיינו את שתי המחרוזות והשוו אותן.

  • לכל מי שאומר עכשיו מה לע*** מזה בכלל אנגרמה?אנגרמה היא מילה או ביטוי שניתן ליצור באמצעות סידור מחדש של האותיות במילה אחרת. לדוגמה, המילים "listen" ו-"silent" הן אנגרמות.
  • מיון האותיות:הפונקציה מחלקת כל מחרוזת למערך של אותיות באמצעות split(''), ממיינת את האותיות באמצעות sort(), ואז מחזירה אותן למחרוזת עם join(''). לדוגמה:
    • "listen" → ["l", "i", "s", "t", "e", "n"] → ["e", "i", "l", "n", "s", "t"] → "eilnst"
    • "silent" → ["s", "i", "l", "e", "n", "t"] → ["e", "i", "l", "n", "s", "t"] → "eilnst"
  • השוואת התוצאות:לאחר מיון האותיות של שתי המחרוזות, הפונקציה משווה את המחרוזות הממוינות. אם הן זהות, המחרוזות הן אנגרמות.
  • יעילות:האלגוריתם הוא פשוט ויעיל יחסית, שכן הפעולה המסובכת ביותר כאן היא המיון, שהמורכבות שלו היא O(n log n).
  • דוגמה:
    • קלט: str1 = "listen", str2 = "silent"
    • מיון: "listen" → "eilnst", "silent" → "eilnst"
    • פלט: true (המחרוזות הן אנגרמות).
    • קלט נוסף: str1 = "hello", str2 = "world"
    • מיון: "hello" → "ehllo", "world" → "dlorw"
    • פלט: false (המחרוזות אינן אנגרמות).

דוגמה 5: המספר החסר בסדרה

שאלה: מצא את המספר החסר בסדרה של מספרים מ-1 עד 𝑛 n, כאשר מספר אחד חסר.

פתרון:

חישוב סכום סדרה מלא והשוואה לסכום בפועל

  • מהי הבעיה?יש לנו סדרת מספרים רציפה מ-1 עד 𝑛, אך מספר אחד חסר. המטרה היא למצוא את המספר החסר בעזרת חישוב פשוט.
  • חישוב סכום סדרה מלא:הסכום של סדרת מספרים מ-1 עד 𝑛 מחושב על פי נוסחה מתמטית ידועה:expectedSum = (n * (n + 1)) / 2. לדוגמה:
    • אם 𝑛=5, אז הסכום המלא הוא (5 * 6) / 2 = 15.
  • חישוב סכום המספרים בפועל:משתמשים בפונקציית reduce כדי לחשב את סכום כל המספרים במערך הנתון. לדוגמה:
    • עבור המערך [1, 2, 4, 5], הסכום בפועל הוא 1 + 2 + 4 + 5 = 12.
  • מציאת המספר החסר:מחסירים את הסכום בפועל מהסכום המצופה. התוצאה היא המספר החסר. לדוגמה:
    • סכום מצופה: 15, סכום בפועל: 12. המספר החסר הוא 15 - 12 = 3.
  • מורכבות זמן:הפתרון יעיל מאוד:
    • חישוב הסכום המצופה הוא פעולה ב-O(1).
    • חישוב הסכום בפועל עם reduce הוא פעולה ב-O(n).
    כך שהפתרון הכולל הוא ב-O(n).
  • דוגמה:
    • קלט: arr = [1, 2, 4, 5], n = 5
    • סכום מצופה: (5 * 6) / 2 = 15
    • סכום בפועל: 12
    • תוצאה: 15 - 12 = 3 (המספר החסר הוא 3).

דוגמה 6: מציאת האלמנט הבודד

מציין שזו שאלה שאוהבים מאד לשאול משום מה, אז בואו נסביר אותה.

שאלה: במערך שבו כל אלמנט מופיע פעמיים חוץ מאלמנט אחד, מצא את האלמנט הבודד.

פתרון:

השתמשו באופרטור XOR, שכן הוא מבטל ערכים זהים.

  • מהי הבעיה?במערך ישנם מספרים שבהם כל מספר מופיע פעמיים חוץ ממספר אחד שמופיע פעם אחת בלבד. המשימה היא למצוא את המספר הבודד.
  • מהו אופרטור XOR?אופרטור XOR (^) הוא אופרטור לוגי שמחזיר 1 אם הביטים של שני המספרים שונים, ו-0 אם הם זהים. לדוגמה:
    • 5 ^ 5 = 0 (כי הביטים זהים).
    • 5 ^ 0 = 5 (כי הביטים שונים).
  • תכונה חשובה של XOR:כאשר מפעילים XOR על שני מספרים זהים, הם מבטלים זה את זה ומחזירים 0. לדוגמה:
    • 3 ^ 3 = 0
    בנוסף, XOR עם 0 מחזיר את המספר עצמו:
    • 3 ^ 0 = 3
  • איך הפתרון עובד?הפונקציה reduce עוברת על כל המספרים במערך ומפעילה XOR על כולם. כל המספרים שמופיעים פעמיים יתבטלו, והמספר הבודד יישאר. לדוגמה:
    • עבור המערך [1, 2, 3, 2, 1], החישוב הוא:1 ^ 2 ^ 3 ^ 2 ^ 1 = (1 ^ 1) ^ (2 ^ 2) ^ 3 = 0 ^ 0 ^ 3 = 3.
  • מורכבות זמן:הפתרון הוא יעיל מאוד, כי הוא עובר על המערך פעם אחת בלבד. המורכבות היא O(n).
  • דוגמה:
    • קלט: arr = [4, 1, 2, 1, 2]
    • חישוב: 4 ^ 1 ^ 2 ^ 1 ^ 2 = (1 ^ 1) ^ (2 ^ 2) ^ 4 = 0 ^ 0 ^ 4 = 4
    • פלט: 4 (המספר הבודד הוא 4).

דוגמה 7: סכום שני מספרים (Two Sum)

שאלה: מצא שני מספרים במערך שסכומם שווה ל- target

פתרון:

שימוש ב-HashMap לאחסון השלמות.

  • מהי הבעיה?המשימה היא למצוא שני מספרים במערך כך שסכומם שווה למספר יעד נתון (target). יש להחזיר את האינדקסים של שני המספרים.
  • השימוש ב-HashMap:במקום לחפש שילוב מתאים בלולאה כפולה (שהיא בעלת מורכבות O(n^2)), אנו משתמשים ב-Map כדי לאחסן את המספרים שראינו ואת המיקומים שלהם, מה שמאפשר למצוא את השילוב בזמן ליניארי O(n).
  • איך זה עובד?עבור כל מספר במערך:
    • מחשבים את ההשלמה הדרושה כדי להגיע ליעד: complement = target - arr[i].
    • בודקים אם ההשלמה נמצאת ב-Map:
      • אם כן – נמצאו שני המספרים, מחזירים את האינדקסים.
      • אם לא – מוסיפים את המספר הנוכחי ל-Map עם האינדקס שלו.
  • מה קורה כשמוצאים התאמה?כאשר נמצא המספר המשלים (complement), אנו מחזירים מערך עם שני האינדקסים: האינדקס של המספר המשלים שנמצא ב-Map והאינדקס הנוכחי.
  • מורכבות זמן:המורכבות היא O(n), כי אנו עוברים על המערך פעם אחת בלבד. פעולות על Map הן בזמן קבוע O(1).
  • דוגמה:
    • קלט: arr = [2, 7, 11, 15], target = 9
    • מהלך הפתרון:
      • מעבדים את המספר 2, ההשלמה היא 7. מוסיפים את 2 ל-Map.
      • מעבדים את המספר 7, ההשלמה היא 2. המספר 2 כבר נמצא ב-Map.
      • מחזירים את האינדקסים [0, 1].
    • פלט: [0, 1] (המספרים הם 2 ו-7).

דוגמה 8: בדיקת פלינדרום

שאלה: כתוב פונקציה שבודקת האם מחרוזת היא פלינדרום.

פתרון:

השוו את המחרוזת המקורית עם ההפוכה שלה.

  • מהו פלינדרום?פלינדרום היא מחרוזת שנקראת זהה משני הצדדים, כלומר, מימין לשמאל ומשמאל לימין. לדוגמה: "racecar" או "level". או בעברית למשל המילה "אבא".
  • שלבי הפתרון:
    • פיצול המחרוזת:הפונקציה משתמשת ב-split('') כדי להפוך את המחרוזת למערך של תווים. לדוגמה:
      • "racecar".split('') → ["r", "a", "c", "e", "c", "a", "r"]
    • היפוך המערך:הפונקציה reverse() הופכת את סדר התווים במערך. לדוגמה:
      • ["r", "a", "c", "e", "c", "a", "r"].reverse() → ["r", "a", "c", "e", "c", "a", "r"]
    • חיבור מחדש למחרוזת:הפונקציה join('') מחברת את המערך המהופך חזרה למחרוזת. לדוגמה:
      • ["r", "a", "c", "e", "c", "a", "r"].join('') → "racecar"
    • השוואה:המחרוזת המקורית מושווית למחרוזת ההפוכה שלה באמצעות ===. אם הן זהות, המחרוזת היא פלינדרום. לדוגמה:
      • "racecar" === "racecar" → true
      • "hello" === "olleh" → false
  • מורכבות זמן:המורכבות היא O(n), כי כל הפעולות (פיצול, היפוך, חיבור והשוואה) עוברות על המחרוזת פעם אחת.
  • דוגמה:
    • קלט: str = "level"
    • פלט: true (המחרוזת היא פלינדרום).
    • קלט נוסף: str = "hello"
    • פלט: false (המחרוזת אינה פלינדרום).

דוגמה 9: מציאת תת-סכום מקסימלי (Kadane’s Algorithm)

שאלה: מצא את תת-המערך עם הסכום הגדול ביותר.

פתרון:

השתמשו באלגוריתם של Kadane.

  • מהי הבעיה?המטרה היא למצוא את תת-המערך עם הסכום הגדול ביותר במערך נתון. תת-מערך הוא רצף של מספרים עוקבים מתוך המערך.
  • איך עובד אלגוריתם Kadane?
    • מעקב אחרי סכום זמני:האלגוריתם משתמש במשתנה maxEndingHere כדי לעקוב אחרי הסכום המקסימלי של תת-המערך שמסתיים במיקום הנוכחי.
    • עדכון הסכום המקסימלי:

      המשתנה maxSoFar מתעדכן כך שיכיל את הסכום המקסימלי שנמצא עד כה בכל המערך.

    • עיקרון הפעולה:בכל צעד:
      • בודקים האם עדיף להתחיל תת-מערך חדש מהמספר הנוכחי או להמשיך את תת-המערך הקיים:maxEndingHere = Math.max(arr[i], maxEndingHere + arr[i]).
      • מעודכנים את maxSoFar אם maxEndingHere גדול יותר:maxSoFar = Math.max(maxSoFar, maxEndingHere).
  • מהלך הפתרון:
    • האלגוריתם מתחיל עם הערך הראשון של המערך כבסיס (maxSoFar ו-maxEndingHere).
    • הוא עובר על כל איבר במערך, מעדכן את maxEndingHere ואת maxSoFar, ומחזיר את הערך המקסימלי בסיום.
  • מורכבות זמן:האלגוריתם פועל ב-O(n), מכיוון שהוא עובר על המערך פעם אחת בלבד.
  • דוגמה:
    • קלט: arr = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
    • מהלך:
      • אינדקס 0: maxEndingHere = -2, maxSoFar = -2
      • אינדקס 1: maxEndingHere = Math.max(1, -2 + 1) = 1, maxSoFar = Math.max(-2, 1) = 1
      • אינדקס 2: maxEndingHere = Math.max(-3, 1 + -3) = -2, maxSoFar = 1
      • אינדקס 3: maxEndingHere = Math.max(4, -2 + 4) = 4, maxSoFar = Math.max(1, 4) = 4
      • אינדקס 4: maxEndingHere = Math.max(-1, 4 + -1) = 3, maxSoFar = 4
      • אינדקס 5: maxEndingHere = Math.max(2, 3 + 2) = 5, maxSoFar = Math.max(4, 5) = 5
      • אינדקס 6: maxEndingHere = Math.max(1, 5 + 1) = 6, maxSoFar = Math.max(5, 6) = 6
      • אינדקס 7: maxEndingHere = Math.max(-5, 6 + -5) = 1, maxSoFar = 6
      • אינדקס 8: maxEndingHere = Math.max(4, 1 + 4) = 5, maxSoFar = 6
    • פלט: 6 (תת-המערך המקסימלי הוא [4, -1, 2, 1]).

דוגמה 10: מספר פיבונאצ'י

שאלה: כתוב פונקציה שמחזירה את המספר ה-

פתרון:

ניתן להשתמש ברקורסיה או בגישה איטרטיבית.

  • מהי סדרת פיבונאצ'י?סדרת פיבונאצ'י היא סדרת מספרים שבה כל מספר הוא סכום שני המספרים הקודמים לו. לדוגמה: 0, 1, 1, 2, 3, 5, 8, 13...
  • מהי הבעיה?עלינו לכתוב פונקציה שמחזירה את המספר ה-n בסדרת פיבונאצ'י. לדוגמה:
    • אם n = 0, הפונקציה תחזיר 0.
    • אם n = 5, הפונקציה תחזיר 5.
  • איך הפתרון עובד?
    • בדיקה עבור מקרה בסיס:אם n &gt = 1, המספר ה-n הוא פשוט n (כי f(0) = 0 ו-f(1) = 1).
    • משתנים למעקב:נשתמש בשני משתנים:
      • prev: ישמור את המספר הקודם בסדרה.
      • curr: ישמור את המספר הנוכחי בסדרה.
    • החזרת התוצאה:בסיום הלולאה, המשתנה curr יכיל את המספר ה-n בסדרה.
  • מורכבות זמן:הפתרון פועל ב-O(n), מכיוון שהלולאה רצה פעם אחת עבור כל מספר עד n.
  • דוגמה:
    • קלט: n = 5
    • מהלך:
      • איטרציה 2: prev = 1, curr = 1
      • איטרציה 3: prev = 1, curr = 2
      • איטרציה 4: prev = 2, curr = 3
      • איטרציה 5: prev = 3, curr = 5
    • פלט: 5

דוגמה 11: זיהוי נקודת שיווי משקל במערך

שאלה: מצא את האינדקס שבו סכום האלמנטים משמאל שווה לסכום האלמנטים מימין.

פתרון:

חישוב סכום כולל ושימוש בסכום מצטבר.

  • מהי הבעיה?למצוא את האינדקס שבו סכום המספרים משמאל שווה לסכום המספרים מימין במערך נתון.
  • שלבי הפתרון:
    • חישוב totalSum: סכום כל המספרים במערך.
    • מעקב אחר leftSum: סכום המספרים משמאל לאינדקס הנוכחי.
    • לכל אינדקס:
      • בדוק אם leftSum שווה ל-totalSum - leftSum - arr[i].
      • אם כן, החזר את האינדקס הנוכחי.
    • עדכון leftSum: הוסף את הערך הנוכחי.
    • אם לא נמצאה נקודת שיווי משקל, החזר -1.
  • דוגמה:
    • קלט: arr = [1, 7, 3, 6, 5, 6]
    • מהלך:
      • אינדקס 0: leftSum = 0, rightSum = 27
      • אינדקס 1: leftSum = 1, rightSum = 20
      • אינדקס 3: leftSum = 11, rightSum = 11
    • פלט: 3 (אינדקס 3 הוא נקודת שיווי המשקל).
  • מורכבות זמן: O(n), מעבר יחיד על המערך.

דוגמה 12: חיפוש בינארי

שאלה: מצא אם מספר קיים במערך ממויין.

פתרון:

השתמשו באלגוריתם חיפוש בינארי.

  • מהי הבעיה?למצוא אינדקס של מספר במערך ממויין או להחזיר -1 אם המספר לא קיים.
  • שלבי הפתרון:
    • הגדר גבולות: left ו-right.
    • חשב את האינדקס האמצעי: mid = Math.floor((left + right) / 2).
    • השווה את המספר באמצע למספר היעד:
      • אם שווה – החזר את האינדקס.
      • אם קטן – עדכן left = mid + 1.
      • אם גדול – עדכן right = mid - 1.
    • אם המספר לא נמצא, החזר -1.
  • דוגמה:
    • קלט: arr = [1, 3, 5, 7, 9], target = 5
    • מהלך:
      • אמצע: mid = 2, ערך: 5.
    • פלט: 2 (אינדקס המספר).
  • מורכבות זמן: O(log n), חלוקת החיפוש בכל איטרציה.

דוגמה 13: זיהוי מעגל ברשימה מקושרת

שאלה: בדוק אם רשימה מקושרת מכילה מעגל.

פתרון:

השתמשו באלגוריתם Floyd’s Cycle Detection (שני מצביעים).

  • מהי הבעיה?לבדוק אם רשימה מקושרת מכילה מעגל שבו קיים חיבור חוזר לאחד הצמתים הקודמים.
  • שלבי הפתרון:
    • השתמשו בשני מצביעים:
      • slow: מתקדם בצעד אחד בכל איטרציה.
      • fast: מתקדם בשני צעדים בכל איטרציה.
    • אם slow ו-fast נפגשים, הרשימה מכילה מעגל.
    • אם fast מגיע לסוף (null), אין מעגל.
  • מורכבות זמן: O(n), מעבר על הרשימה פעם אחת.
  • מורכבות מקום: O(1), ללא שימוש בזיכרון נוסף.

דוגמה 14: עבודה עם הבטחות

שלב 1: קוד ראשוני כתוב לולאה שמבצעת פעולה אסינכרונית עבור כל איטרציה. הפעולה האסינכרונית מתבצעת על ידי שימוש ב-Promise וב-setTimeout.

שלב 2: שינוי ראשון שנה את הקוד כך שכל איטרציה תחכה שהאיטרציה הקודמת תסתיים לפני שהיא מתחילה.

שלב 3: שינוי נוסף שנה את הקוד כך שהעיכוב ב-setTimeout יגדל עם כל איטרציה:

פתרון:

שלב 1: קוד ראשוני

בקוד זה, כל איטרציה יוצרת Promise שממתין שנייה אחת ואז פותר את הערך של i.

שלב 2: שינוי ראשון - איטרציות עוקבות

שינינו את הקוד כך שכל איטרציה תחכה שהקוד באיטרציה הקודמת יסתיים לפני שתתחיל.

שלב 3: שינוי נוסף - עיכוב משתנה

שינינו את הקוד כך שהעיכוב ב-setTimeout יגדל עם כל איטרציה.

שלב 1: הפעולות מתבצעות במקביל.

שלב 2: כל פעולה מחכה שהקוד באיטרציה הקודמת יסתיים.

שלב 3: העיכוב משתנה ומותאם לאינדקס האיטרציה, כך שהזמן בין הפעולות גדל באופן ליניארי.


דוגמה 15: קצת patterns.

יצירת פונקציה שמייצגת את תבנית העיצוב של ה-Observer

צרו פונקציה בשם createGame המאפשרת להוסיף צופים, להסיר צופים, ולעדכן אותם בתוצאות המשחק.

משחק כדורגל

הפונקציה createGame תאפשר יצירת משחק עם שתי קבוצות: awayTeam ו-homeTeam.

פעולות בתוך הפונקציה

  • הוספת צופה (Watcher): אפשרות להוסיף צופה שיעודכן בתוצאות המשחק.
  • הסרת צופה: אפשרות להסיר צופה מסוים.
  • עדכון הצופים: אפשרות לעדכן את כל הצופים עם תוצאה חדשה של המשחק.

יצירת שני משחקים

צרו שני משחקים עם פונקציית createGame:

  • משחק ראשון בין 'Real Madrid' ל-'Barcelona'.
  • משחק שני בין 'Manchester United' ל-'Liverpool'.

יצירת פונקציה ליצירת צופים

צרו פונקציה בשם createWatcher שמייצרת אובייקט צופה עם פרטים רלוונטיים.

יצירת צופים

צרו שלושה צופים:

  • צופה בשם 'Daniel'.
  • צופה בשם 'Doctor'.
  • צופה בשם 'Liat'.

הרשמת הצופים

רשמו כל צופה למשחק מסוים. ודאו שכל צופה מקבל עדכון על שינויים בתוצאה.

עדכון התוצאות

עדכנו תוצאות בשני המשחקים ובדקו שכל הצופים מקבלים את ההתראה.

פתרון:

  • מהי הבעיה?

    יצירת מערכת המאפשרת להוסיף צופים (Watchers) למשחק, לעדכן אותם בתוצאות ולהסירם.

  • שלבי הפתרון:
    • יצירת משחק: הפונקציה createGame מאפשרת:
      • הוספת צופה עם addWatcher.
      • הסרת צופה עם removeWatcher.
      • עדכון הצופים עם notifyWatchers.
    • יצירת צופה: הפונקציה createWatcher מגדירה צופה עם callback שמדפיס תוצאה.
    • הרשמת צופים: כל צופה נרשם למשחק בעזרת addWatcher.
    • עדכון תוצאות: תוצאה חדשה נשלחת לכל הצופים הרשומים עם notifyWatchers.
  • דוגמה:
    • משחק 1: 'Real Madrid' מול 'Barcelona', תוצאה: 'Real Madrid 3 - 2 Barcelona'.
    • משחק 2: 'Manchester United' מול 'Liverpool', תוצאה: 'Manchester United 1 - 1 Liverpool'.
    • צופים קיבלו עדכונים בהתאם להרשמתם.
  • מורכבות זמן: O(n) לכל עדכון, כאשר n הוא מספר הצופים.

לסיכום: הכנה טכנית היא המפתח להצלחה

חוסר הכנה לראיונות טכניים הוא אחת הסיבות הנפוצות ביותר לכישלון בראיונות עבודה. אך החדשות הטובות הן שזה לגמרי בשליטתכם.

תרגלו, הכינו את עצמכם, ותגיעו עם ביטחון. ברגע שתשלטו בנושאים המרכזיים ותהיו רגועים מול לוח או מחשב, תגלו שגם השאלות המפחידות ביותר ניתנות לפתרון.

זכרו: ראיון עבודה הוא לא מבחן חד-פעמי שמגדיר אתכם. גם אם לא הצלחתם בראיון אחד, זה רק עוד צעד בדרך לתפקיד שאתם באמת רוצים.