Microsoft Software Engineer (Entry Level) Interview Preparation Guide
Microsoft's entry-level Software Engineer interview process is a rigorous, multi-stage evaluation spanning 3-5 weeks, designed to assess fundamental coding proficiency, problem-solving ability, structured thinking, and cultural fit. The process emphasizes data structures, algorithms, and behavioral competencies through a combination of online assessments, technical phone screens, and onsite panel interviews conducted virtually or in-person. Candidates are evaluated collaboratively with emphasis on growth mindset, clear communication, and ability to work effectively in cross-functional teams.
Interview Rounds
Recruiter Screening
What to Expect
Your initial interaction with Microsoft's recruiting team, conducted via phone or video. The recruiter verifies your background, assesses your motivation for the role and company, and gauges cultural fit. They explain the interview process timeline, answer your questions about the team and role, and determine if you should advance to technical assessments. This conversation sets expectations and confirms initial alignment between your goals and the position.
Tips & Advice
Research Microsoft thoroughly before this call—understand their products (Azure, Office 365, GitHub, Windows), recent innovations, and their growth mindset culture. Be enthusiastic and genuine. Prepare concise, compelling answers to 'Why Microsoft?' and 'Why this role?'. Have 2-3 thoughtful questions about the team or product ready. For entry-level, be honest about your experience level while emphasizing your eagerness to learn and grow. Speak clearly, listen actively, and respond directly to questions. Highlight any relevant projects, internships, or coursework that demonstrates coding ability.
Focus Topics
Professional Communication and Coachability
Communicate clearly and concisely. Listen carefully to questions and answer directly without rambling. Use concrete examples rather than vague statements. Show respect, professionalism, and enthusiasm. If you don't know something, be honest and express willingness to learn. Demonstrate that you're coachable and can take feedback—this is especially important for entry-level candidates.
Practice Interview
Study Questions
Understanding the Software Engineer Role and Learning Orientation
Show that you understand what Software Engineers do at Microsoft: write clean, efficient code; collaborate across teams; design solutions that meet business requirements; and stay current with technology. Emphasize your learning orientation. Say things like 'I'm excited to learn best practices for writing scalable code' or 'I'm interested in how Microsoft approaches system design.' Ask thoughtful questions about the team's technology stack, projects, or learning opportunities.
Practice Interview
Study Questions
Resume Highlights and Technical Background
Prepare to walk through your resume highlighting technical skills, relevant coursework (data structures, algorithms, system design), academic projects, internships, hackathons, or open-source contributions. For entry-level candidates, emphasize any practical coding experience, problem-solving demonstrations, and tools used (programming languages, frameworks, version control). Be ready to briefly explain the most interesting technical project you've worked on.
Practice Interview
Study Questions
Genuine Interest in Microsoft and the Role
Articulate why you specifically want to work at Microsoft, not just any tech company. Reference Microsoft's mission, specific products or technologies you admire, recent developments, or cultural aspects that appeal to you. Connect the role to your career goals: 'I want to grow as a software engineer by working on scalable systems and learning from experienced engineers at Microsoft.' Show that you've done research and are genuinely interested.
Practice Interview
Study Questions
Online Coding Assessment
What to Expect
A timed coding challenge conducted on Codility or similar platform, lasting 60-90 minutes. You'll solve 2-3 algorithmic problems involving data structures, ranging from easy to medium difficulty. The assessment tests your ability to understand problems, develop correct solutions, write clean code, and optimize for efficiency. You're evaluated on correctness, code quality, time/space complexity, and ability to handle edge cases. This is an objective assessment with no human interaction, so solutions must be functionally correct.
Tips & Advice
Read each problem carefully and understand all requirements before coding. Take 5-10 minutes to plan your approach mentally or on paper. Start with a working solution, even if not optimized, then refine. Test your solution mentally with simple and edge case inputs (empty, single element, duplicates, boundary values). Write clean code with meaningful variable names and comments for complex logic. Time management is critical: if stuck on a problem, move on and return if time permits. Aim for correctness first; optimization is secondary. Debug any failures logically by tracing through the logic with test cases.
Focus Topics
Edge Case Handling and Testing
Always consider edge cases: empty inputs, single elements, negative numbers, zeros, duplicates, very large inputs, and boundary values. Mentally test your solution with at least 3 cases: a simple case, a more complex case, and an edge case. Many solutions fail due to missed edge cases. Write defensive code that handles unexpected inputs gracefully.
Practice Interview
Study Questions
Time and Space Complexity Analysis
Understand Big O notation deeply. Analyze your solutions for time complexity (e.g., O(n), O(n log n), O(n²)) and space complexity (e.g., O(1), O(n)). Know which complexities are generally acceptable: O(n) or O(n log n) for time is good, O(n²) is borderline and often needs optimization. Recognize when you can trade space for time (e.g., using a hash map). Be able to identify complexity improvements from your initial approach.
Practice Interview
Study Questions
Algorithmic Problem-Solving Patterns
Learn to recognize and apply common patterns: brute force (understand baselines), two pointers (converging from ends), sliding window (fixed/variable sized windows), binary search (divide and conquer), and basic recursion. Understand how each pattern works and which problems fit each pattern. Practice identifying the pattern from problem description. For entry-level, focus on two pointers, sliding window, and basic binary search.
Practice Interview
Study Questions
LeetCode Practice - Target 50+ Problems
Actively practice on LeetCode, focusing on Easy and Easy-Medium problems (Arrays, Strings, Linked Lists, Hash Tables). Aim for 50-80 problems covering diverse patterns. Use LeetCode's filtering by topic to practice systematically. Time yourself to simulate the assessment environment. Review solutions you get wrong to understand the correct approach. Build pattern recognition muscle memory.
Practice Interview
Study Questions
Data Structures Fundamentals
Deeply understand core data structures: arrays (indexing, slicing, searching, sorting), strings (immutability in some languages, common operations), linked lists (traversal, insertion, deletion), stacks and queues (LIFO/FIFO behavior), hash maps (key-value mapping, collision handling), basic trees (structure, traversal). Know time/space complexity of operations on each structure. For entry-level, focus primarily on arrays, strings, and linked lists; understand trees conceptually.
Practice Interview
Study Questions
Arrays and Strings Problem Mastery
Master problems involving arrays and strings since these constitute 36% of Microsoft coding questions. Focus on: two-pointer techniques, sliding window approach, string manipulation (reversals, anagrams, substrings), array operations (rotation, sorting, searching), and prefix/suffix problems. Understand when each technique applies and how to implement efficiently. For entry-level, prioritize problems on LeetCode Easy-Medium difficulty with these topics.
Practice Interview
Study Questions
Technical Phone Screen
What to Expect
A 45-60 minute live technical interview conducted via phone or video with a Microsoft software engineer. You'll solve one coding problem similar in style to the online assessment, but with real-time discussion. The interviewer asks clarifying questions, observes your problem-solving process, and may provide hints or feedback. You're evaluated on your ability to understand requirements, articulate your approach, write correct code, handle feedback, and communicate clearly. Behavioral questions may be interspersed to assess collaboration and learning mindset.
Tips & Advice
Listen carefully to the problem and ask clarification questions before jumping into coding. Think out loud—explain your approach, discuss why you're choosing a particular data structure, and walk through a simple example before writing code. Write clean, syntactically correct code deliberately; the interviewer cares about your thinking process as much as the final solution. If stuck, ask for hints rather than sitting silently. Be receptive to feedback; if the interviewer suggests a different approach, embrace it gracefully. For behavioral questions, use the STAR method and emphasize growth mindset and collaboration. Show genuine interest in learning from the interviewer.
Focus Topics
STAR Method for Behavioral Questions
Prepare 2-3 STAR stories for common behavioral questions: (1) A problem you solved (technical or otherwise), (2) A time you collaborated successfully in a team, (3) A time you learned from failure or feedback. Structure each as Situation (context), Task (your role), Action (what you did), Result (outcome and learning). For entry-level, draw from academic projects, internships, or personal projects. Focus on growth mindset: 'I didn't initially know how to approach this, but I researched, learned, and ultimately succeeded.'
Practice Interview
Study Questions
Handling Technical Challenges and Problem Variants
The interviewer may ask follow-up questions or request modifications to your solution. If asked 'Can you solve this with less space?', adapt your approach. If asked 'What if the input is modified this way?', think through the implications. These variants test your flexibility and deep understanding. Stay calm, think methodically, and adjust your approach.
Practice Interview
Study Questions
Linked List and Tree Problem Competency
Based on interview patterns, linked list problems are common in phone screens. Be comfortable with: linked list traversal, insertion, deletion, reversal, and detecting cycles. Understand the difference between singly and doubly linked lists. Also practice basic tree problems: traversal (in-order, pre-order, post-order), searching, and simple tree construction. These topics are frequently tested after array/string problems.
Practice Interview
Study Questions
Receiving Feedback and Adaptive Problem-Solving
If the interviewer points out an issue or suggests a different approach, respond gracefully and positively: 'That's a good point. Let me reconsider.' Adjust your solution without defensiveness. Show flexibility and openness to learning. Entry-level candidates are not expected to have perfect solutions immediately; adaptability and coachability are highly valued. Demonstrate that you learn quickly and implement feedback effectively.
Practice Interview
Study Questions
Structured Problem-Solving Methodology
Follow a clear 5-step approach: (1) Clarify—ask about input constraints, output format, and edge cases; explore the problem space; (2) Plan—discuss your approach aloud, consider trade-offs, outline high-level steps without coding; (3) Implement—write clean, commented code deliberately; (4) Test—trace through test cases, verify correctness, check edge cases; (5) Optimize—analyze complexity, discuss improvements. This structured approach demonstrates maturity and collaboration.
Practice Interview
Study Questions
Clear Communication and Thinking Out Loud
Articulate your thought process verbally. Explain why you're using a particular data structure: 'I'll use a hash map because I need O(1) lookup for each element.' Discuss trade-offs aloud: 'An O(n²) brute force approach works but might be slow; alternatively, I could...' Ask questions to confirm understanding: 'Should I assume the input is always valid?' Show your reasoning, not just your code. This transparency helps the interviewer understand your thinking and guide you if needed.
Practice Interview
Study Questions
Onsite Technical Round 1 - Coding
What to Expect
The first of three in-person (or virtual) panel interviews lasting approximately 1 hour each. You're interviewed by a Microsoft software engineer, potentially from the team you're applying to. This round focuses on coding and algorithmic problem-solving, similar in style to the phone screen but conducted in person. You'll solve one complex coding problem, potentially at medium difficulty with multiple follow-ups. The interview assesses your coding proficiency, problem-solving approach, ability to handle real-time feedback, code quality, and communication. Behavioral elements are woven throughout the technical discussion.
Tips & Advice
Treat this as a collaborative session, not an exam. Start by asking clarification questions and outlining your approach before coding. Spend 5-10 minutes planning; rushing into code often leads to mistakes. Write clean code on the whiteboard or computer deliberately—don't worry about speed initially. If you make a mistake, catch and fix it yourself, explaining your reasoning. Trace through your solution with test cases before declaring it complete. Be open to the interviewer's suggestions and proactively discuss optimizations. Show enthusiasm for the problem and genuine interest in the interviewer's perspective. Remember: demonstrating your thinking matters as much as the final solution.
Focus Topics
Whiteboard Coding and In-Person Communication
If the interview is in-person, you may be coding on a whiteboard. Writing by hand is slower than typing; adjust your pace accordingly. Write clearly and speak as you code, explaining each step. The interviewer is observing your thought process, not just the final code. If coding on a computer, still verbalize your thinking. Maintain eye contact and engage conversationally with the interviewer throughout.
Practice Interview
Study Questions
Behavioral Integration: Growth Mindset in Technical Context
During technical discussions, demonstrate growth mindset. When discussing your solution, acknowledge the learning: 'I initially considered an O(n²) approach, but I realized I could optimize to O(n) by using...' When the interviewer makes a suggestion, respond positively: 'That's a great optimization. I see how that works.' Show genuine curiosity: 'Why does that approach work better in this case?' This demonstrates alignment with Microsoft's culture.
Practice Interview
Study Questions
Problem Decomposition and Approach Planning
When faced with a complex problem, break it into smaller subproblems. For example, if solving a problem involving multiple conditions, handle each condition separately before combining. Plan your approach aloud before implementing. Ask yourself: 'What's the key insight here?' and 'How can I simplify this?' Entry-level engineers who show decomposition skills are seen as thoughtful and systematic.
Practice Interview
Study Questions
Debugging and Testing Under Pressure
During the onsite, you're in a live environment. If your logic has issues, methodically debug. Trace through your code with a test case on the whiteboard or mentally. Ask yourself: 'Where could this fail?' Check for off-by-one errors, boundary conditions, and incorrect assumptions. If a test case reveals a bug, fix it logically and verify the fix. Demonstrating calm, systematic debugging shows professionalism.
Practice Interview
Study Questions
Arrays, Strings, and Linked Lists Mastery
These three data structures represent the majority of onsite coding questions. Master problems involving: array manipulation (searching, sorting, rotating, merging), string operations (reversals, anagrams, substring matching, pattern finding), and linked list operations (traversal, reversal, cycle detection, merging). For entry-level, practice 40-50 problems across these three topics to build deep confidence.
Practice Interview
Study Questions
Core Coding Problem Pattern Recognition
Master recognition of common patterns that appear in onsite interviews: two pointers, sliding window, hash map usage, binary search, depth-first search (DFS), breadth-first search (BFS), and basic recursion. For entry-level, focus most heavily on two pointers, sliding window, and hash maps. Understand which pattern applies to a given problem and how to implement it efficiently. Practice 30-40 diverse problems to build pattern recognition confidence.
Practice Interview
Study Questions
Onsite Technical Round 2 - Coding
What to Expect
The second of three onsite technical rounds, lasting approximately 1 hour. You're interviewed by a different Microsoft engineer, potentially from a different team. This round follows the same format as Round 1: one coding problem solved live with discussion and collaborative feedback. The difficulty level is typically similar to Round 1 but may test different concepts (for instance, if Round 1 emphasized arrays, Round 2 might focus on linked lists, trees, or combinations). You're evaluated on coding proficiency, problem-solving ability, communication, and how well you apply feedback from previous rounds.
Tips & Advice
Bring the experience from Round 1 to bear here. You've already demonstrated you can solve coding problems onsite; use that confidence. Follow the same structured approach: clarify, plan, implement, test, optimize. Each interviewer has a different style and may ask different follow-up questions; be adaptable. If the problem seems unfamiliar, break it down into fundamentals and apply core concepts. Don't overthink or second-guess yourself. Engage actively with the interviewer, ask for hints if stuck, and discuss trade-offs. Remember: the interview is collaborative, not adversarial. By Round 2, you should be more efficient and confident than Round 1.
Focus Topics
Building on Round 1 Success and Learning
Reflect briefly on Round 1. What went well? What could you improve? Carry forward your strengths into Round 2. If you struggled with a particular concept in Round 1, this is an opportunity to show growth. If Round 1 went very well, maintain that momentum. Entry-level candidates are expected to show improvement with experience. Use each round as a learning opportunity.
Practice Interview
Study Questions
Adjusting to Different Interviewer Styles
Each interviewer has unique communication style, pacing, and expectations. Some might be very hands-off, expecting you to drive; others might guide more closely. Be observant and adapt. If an interviewer is quiet, take it as a cue to think and communicate more out loud. If they're very engaged, embrace the collaboration. Both styles are valid; your job is to match their energy and show professionalism across different personalities.
Practice Interview
Study Questions
Optimization Discussions and Trade-offs
In Round 2, confidently discuss time/space trade-offs. If you have an O(n) time, O(n) space solution, discuss whether you can optimize space to O(1) by sacrificing time. Explain your reasoning: 'This trade-off makes sense because [reason].' Show that you understand not all problems have equally important optimization axes. Demonstrate nuanced thinking about optimization decisions.
Practice Interview
Study Questions
Trees, Graphs, and Graph Traversal Fundamentals
If not heavily tested in Round 1, expect tree and graph problems in Round 2 or Round 3. Understand tree structure and common operations: in-order, pre-order, post-order traversals; binary search tree operations; tree height/balance concepts. For graphs, understand basic concepts: adjacency lists, DFS, BFS, and when to use each. For entry-level, focus on standard tree traversal and simple graph connectivity problems.
Practice Interview
Study Questions
Recursion and Basic Backtracking
If Round 1 didn't heavily test recursion, Round 2 might. Understand basic recursion: identifying base cases, formulating recursive cases, ensuring proper return values. Practice problems like computing factorials, tree traversals, simple path finding, and permutation/combination generation. Understand the difference between permutations and combinations. For entry-level, focus on straightforward recursive problems rather than complex constraint satisfaction or optimization.
Practice Interview
Study Questions
Diverse Problem Types and Adaptability
After Round 1, expect different problem types in Round 2. If Round 1 tested arrays heavily, Round 2 might involve linked lists, trees, recursion, or combinations. Your practice should cover diverse topics so you can adapt quickly. When facing an unfamiliar problem, apply fundamental problem-solving: understand the problem, identify the pattern, apply appropriate techniques. Flexibility and adaptability demonstrate true mastery rather than pattern memorization.
Practice Interview
Study Questions
Onsite Technical Round 3 - Coding with Behavioral Focus
What to Expect
The third and final of three main onsite technical rounds, lasting approximately 1 hour. You're interviewed by another Microsoft engineer, continuing the coding interview format. By this round, you've demonstrated consistent coding competency across multiple problems. This final technical round emphasizes both your sustained coding ability and deeper exploration of behavioral and cultural fit. The interviewer will ask behavioral questions more substantively, probing your collaboration, learning from feedback, conflict resolution, and alignment with Microsoft's growth mindset principles. The coding problem is at similar difficulty to previous rounds, but your ability to discuss it collaboratively and relate it to team dynamics becomes increasingly important.
Tips & Advice
Maintain your structured coding approach from Rounds 1 and 2, but recognize this round is partly about confirming cultural fit. Between coding segments, engage more deeply in behavioral discussions. When solving the problem, pause frequently to discuss decisions collaboratively: 'How would you approach this?' or 'Do you see any issues with this strategy?' Be genuinely curious about the interviewer's perspective. For behavioral questions, tell authentic STAR stories emphasizing growth mindset and learning. Ask thoughtful questions about the team, their technical challenges, and culture. Show genuine excitement about the possibility of joining Microsoft. This is your last impression; make it count by being both technically solid and culturally aligned.
Focus Topics
Thoughtful Questions for the Interviewer
Prepare 3-4 thoughtful questions that show genuine interest and engagement: 'What are the biggest technical challenges your team is currently working on?' or 'How does the team approach code quality and testing?' or 'What does career development look like for entry-level engineers at Microsoft?' or 'How does your team foster a growth mindset culture?' Asking good questions demonstrates respect, engagement, and curiosity—all valued traits. It's also your opportunity to assess whether Microsoft is the right fit for you.
Practice Interview
Study Questions
Knowledge of Microsoft's Products, Vision, and Direction
By Round 3, demonstrate substantive knowledge of Microsoft. Reference specific products: 'Azure's growth in cloud computing is impressive' or 'GitHub's integration with Microsoft is transforming developer workflows.' Discuss the company's vision: 'Microsoft's focus on empowering every person and organization to achieve more aligns with my values.' Show you've researched recent developments and company strategy. When appropriate, connect your interests to Microsoft's direction. This demonstrates genuine interest beyond just getting a job.
Practice Interview
Study Questions
Collaborative Problem-Solving and Team Dynamics Discussion
During the coding portion, actively involve the interviewer. Ask 'How would you approach this?' or 'Do you see any issues with my strategy?' Discuss how you'd collaborate with teammates to validate your solution. Talk about code review: 'I'd explain my approach to teammates and ask for feedback.' For entry-level, emphasize eagerness to learn from senior engineers: 'I'd be grateful to receive feedback from experienced engineers on how to improve my code.' Show that you value teamwork and are coachable. Discuss how you'd help junior team members if you encounter problems they're working on.
Practice Interview
Study Questions
Final Technical Problem Mastery and Confidence
You've now solved three coding problems onsite. Bring everything together for this final problem: structured approach, clear communication, collaborative discussion, debugging confidence, and optimization thinking. Solve cleanly and confidently. This is your last technical impression. Show that by Round 3, you're not just capable but demonstrating mastery and comfort with the process.
Practice Interview
Study Questions
Microsoft Growth Mindset and Leadership Principles Alignment
Microsoft's culture emphasizes growth mindset—the belief that abilities develop through dedication and effort. Demonstrate this in technical and behavioral discussions. Use phrases like: 'I initially approached it this way, but I learned that...' or 'I see how that strategy is better; I didn't consider that angle.' Reference Microsoft's leadership principles in your stories: strategy, execution, teamwork, and learning. Show that you actively seek to understand different perspectives and grow from them. Internalize the idea that at Microsoft, talent + hard work + learning = success.
Practice Interview
Study Questions
Behavioral Questions with Detailed STAR Stories
Prepare comprehensive STAR responses for common behavioral questions: (1) 'Tell me about a time you solved a difficult problem.' Focus on your problem-solving process and learning. (2) 'Describe a time you worked effectively in a team.' Emphasize collaboration, communication, and shared success. (3) 'Tell me about a time you received feedback you initially disagreed with. How did you handle it?' Show humility and learning orientation. (4) 'Tell me about a project where you learned something new.' Highlight growth mindset. For entry-level, use academic projects, internship experiences, or personal projects. Make stories specific, authentic, and highlight learning.
Practice Interview
Study Questions
Frequently Asked Software Engineer Interview Questions
Given a JavaScript (Node.js) function parseDate(dateStr) that must accept multiple user-provided date formats and return a JS Date, enumerate edge cases you would test: ambiguous formats ("01/02/2020"), leap-day, timezone offsets, epoch/ISO strings, invalid strings, empty/null, and locale-specific formats. Then write three Jest unit tests that capture key edge cases and state the expected result or error.
Sample Answer
Direct answer
parseDate has to make an explicit policy decision before any test can be written: when a format is genuinely ambiguous (like "01/02/2020", which could be January 2nd or February 1st depending on locale), does the function guess using one convention, or refuse and force the caller to disambiguate? The edge cases below assume the safer senior default: reject genuine ambiguity rather than silently guessing, and only accept slash-dates where one component is unambiguously out of range for the other reading.
Structured elaboration
The edge-case categories map to distinct failure modes of a multi-format date parser:
| Category | Example | Why it's an edge case |
|---|---|---|
| Ambiguous formats | "01/02/2020" | Two equally plausible readings (US MM/DD vs rest-of-world DD/MM) exist with no locale signal in the string itself; silently picking one is a correctness bug for the other convention's users. |
| Leap-day | "2020-02-29" vs "2019-02-29" | The Gregorian calendar's leap-year rule (divisible by 4, except centuries not divisible by 400) means "is Feb 29 valid" depends on the year, and JavaScript's native Date constructor will silently ROLL invalid dates forward (new Date(2019, 1, 29) becomes March 1, 2019) instead of erroring, so a parser must explicitly re-validate. |
| Timezone offsets | "2020-06-15T10:00:00+05:30" | An offset changes the represented instant in time, not just the display, and a parser that strips or ignores the offset silently corrupts the value. |
| Epoch/ISO strings | "1700000000", "2020-06-15T10:00:00Z" | Two different "machine" representations (seconds-since-epoch vs. ISO 8601) that look superficially similar to other numeric or slash-delimited strings, so format detection has to be unambiguous, e.g. exact digit-count for epoch seconds vs. milliseconds. |
| Invalid strings | "not-a-date", "2020-13-40" | Must fail predictably (return null or throw, per the function's contract) rather than falling through to new Date("garbage"), which returns an Invalid Date object that is truthy and easy to accidentally propagate. |
| Empty/null | "", null, undefined | Distinct from "invalid string": these are absence-of-input, and a caller iterating over optional fields needs this path to be predictable and not throw a different error type than a malformed string does. |
| Locale-specific formats | "15 juin 2020", "2020年6月15日" | Out of scope for a function whose examples are all Western numeric formats; the correct behavior is to explicitly document non-support and return the same "unparseable" result as any other unrecognized string, not to half-parse it. |
Worked example
function parseDate(dateStr) {
if (dateStr === null || dateStr === undefined) return null;
if (typeof dateStr !== 'string' || dateStr.trim() === '') return null;
const s = dateStr.trim();
// ISO 8601 (date, or date+time with optional offset)
const iso = /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2}))?(Z|[+-]\d{2}:?\d{2})?)?$/;
const isoMatch = s.match(iso);
if (isoMatch) {
const [, y, mo, d, h = '00', mi = '00', se = '00', off] = isoMatch;
const dt = new Date(`${y}-${mo}-${d}T${h}:${mi}:${se}${off ? off : 'Z'}`);
if (isNaN(dt.getTime())) return null;
// Reject calendar-invalid dates the Date constructor would otherwise silently roll forward.
if (dt.getUTCFullYear() !== Number(y) || dt.getUTCMonth() + 1 !== Number(mo) || dt.getUTCDate() !== Number(d)) {
return null;
}
return dt;
}
if (/^\d{10}$/.test(s)) return new Date(Number(s) * 1000); // epoch seconds
if (/^\d{13}$/.test(s)) return new Date(Number(s)); // epoch milliseconds
// Slash format MM/DD/YYYY only; ambiguous forms are rejected rather than guessed.
const slash = /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/;
const slashMatch = s.match(slash);
if (slashMatch) {
const [, mm, dd, yyyy] = slashMatch.map(Number);
if (mm < 1 || mm > 12) return null; // first component cannot be a month at all
if (dd < 1 || dd > 31) return null;
if (dd <= 12 && dd !== mm) return null; // genuinely ambiguous: MM/DD and DD/MM both plausible
const dt = new Date(Date.UTC(yyyy, mm - 1, dd));
if (dt.getUTCMonth() !== mm - 1 || dt.getUTCDate() !== dd) return null; // e.g. 02/30/2020
return dt;
}
return null;
}
Three Jest tests, run for real (Jest 30, npx jest parseDate.test.js, all passed: "Test Suites: 1 passed, 1 total. Tests: 3 passed, 3 total."):
const { parseDate } = require('./parseDate');
test('leap-day ISO string parses to the correct UTC calendar date', () => {
const d = parseDate('2020-02-29');
expect(d).not.toBeNull();
expect(d.getUTCFullYear()).toBe(2020);
expect(d.getUTCMonth()).toBe(1); // 0-indexed: February
expect(d.getUTCDate()).toBe(29);
});
test('non-leap-year Feb 29 is rejected, not silently rolled to March 1', () => {
expect(parseDate('2019-02-29')).toBeNull();
});
test('null, empty string, and an unambiguous-invalid slash date all return null', () => {
expect(parseDate(null)).toBeNull();
expect(parseDate('')).toBeNull();
expect(parseDate('13/40/2020')).toBeNull(); // month 13 has no valid reading in either convention
});
Key points: the parser tries formats in a fixed priority order (ISO, epoch, slash) and returns null on no match rather than falling through to JavaScript's permissive native Date parsing, which is intentionally never called on unrecognized strings. Complexity: O(1) per call relative to input length (a fixed small number of regex matches against a short string); there is no loop over the string's characters beyond what the regex engine does internally. Edge cases covered by the tests above: leap-day validity (year-dependent), non-leap-day rejection (proves the parser doesn't trust the native Date object's auto-rollover), and the null/empty/unambiguous-invalid family in one test to keep the suite proportionate to a medium-difficulty question.
Trade-offs and pitfalls
The biggest pitfall is delegating straight to new Date(dateStr): it accepts a wide, engine-dependent set of formats, silently rolls invalid calendar dates forward, and returns an Invalid Date object (which is a Date instance, so typeof checks don't catch it; only isNaN(dt.getTime()) does) instead of null or throwing. A second pitfall is guessing on ambiguous formats: picking US convention by default silently mis-parses every DD/MM date from a non-US caller, and the bug is invisible in code review because both dates "look valid." The safer senior answer explicitly narrows supported formats and fails loudly and predictably outside that set, and documents that narrowing as part of the function's contract rather than leaving callers to reverse-engineer it from behavior. A third pitfall, caught by running the original version of this exact code against its own stated design goal: the first draft's slash-format guard only rejected mm > 12 and silently accepted every other slash date as MM/DD, including genuinely ambiguous ones like "01/02/2020" (both Jan 2 and Feb 1 are valid dates), which directly contradicted the "reject genuine ambiguity" policy stated above. Verified: parseDate('01/02/2020') returned a Date object instead of null under that first version. The fix (shown in the code above) adds an explicit ambiguity check, dd <= 12 && dd !== mm, so a slash date is only accepted when the day component is unambiguously out of range as a month (e.g. 03/25/2020) or when both readings agree (e.g. 05/05/2020); re-run against the same three Jest tests, all three still pass, and parseDate('01/02/2020') now correctly returns null.
Write a Python script or well-commented pseudo-code that consumes a CI pipeline JSON report with records containing: project, test_name, run_id, status (pass/fail), duration_seconds, timestamp. The script should output a prioritized list of the top 10 flaky tests across projects, where flaky = tests with >5 runs and pass rate between 40% and 95%, sorted by an impact score (failure_rate * average_duration * runs). Explain assumptions and algorithmic complexity.
Sample Answer
Approach: parse the JSON report, aggregate stats per (project, test_name), compute runs, passes, pass_rate, avg_duration, failure_rate, and impact = failure_rate * avg_duration * runs. Filter tests with runs > 5 and pass_rate between 40% and 95% (inclusive). Sort by impact descending and output top 10.
import json
from collections import defaultdict
from statistics import mean
# Example: report is a JSON array loaded from a file
def top_flaky_tests(report_json, top_n=10):
# Aggregation: key -> list of (status, duration)
agg = defaultdict(lambda: {"durations": [], "passes": 0, "runs": 0, "project": None})
for rec in report_json:
key = (rec["project"], rec["test_name"])
entry = agg[key]
entry["project"] = rec["project"]
entry["runs"] += 1
entry["durations"].append(rec.get("duration_seconds", 0) or 0)
if rec.get("status") == "pass":
entry["passes"] += 1
candidates = []
for (project, test_name), v in agg.items():
runs = v["runs"]
if runs <= 5:
continue
passes = v["passes"]
pass_rate = passes / runs
# pass rate between 40% and 95%
if not (0.40 <= pass_rate <= 0.95):
continue
avg_duration = mean(v["durations"])
failure_rate = 1 - pass_rate
impact = failure_rate * avg_duration * runs
candidates.append({
"project": project,
"test_name": test_name,
"runs": runs,
"pass_rate": pass_rate,
"avg_duration": avg_duration,
"failure_rate": failure_rate,
"impact": impact
})
# sort by impact descending and return top N
candidates.sort(key=lambda x: x["impact"], reverse=True)
return candidates[:top_n]
# Usage:
# with open("ci_report.json") as f:
# report = json.load(f)
# print(top_flaky_tests(report))
Assumptions:
- Input is a list of records (or JSON-lines) with keys shown.
- duration_seconds present or treated as 0 if missing.
- pass_rate bounds are inclusive (adjust if exclusive desired).
- Impact formula prioritizes frequent, long-running failures.
Complexity:
- Let n = number of records, m = unique tests.
- Aggregation: O(n) time, O(m) space.
- Sorting candidates: O(k log k) where k ≤ m (k is filtered tests). Overall O(n + k log k).
Write a concise status-update message (email or chat) about an in-progress piece of work. Lead with the headline (on track, at risk, or blocked), then give the supporting detail: what changed, what you need, and by when.
Sample Answer
Direct answer
Open with the headline status (on track, at risk, or blocked), then give the reader exactly what changed since the last update, what you need from them if anything, and by when, in that order.
Structured elaboration
- Headline status first, as an explicit word, not something the reader has to infer from the tone: "on track," "at risk," or "blocked." This lets a reader triage in two seconds whether they need to read further.
- What changed, briefly: the one or two things that happened since the last update that the reader doesn't already know.
- What you need, if anything, stated as a specific, actionable ask rather than a vague mention of a problem. If there's nothing needed, say so, so the reader isn't left wondering whether an ask is buried in the update.
- By when. A specific date or milestone anchors the ask; "soon" is not a deadline.
- Keep it to a few sentences. A status update that's long enough to require its own summary has failed at being a status update.
Worked example
"Status: at risk. The vendor integration is behind because their sandbox environment has been down for two days; no ETA from them yet. I need someone with an existing vendor contact to escalate on our side, since my usual contact hasn't responded since Monday. If we don't get sandbox access back by Thursday, this pushes our launch date by roughly a week."
Headline status stated explicitly, what changed (vendor outage) stated in one clause, a specific ask (escalate via a different contact) instead of a vague complaint, and a concrete deadline and consequence tied to it.
Trade-offs and pitfalls
- Burying "at risk" or "blocked" inside a paragraph of otherwise neutral-sounding detail is the single most common failure; a reader skimming quickly can miss it entirely if it's not the first word.
- Overusing "at risk" as a reflexive hedge, when things are actually fine, trains readers to stop trusting your status labels; reserve it for genuine risk.
- A status update isn't the place for a root-cause deep dive; that belongs in a linked doc or a follow-up conversation, not in the two sentences someone reads on their phone.
Prepare a live three-minute demonstration to a mixed audience showing how you reason through a tricky algorithmic trade-off (space vs time). Provide the demo script, a small example input with expected outputs, a short pseudocode sketch, and two quick audience-check questions to confirm understanding mid-demo.
Sample Answer
Intro (0:00–0:20): "Hi—I'll demonstrate reasoning about a space vs time trade-off using the 'top-k frequent elements' problem. Goal: given an array, return k most frequent elements. I'll contrast two approaches and show why you'd pick one over the other depending on constraints."
Problem & example (0:20–0:40):
Input: nums = [1,1,1,2,2,3], k = 2
Expected output: [1,2] (order doesn't matter)
Approach overview (0:40–1:10):
- Option A (low extra space): Use a min-heap of size k. Time O(n log k), Space O(k + unique).
- Option B (fast time): Use bucket sort (frequency buckets). Time O(n), Space O(n + unique).
I'll pick based on n, number of unique values (u), memory limits, and k.
Quick audience-check 1 (1:10–1:15): "If u ≫ k but memory is constrained, which approach favors memory?" (Expected quick answer: heap.)
Pseudocode sketch (1:15–1:50):
# Build freq map
freq = Counter(nums) # O(n)
# Option A: min-heap size k
heap = []
for val, f in freq.items(): # O(u)
if len(heap) < k:
heappush(heap, (f,val))
else:
if f > heap[0][0]:
heapreplace(heap, (f,val))
# result = [val for (_,val) in heap] # O(k)
Trade-off reasoning (1:50–2:25):
- If k is small and memory limited, heap uses only O(k) extra (good).
- If you need fastest possible time and can afford O(n) extra, bucket sort gives linear time by placing values into frequency-indexed lists.
- Also consider worst-case u ~ n: heap time becomes O(n log k) still good when k small; bucket uses O(n) space which may be unacceptable in embedded contexts.
Quick audience-check 2 (2:25–2:30): "If k is near u (k≈u), which approach is simpler/faster?" (Expected: bucket or simply sort by frequency — bucket or sort both OK; bucket gives O(n).)
Closing (2:30–3:00):
"Summary: pick heap when memory constrained or k small; pick bucket when you need optimal time and have memory. Always quantify n, u, k, and memory budget before choosing. Happy to dive deeper into bucket pseudocode or runtime measurements."
A GPU OOM occurs in CI but not locally. You have 30 minutes to reproduce and mitigate. Provide a prioritized, systematic plan to reproduce the OOM, find the root cause, and apply a quick mitigation so CI can continue while you investigate further.
Sample Answer
A CI-only, non-locally-reproducing GPU OOM under a hard 30-minute clock needs a plan built around commands that surface the actual memory numbers fast, not guesswork.
First 5 minutes: diff the environment with real commands
# On the CI runner (or in its logs), find the GPU type and total memory
nvidia-smi --query-gpu=name,memory.total,memory.used --format=csv
# Diff CI's job config against local: batch size, precision, GPU count
diff <(cat ci/config.yaml) <(cat local/config.yaml)
Check whether CI shares one GPU across multiple concurrent jobs (a common cost-saving CI setup): if two jobs are scheduled onto the same device, each effectively sees half the memory nvidia-smi reports as free.
Next 10 minutes: reproduce CI's exact conditions, not approximate ones
import torch
# Cap memory to match CI's actual per-job budget instead of using the full local GPU
torch.cuda.set_per_process_memory_fraction(0.5, device=0)
torch.cuda.empty_cache()
try:
run_training_step(batch_size=CI_BATCH_SIZE) # use CI's config value, not local's
except torch.cuda.OutOfMemoryError:
print(torch.cuda.memory_summary(device=0, abbreviated=True))
torch.cuda.memory_summary() shows allocated vs. reserved memory and the largest free block, which distinguishes a hard ceiling breach (allocated is genuinely near the total) from fragmentation (reserved is high but no single free block is large enough for the next allocation).
Next 10 minutes: bisect whether it is a ceiling breach or fragmentation
If memory_summary() shows many small free blocks but no contiguous block large enough for the failing allocation, it is fragmentation building up across test cases in the same process; if allocated memory alone already approaches the device total, it is a real ceiling breach (too large a batch/model for the available memory, or tensors never released between test cases).
Last 5 minutes: cheapest safe mitigation to unblock CI
# ci/config.yaml - minimal change to restore a green CI while investigation continues
gpu_tests:
batch_size: 16 # was 64; matches what CI's smaller/shared GPU can hold
parallel_jobs: 1 # was 2; stop co-scheduling two GPU-heavy jobs on one device
# add between GPU-heavy test cases as a stopgap
import gc, torch
gc.collect()
torch.cuda.empty_cache()
File the deeper investigation (genuine leak, fragmentation, or under-provisioned CI resources) as a follow-up; the batch-size/serialization change is a mitigation, not the root-cause fix.
Complexity and edge cases
No algorithmic complexity change; the mitigation trades CI throughput (smaller batches, serialized GPU jobs) for a passing pipeline. Edge case: reducing batch size can quietly change what a test is actually validating (a model behavior test tuned for batch_size=64 may behave differently at 16); note this explicitly in the follow-up ticket so the mitigation does not silently become the permanent, untracked test configuration.
You're blocked because an external team's API is returning intermittent errors and the other team is in a different timezone. Describe step-by-step what you do in the first 30 minutes to surface the blocker, communicate status to your team and stakeholders, and move toward resolution.
Sample Answer
Situation: I discover our service is failing intermittently because an external team’s API returns errors and that team is in a different timezone.
First 30 minutes — step-by-step:
- Quick verification (0–5 min)
- Reproduce the error once to confirm it’s external (curl/postman) and note timestamps, request IDs, status codes, and error bodies.
- Check recent deploys/rollbacks and internal metrics to rule out our changes.
- Triage & evidence collection (5–12 min)
- Pull logs, error rates, traces, and affected endpoints/users. Screenshot or copy key error responses.
- Identify scope: % of requests failing, user impact, whether retries help.
- Immediate mitigation attempt (12–18 min)
- If safe, enable circuit-breaker/shorter retries or route around the endpoint (fallback responses, cached data) to reduce user impact.
- Create a temporary ticket/incident in our tracker with tags and priority.
- Communicate status (18–24 min)
- Post an incident update in the team Slack channel and to product/stakeholders: one-sentence summary, impact, what we’re doing now, ETA for next update.
- Include reproducible steps, sample logs, and link to the ticket/incident doc.
- Escalate to the external team and follow-up plan (24–30 min)
- Open a concise message to the external team’s support/on-call channel or pager (include timestamps, request IDs, sample curl, correlation IDs), and mark it urgent. If out-of-hours, create a clear async handoff: what we need from them and when we’ll check back.
- Schedule follow-up checkpoints (e.g., 30/60 minutes) and assign owners internally to monitor and implement fixes.
Result / why this works:
- Confirms root cause quickly, reduces user impact with mitigations, creates a single source of truth for stakeholders, and provides the external team with actionable evidence so resolution can proceed even across timezones.
Implement a binary search tree from scratch with search, insert, and delete, handling the 0-child, 1-child, and 2-child deletion cases. Then explain what can make this tree degrade to O(n) operations, and what a self-balancing variant (AVL or red-black) does differently on insert to prevent it.
Sample Answer
Direct answer
A binary search tree (BST), a tree where every node's left subtree holds smaller keys and its right subtree holds larger keys, supports search, insert, and delete by walking down from the root using key comparisons, giving O(log n) operations only when the tree stays roughly balanced. Deleting a node has three cases depending on how many children it has: a leaf (0 children) is simply removed, a node with exactly 1 child is replaced by that child, and a node with 2 children is replaced by its in-order successor's key (the smallest key in its right subtree), after which that successor is deleted from its original position, where it is now guaranteed to have at most one child.
Structured elaboration
Approach: BST search, insert, delete
class Node:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
def bst_search(root, key):
node = root
while node is not None:
if key == node.key:
return node
node = node.left if key < node.key else node.right
return None
def bst_insert(root, key):
if root is None:
return Node(key)
if key < root.key:
root.left = bst_insert(root.left, key)
elif key > root.key:
root.right = bst_insert(root.right, key)
return root
def _min_node(root):
node = root
while node.left is not None:
node = node.left
return node
def bst_delete(root, key):
if root is None:
return None
if key < root.key:
root.left = bst_delete(root.left, key)
elif key > root.key:
root.right = bst_delete(root.right, key)
else:
if root.left is None and root.right is None:
return None # 0-child case
if root.left is None:
return root.right # 1-child case (right only)
if root.right is None:
return root.left # 1-child case (left only)
# 2-child case: replace key with in-order successor, then delete it
successor = _min_node(root.right)
root.key = successor.key
root.right = bst_delete(root.right, successor.key)
return root
Approach: why a plain BST can degrade, and what AVL does differently
- A plain BST's height depends entirely on insertion order: inserting already-sorted keys (or reverse-sorted keys) builds a tree that is really a linked list in disguise, one child per node, giving O(n) search, insert, and delete instead of O(log n).
- An AVL tree (a self-balancing BST named for its inventors, Adelson-Velsky and Landis) prevents this by tracking a height at every node and, after every insert, walking back up and checking a balance factor (the height of the right subtree minus the height of the left subtree) at each ancestor. If the balance factor ever reaches +-2, a rotation restructures that subtree back to +-1; this happens on the way back up from the newly inserted node, so no ancestor is ever left unbalanced for more than the single insert that caused it.
- The specific rotation applied depends on where the imbalance shows up: a single rotation fixes a "straight-line" imbalance (left-left or right-right), and a double rotation (rotating the child first, then the node itself) fixes a "zig-zag" imbalance (left-right or right-left).
- A red-black tree solves the same degradation problem with a different, looser invariant, a coloring rule rather than a strict height-balance rule, trading a slightly taller worst-case tree for fewer rotations per insert.
def _h(node):
return node.height if node else 0
def _update_height(node):
node.height = 1 + max(_h(node.left), _h(node.right))
def _balance_factor(node):
return _h(node.right) - _h(node.left)
def _rotate_left(x):
y = x.right
x.right = y.left
y.left = x
_update_height(x)
_update_height(y)
return y
def _rotate_right(y):
x = y.left
y.left = x.right
x.right = y
_update_height(y)
_update_height(x)
return x
def avl_insert(root, key):
if root is None:
node = Node(key)
node.height = 1
return node
if key < root.key:
root.left = avl_insert(root.left, key)
elif key > root.key:
root.right = avl_insert(root.right, key)
else:
return root
_update_height(root)
bf = _balance_factor(root)
if bf > 1: # right-heavy
if _balance_factor(root.right) < 0:
root.right = _rotate_right(root.right) # RL case
return _rotate_left(root)
if bf < -1: # left-heavy
if _balance_factor(root.left) > 0:
root.left = _rotate_left(root.left) # LR case
return _rotate_right(root)
return root
Key points
- Search, insert, and delete on a BST are all O(height), so the entire performance story of a BST reduces to controlling its height.
- The delete case that needs the most care is the 2-child case: the node cannot simply be removed, a replacement key must be found that preserves the ordering invariant, and the in-order successor (or equivalently, the in-order predecessor) is the only choice that doesn't require restructuring more than one path.
- AVL's rebalancing only ever looks at the path from the inserted node back to the root, keeping a single insert's rebalancing cost proportional to the tree's height, not its size.
Worked example
Building a BST from [5, 3, 8, 2, 4, 7, 9] via repeated bst_insert, an in-order traversal prints [2, 3, 4, 5, 7, 8, 9], confirming the BST property. Deleting 2 (a leaf, the 0-child case) leaves [3, 4, 5, 7, 8, 9]. Deleting 3 next (now a 1-child case, since 3's only remaining child is 4) leaves [4, 5, 7, 8, 9]. Deleting 5, the root (a 2-child case), replaces its key with its in-order successor, 7, then removes the original 7 from the right subtree, leaving [4, 7, 8, 9].
To see the degradation: inserting [1, 2, 3, 4, 5, 6, 7] in sorted order into a plain BST via bst_insert produces a tree of height 7 (a straight chain, one child per node, for n = 7 nodes, the O(n) worst case). Running the same 7 keys through avl_insert instead produces a tree of height 3, and an in-order traversal still prints [1, 2, 3, 4, 5, 6, 7], confirming the rebalancing preserved the BST property while keeping the tree flat.
Trade-offs & pitfalls
Complexity
Plain BST: search, insert, and delete are all O(h), where h is the tree's height; h ranges from O(logn) (balanced) to O(n) (degenerate, such as sorted-order insertion).
AVL: search, insert, and delete are all O(logn) worst case, since the height-balance invariant guarantees h=O(logn) regardless of insertion order; each insert does O(logn) work walking back up, plus at most a constant number of rotations.
Space: O(n) for the tree itself; O(h) additional stack space for the recursive implementations shown here.
Edge cases
- Deleting a node with 2 children whose in-order successor is itself a leaf: the recursive
bst_deletecall on the successor correctly falls into the 0-child case. - Deleting the root: handled the same as any other node, since the function returns the (possibly new) subtree root at every level.
- Inserting a duplicate key: the implementation shown ignores duplicates; a production version needs to decide up front whether duplicates are allowed and where they go if so.
- Empty tree: search and delete both return
Nonesafely; insert on an empty tree creates the first node.
A common bug in from-scratch delete implementations is fixing up the tree's shape but forgetting to also update any augmented metadata (heights, subtree sizes, color bits) on every node along the path back to the root; for AVL specifically, forgetting to update height before computing the balance factor at a node makes every rebalancing decision above it wrong. A second pitfall is choosing the in-order predecessor instead of the in-order successor for the 2-child case inconsistently across an implementation; either works, but mixing them without matching invariant logic can subtly break ordering.
You are given an array of n+1 integers where each value is between 1 and n (inclusive). Prove and implement an algorithm to find a duplicate value in O(n) time and O(1) extra space without modifying the array. (Hint: use cycle detection/floyd's algorithm treating indices as pointers.)
Sample Answer
Direct answer
Treat each value in the array as a pointer: from index i, "follow" nums[i] to land on index nums[i]. Because there are n+1 values all in the range [1, n], at least two different indices must point to the same value (pigeonhole), which means this functional graph has a cycle, and the duplicate value is exactly the entry point of that cycle. Floyd's tortoise-and-hare cycle detection finds that entry point in O(n) time and O(1) extra space, without modifying the array at all, which is exactly what the question asks for.
Approach (Floyd's cycle detection)
- Start both
slowandfastatnums[0], i.e. one step into the implicit linked structure (index 0 always has an outgoing "pointer," but nothing points back to it, so it can't be part of the cycle itself, only the tail leading into it). - Advance
slowone step (slow = nums[slow]) andfasttwo steps (fast = nums[nums[fast]]) each iteration until they meet; a meeting point is guaranteed to exist since the structure has a cycle (standard tortoise-and-hare argument). - Reset a second pointer to index 0, then advance it and
slowone step at a time together; the index where they meet is the cycle's entry point, which is the duplicate value.
Complexity
Time: O(n) (each phase does at most O(n) steps). Space: O(1) extra; nums itself is never modified.
Edge cases
- Exactly one duplicate value, appearing exactly twice: this is the assumed input shape and the algorithm handles it directly.
- The duplicate value equal to
nitself (the largest allowed value): handled the same way, since indexing is 0-based but values start at 1, sonums[i]is always a valid index regardless of which value 1..n is duplicated.
def find_duplicate_floyd(nums):
slow = nums[0]
fast = nums[nums[0]]
while slow != fast:
slow = nums[slow]
fast = nums[nums[fast]]
slow2 = 0
while slow2 != slow:
slow2 = nums[slow2]
slow = nums[slow]
return slow
data = [1, 3, 4, 2, 2]
original = list(data)
print(find_duplicate_floyd(data), data == original)
Output:
2 True
The duplicate is correctly identified as 2, and data == original confirms the array was never mutated during the search.
Alternative technique: index-marking
A second valid approach exploits the same "values are indices" fact differently: walk the array once, and for each value, negate the entry at the index that value points to (abs(value) - 1). If you ever land on an index whose entry is already negative, that index (converted back to 1-based) is the duplicate, because it means two different positions "pointed" to it. This is also O(n) time and O(1) additional space, but unlike Floyd's approach, it works by temporarily mutating nums in place (each visited value's target slot gets negated), so if the caller needs nums to remain externally unmodified while the function runs (not just restored by the time it returns), Floyd's version is the safer default.
def find_duplicate_marking(nums):
duplicate = None
for x in nums:
idx = abs(x) - 1
if nums[idx] < 0:
duplicate = idx + 1
break
nums[idx] = -nums[idx]
for i in range(len(nums)):
nums[i] = abs(nums[i])
return duplicate
data2 = [1, 3, 4, 2, 2]
print(find_duplicate_marking(data2), data2)
Output:
2 [1, 3, 4, 2, 2]
Both techniques agree on the duplicate (2), and the marking approach restores the array to its original values by the time it returns, even though it mutated it during the scan.
Trade-offs and pitfalls
- "Does not modify the array" has two readings, and the question's phrasing ("without modifying the array") most naturally means Floyd's guarantee: never mutated, at any point, including during execution. The marking approach only satisfies a weaker version ("unmodified once the function returns"), which is a meaningful difference if another thread could read
numsconcurrently while this function runs, or if the function could throw partway through and leave the array in its negated state. - A frequent proof gap: candidates often reach for cycle detection without first establishing why a cycle must exist here. The argument is exactly pigeonhole: n+1 values drawn from a range of only n possible values guarantees at least one repeat, and because every value is a valid index (never 0, since the range is [1, n] not [0, n-1]), the "value points to index" structure is well-defined for every position, forcing at least one node in the sequence to be revisited, i.e. a cycle.
- A common bug in the marking approach: forgetting the final restoration pass, which silently corrupts the caller's array (still functionally finds the right duplicate, but violates the "don't modify the array" requirement in a way that's easy to overlook if you only test the return value).
Given a DAG where multiple valid topological orders exist, implement a deterministic topological sort that returns the lexicographically smallest (by node id) valid order. Implement def topo_lex(graph) -> Optional[List[int]] in Python using Kahn's approach with tie-breaking. Detect cycles and return None when DAGness is violated.
Sample Answer
Direct answer
Plain Kahn's algorithm with a FIFO queue produces a valid topological order, but which one depends on insertion order among tied, simultaneously-available vertices. To force the lexicographically smallest valid order (smallest by node id, compared position by position), replace the queue with a min-heap: at every step, among all vertices currently free of unmet dependencies, always emit the smallest id. This is a small structural change with a real complexity cost: O((V+E)logV) instead of O(V+E), because every insertion and extraction on the heap costs O(logV).
Structured elaboration
The algorithm is Kahn's algorithm verbatim except for one substitution: swap deque for heapq. Correctness for cycle detection is unchanged (a cycle still means some vertices never reach in-degree 0, so the output stays short); the only new property is that whenever more than one vertex is simultaneously eligible, the heap always yields the smallest one first, which is exactly the greedy rule that produces the lexicographically smallest sequence: committing to the smallest available choice at every position, given that any later choice is still available to be picked in a later position if it becomes newly eligible.
Why greedy-smallest-first is provably correct here (not just plausible). Suppose the true lexicographically smallest valid order picks vertex x at some position, but x is not the smallest currently-eligible vertex; call the smallest eligible one y<x. Since y has no unmet dependency, nothing prevents placing y at that position instead, and y's own dependents only become eligible later regardless of whether y is placed now or later, so swapping y into that earlier position can only make the sequence lexicographically smaller or equal, never invalid. This is the standard exchange argument for greedy algorithms; it is why the heap substitution alone (no other logic change) is sufficient.
Worked example
import heapq
from typing import Dict, List, Optional
def topo_lex(graph: Dict[int, List[int]]) -> Optional[List[int]]:
# Kahn's algorithm with a min-heap instead of a FIFO queue: among all
# nodes currently available (indegree 0), always pop the smallest id.
# Returns the lexicographically smallest valid topological order, or
# None if the graph has a cycle.
indegree = {u: 0 for u in graph}
for u in graph:
for v in graph[u]:
indegree[v] += 1
heap = [u for u in graph if indegree[u] == 0]
heapq.heapify(heap)
order = []
while heap:
u = heapq.heappop(heap)
order.append(u)
for v in graph[u]:
indegree[v] -= 1
if indegree[v] == 0:
heapq.heappush(heap, v)
if len(order) != len(graph):
return None
return order
if __name__ == "__main__":
dag = {5: [2, 0], 4: [0, 1], 2: [3], 3: [1], 0: [], 1: []}
result = topo_lex(dict(dag))
print("Lexicographically smallest order:", result)
def is_valid_topo(order, graph):
pos = {n: i for i, n in enumerate(order)}
return all(pos[u] < pos[v] for u in graph for v in graph[u])
print("Valid topological order:", is_valid_topo(result, dag))
from itertools import permutations
nodes = list(dag.keys())
valid_orders = [list(p) for p in permutations(nodes) if is_valid_topo(list(p), dag)]
brute_min = min(valid_orders)
print("Brute-force minimum over all valid orders:", brute_min)
print("Matches heap-based result:", brute_min == result)
cyclic = {0: [1], 1: [2], 2: [0]}
print("Cyclic graph result:", topo_lex(dict(cyclic)))
Output (actually executed with python3):
Lexicographically smallest order: [4, 5, 0, 2, 3, 1]
Valid topological order: True
Brute-force minimum over all valid orders: [4, 5, 0, 2, 3, 1]
Matches heap-based result: True
Cyclic graph result: None
The brute-force check enumerates every permutation of the six vertices, keeps only the ones that respect every edge, and takes the minimum by standard list (lexicographic) comparison. This is only feasible for the toy example (6!=720 permutations) and exists purely to independently confirm the heap-based algorithm's exchange argument holds on a real case, not as a scalable approach in itself.
Complexity
Time O((V+E)logV): every vertex is pushed and popped from the heap once (O(VlogV) total), and every edge triggers at most one additional push when its target's in-degree reaches zero (O(ElogV) total). Space O(V) for the heap and in-degree map, plus O(V+E) for the adjacency list.
Edge cases
- All vertices tied at in-degree 0 (a totally disconnected graph): the heap degenerates to simply popping vertices in ascending id order, which is correct and matches the lexicographically-smallest definition trivially.
- A long single chain (0→1→2→…): only one vertex is ever eligible at a time, so the heap never actually has a choice to make; the result is forced and identical to what plain Kahn's algorithm would produce.
- Cycle: identical detection to plain Kahn's algorithm,
Nonewhen the output falls short oflen(graph). - Negative or non-integer ids: the heap comparison works for any totally ordered, hashable type, so this generalizes to string ids (alphabetical order) without changing the algorithm, only the type annotation.
Trade-offs and pitfalls
- Common mistake: assuming a plain, unmodified Kahn's algorithm with a FIFO queue already gives the lexicographically smallest order "because it processes in the order things become available." That is false in general: two vertices can become eligible in the same round, and FIFO preserves insertion order (which reflects the order their prerequisites happened to be processed in), not numeric order. Only replacing the queue's tie-breaking mechanism with an explicit min-heap (or a per-round sort) fixes this.
- Common mistake: sorting the entire vertex list once at the start and iterating in that fixed order while checking in-degree, instead of using a heap. This looks similar but is wrong: a vertex with a smaller id can become eligible several rounds after a vertex with a larger id, and a single static sort pass cannot re-examine a vertex once skipped in an earlier scan, or would need to re-scan the whole list every round, which is O(V) per round instead of O(logV) per operation.
- This exact technique (a min-heap-based topological sort relying on the same exchange-argument correctness) shows up repeatedly in practice, confirming it is a stable, recognized approach rather than an unusual one-off construction; the min-heap substitution is the standard way this requirement is solved, not a workaround.
- The O(logV) factor genuinely matters at scale: for a graph with V=106 vertices, plain Kahn's algorithm and the lexicographic variant differ by roughly a factor of 20, which is worth naming explicitly if a system does not actually need reproducible ordering and is paying this cost for no functional benefit.
Tell me about a time internal or external pressure, such as a deadline, a client, or a business commitment, pushed you toward a decision that conflicted with a principle or value your company had explicitly committed to (for example privacy, security, or data quality). Walk through how you recognized the conflict, what you did about it, how you communicated your position to stakeholders, and what the final outcome was.
Sample Answer
Direct answer
When a deadline, a client, or a business ask pushes toward something that conflicts with a principle a company has committed to, such as privacy, security, or data quality, the strongest answers show three things: you noticed the conflict explicitly rather than complying without registering it, you raised it through the right channel rather than either silently complying or unilaterally blocking the work, and you drove toward a resolution rather than just splitting the difference.
Structured elaboration
- Notice: name the specific moment you recognized the tension, and what concrete detail made you pause.
- Raise it: describe how you raised it, ideally backed by data or a concrete risk rather than an appeal to principle alone. A values-based objection lands far better when it is backed by the actual risk it protects against.
- Navigate: what you actually did in the interim, whether you proposed a compromise or a phased approach, who you looped in, and how you kept the relationship functional even while disagreeing.
- Outcome: what actually happened. An honest outcome, including "I was overruled and here is what I did next," is often more credible than a suspiciously clean win.
Worked example
A team was under pressure to ship a change quickly, and the fastest path meant skipping a validation step that existed specifically to catch a known class of data-quality problem. Rather than quietly skipping it or unilaterally blocking the release, the response was to time-box a reduced version of the validation, checking the highest-risk subset in the time available, and to flag explicitly and in writing what wasn't covered and what the residual risk was, so the decision to accept that risk was made deliberately by the right people rather than by default. The release shipped on time, and the flagged gap was closed within the following two days as agreed, rather than being silently forgotten.
Trade-offs and pitfalls
A story where you unilaterally blocked the work and were later vindicated can read as inflexible if it doesn't also show you understood the business pressure; the strongest answers show empathy for that pressure while still holding the line. A story where you quietly went along with the shortcut is not really an example of this competency at all; the action needs to show you actively surfaced the tension, not merely noticed it internally. Vague appeals to "our values" without a concrete risk attached tend to land weaker than a specific technical or business risk, clearly stated.
Recommended Additional Resources
- LeetCode (leetcode.com) - Practice 100+ coding problems, prioritizing arrays and strings (Easy to Medium difficulty), linked lists, and trees. Filter by topic to practice systematically.
- NeetCode (neetcode.io) - Video explanations of LeetCode problems categorized by patterns and difficulty, including blind 75 curated problems.
- Cracking the Coding Interview by Gayle Laakmann McDowell - Comprehensive book covering interview strategies, problem-solving methodologies, and 150+ problems with solutions.
- Interview Cake (interviewcake.com) - Detailed explanations of coding problems with interactive walkthroughs and algorithm breakdowns.
- Pramp (pramp.com) - Free mock interview platform where you practice with real people, get feedback, and refine your interview skills.
- Exponent (tryexponent.com) - Interactive platform with mock interviews, problem explanations, and personalized feedback.
- Microsoft Careers official page (careers.microsoft.com) - Learn about Microsoft's culture, products, and open positions. Study Microsoft's leadership principles and growth mindset philosophy.
- YouTube channels - NeetCode, LeetCode Official, Coding.Ninja, and Techlead provide video explanations of coding problems and interview strategies.
- Azure Fundamentals documentation - Optional but valuable: learn basics of cloud computing, Azure services, and Microsoft's primary cloud platform.
- System Design Primer (GitHub repository) - Optional for entry-level but useful foundational knowledge: covers scalability, databases, caching, and distributed systems at a conceptual level.
Search Results
Top Microsoft Interview Questions 2025
Microsoft's interview process is rigorous but fair, typically spanning 4-8 weeks and focusing on problem-solving, collaboration, and cultural ...
Microsoft software engineer interview (questions, process ...
The most common is a three-question test on Codility, which you'll have 60 to 90 minutes to complete. The questions are typical data structure ...
Microsoft Software Engineer Interview Questions & Process ...
How Long Does the Microsoft SWE Interview Process Take? On average, the Microsoft Software Engineer interview process takes about 3 to 5 weeks.
Microsoft L63-64 Interview Guides & Questions (2025)
The Microsoft L63 and L64 senior software engineer interview process typically starts with a recruiter screen, followed by either an online coding ...
Microsoft Software Engineer Interview Experience - Redmond ...
Panel interview: 1 hour each, 3 rounds. Questions ranged from strings to linked lists, with an emphasis on many behavioral questions. Questions.
How we hire | Microsoft Careers
Most interviews include 2-4 conversations with potential teammates and cross-functional colleagues, each lasting up to an hour. · Interviews may take place over ...
This interview preparation guide was generated using AI-powered research from the sources listed above. While we strive for accuracy, we recommend verifying critical information from official company sources.
Want to create your own tailored preparation guide using our deep research?
Get Started for FreeInterview-Ready Courses
Visual-first, interactive, structured learning paths
Browse Software Engineer jobs
AI-enriched listings across hundreds of company career pages
Explore Jobs