Google Software Engineer Entry Level (L3) Interview Preparation Guide
Google's entry-level software engineer interview process (L3) focuses heavily on fundamental coding skills, data structures, and algorithms. The interview consists of a recruiter screening call, a technical phone screen, and three technical coding rounds conducted virtually or in-person, followed by a behavioral assessment. Entry-level candidates are expected to demonstrate strong problem-solving abilities, clean coding practices, and cultural alignment with Google's values. The entire process typically spans 4-8 weeks.
Interview Rounds
Recruiter Screening
What to Expect
Your first interaction with Google, this 20-30 minute call is conducted by a Google recruiter via phone or video. The recruiter will verify your basic qualifications, discuss your educational background and relevant experience, assess your communication skills, and gauge your genuine interest in the company and role. This round focuses on confirming baseline fit before investing time in technical evaluations. The recruiter will explain the role, team dynamics, and provide clarity on the interview process and timeline.
Tips & Advice
Be genuine and concise when describing your background, focusing on software development projects, internships, and coursework. Highlight relevant programming languages (Java, Python, C++, JavaScript) and any frameworks or tools you've used. Show authentic enthusiasm for Google's products and mission—reference specific products or projects that excite you rather than generic praise. Ask thoughtful questions about the team and role to demonstrate genuine interest. Prepare a 2-3 minute summary of your experience emphasizing your coding ability and problem-solving mindset. Speak clearly, avoid unnecessary jargon, and be honest about your skill level—overselling leads to mismatches later.
Focus Topics
Learning Mindset and Adaptability
For entry-level roles, emphasize your enthusiasm for learning and growth. Share examples of picking up new programming languages, frameworks, or technologies on your own. Show comfort with challenges and explain how you approach learning new domains. Demonstrate that you view obstacles as learning opportunities.
Practice Interview
Study Questions
Motivation and Alignment with Google
Articulate specific reasons for wanting to work at Google beyond generic statements like 'it's a great company.' Reference particular Google products you use, engineering challenges Google solves, or aspects of their culture that resonate with you. Connect your career goals and values with what Google offers.
Practice Interview
Study Questions
Communication and Interpersonal Skills
Demonstrate clear articulation when explaining technical concepts, active listening when the recruiter speaks, and ability to ask thoughtful follow-up questions. Show collaboration mindset by discussing past team experiences. Your communication style during this call predicts how you'll participate in code reviews and cross-functional collaboration.
Practice Interview
Study Questions
Professional Background and Technical Foundation
Concisely communicate your educational background, internships, personal projects, and relevant work experience in software development. Clearly describe programming languages and frameworks you're proficient in, showing honesty about competency levels. For entry-level, internship experiences and substantial academic projects demonstrate capability.
Practice Interview
Study Questions
Technical Phone Screen
What to Expect
Your first technical evaluation, this 45-60 minute interview is conducted via video call using Google Meet or Hangouts. You'll write code in a shared Google Doc or collaborative coding platform with screen sharing required. The interviewer will present 1-2 algorithmic problems of medium difficulty that you're expected to solve in real-time. You'll discuss your approach, write functional code, handle edge cases, and explain your solution's time and space complexity. This round directly assesses your fundamental coding ability, problem-solving methodology, and understanding of core data structures and algorithms.
Tips & Advice
Before writing code, ask clarifying questions about problem constraints, input ranges, and expected output format. Verbalize your approach and discuss your algorithm strategy before implementing—this demonstrates clear thinking and allows the interviewer to provide guidance if you're headed in the wrong direction. Write clean, readable code using meaningful variable names and proper indentation. Aim to write approximately 30 lines of code per problem. Explicitly identify and handle edge cases like empty inputs, single elements, or boundary values. Analyze your solution's time and space complexity, and discuss potential optimizations. If stuck, think out loud and ask for hints rather than sitting silently. Test your solution mentally with provided examples before declaring it complete. Practice coding on Google Docs beforehand to become comfortable with the environment and its quirks.
Focus Topics
Time and Space Complexity Analysis
Calculate Big O complexity for your solutions. Understand when O(n²) might be acceptable versus when O(n log n) is necessary. Recognize common complexity patterns: O(1), O(log n), O(n), O(n log n), O(n²). Trade-offs between time and space—sometimes using extra memory improves speed. Discuss optimization opportunities.
Practice Interview
Study Questions
Clean Code and Clear Communication
Write code with descriptive variable names that reveal intent. Use consistent formatting and indentation. Include brief comments for non-obvious logic. Explain your solution approach clearly as you code. Discuss edge cases and defensive programming techniques. Your code should be readable and maintainable by others.
Practice Interview
Study Questions
Problem Decomposition and Algorithm Development
Ability to break complex problems into manageable components. Start by understanding the problem thoroughly, then develop a brute force solution, then optimize. Recognize problem patterns and apply standard algorithms. Validate assumptions about the problem with the interviewer.
Practice Interview
Study Questions
Arrays and String Problem-Solving
Master array and string manipulation including indexing, iteration, element searching, and transformation. Understand two-pointer technique for solving problems efficiently, sliding window approach for contiguous subarrays, and prefix sum technique for range queries. Be comfortable working with both 1D and 2D arrays. Practice substring and subarray problems, character frequency analysis, and string pattern matching.
Practice Interview
Study Questions
Data Structures Fundamentals and Selection
Deep understanding of arrays, linked lists, stacks, queues, hash tables, and sets. Know time and space complexities for each operation: insertion, deletion, search, and access. Understand when to use each data structure to solve different problem types. Recognize tradeoffs—hash tables provide fast lookup but use more memory; linked lists enable efficient insertion but require traversal for search.
Practice Interview
Study Questions
Onsite Coding Interview 1
What to Expect
The first of three onsite technical rounds (approximately 45 minutes), this interview focuses on coding problems involving arrays, strings, and basic data structure manipulation. You'll write code on a whiteboard, provided Chromebook, or virtual editor depending on your office location. The interviewer presents a medium-difficulty problem. You're expected to discuss your approach, write clean functional code, handle edge cases explicitly, and demonstrate optimization thinking. This round assesses your foundational problem-solving skills, coding proficiency, and ability to explain technical reasoning.
Tips & Advice
Treat the whiteboard or Chromebook like a production code editor—write clean, well-formatted code. Verify the problem requirements with the interviewer before solving. Discuss your approach and algorithm before writing code. Write out your solution step-by-step, explaining your logic for each section. Test your solution with provided examples and edge cases. If you make mistakes, explain what you'd correct rather than erasing everything repeatedly. For whiteboards, write larger and clearer than feels natural. Practice your preferred programming language on whiteboards and Chromebooks beforehand—the environment is different from your IDE and requires adaptation.
Focus Topics
Code Organization and Professional Standards
Write functions with clear, single responsibilities. Use variable names that reveal intent rather than cryptic abbreviations. Follow consistent formatting conventions and proper indentation. Include brief comments explaining non-obvious logic. Your code should be readable by others without extensive explanation.
Practice Interview
Study Questions
Solution Testing and Verification
Before declaring your solution complete, trace through it with provided test cases. Verify output correctness. Explicitly test edge cases you identified. Discuss your testing approach with the interviewer. Be willing to catch and fix bugs when you spot them.
Practice Interview
Study Questions
String Processing and Character-based Problems
Practice string algorithms: character frequency counting using hash tables, pattern detection, substring searches, and string transformations. Understand when to use different approaches—sometimes iterating character by character, sometimes using built-in string methods. Solve problems involving palindromes, anagrams, and string matching.
Practice Interview
Study Questions
Edge Case Identification and Defensive Coding
Before coding, identify potential edge cases: empty inputs, single elements, duplicates, negative numbers, very large values, and boundary conditions. Explicitly code defensive checks. Discuss edge cases with the interviewer to demonstrate thorough analytical thinking. Robust code handles unexpected inputs gracefully rather than crashing.
Practice Interview
Study Questions
Array Manipulation Techniques and Algorithms
Master common array algorithms: two-pointer technique for finding pairs, sliding window for subarray problems, prefix sums for range calculations, and binary search for sorted arrays. Practice in-place modifications and understand when they're appropriate. Solve problems involving rotations, removals, rearrangements, and searches within arrays.
Practice Interview
Study Questions
Onsite Coding Interview 2
What to Expect
The second onsite technical round (approximately 45 minutes), this interview focuses on more complex data structures including trees, graphs, and linked lists. The problem is typically medium to medium-hard difficulty and often involves tree traversal algorithms, graph exploration, or linked list manipulation. You'll write code on a whiteboard or Chromebook. This round assesses your understanding of hierarchical and networked data structures, recursive thinking, and ability to implement standard graph/tree algorithms.
Tips & Advice
Draw the data structure before coding to visualize the problem and think through your approach. Recursion is often useful for tree problems—think recursively about how to solve the problem on smaller subtrees. For graph problems, carefully consider whether to use depth-first search (DFS) or breadth-first search (BFS). Clearly define your base cases for recursion before implementing. For linked lists, be careful with pointer manipulation—consider using a temporary node to avoid losing references. Trace through your recursive logic with a small example to verify correctness before claiming your solution works.
Focus Topics
Algorithm Selection and Complexity Trade-offs
For each problem, discuss multiple approaches before implementing. Compare their time and space complexity. Understand when a slightly less efficient algorithm might be better due to simplicity, easier implementation, or reduced space usage. Make informed design decisions about trade-offs.
Practice Interview
Study Questions
Systematic Debugging and Problem-Solving Approach
When stuck, break the problem into smaller pieces and solve incrementally. Trace through your algorithm step-by-step with concrete examples. Identify assumptions you've made and validate them. Use clarifying questions rather than getting stuck silently. When bugs appear, use hand-tracing or print statements to identify the issue.
Practice Interview
Study Questions
Recursion and Backtracking Techniques
Understand recursive function design: defining base cases clearly, making recursive calls with reduced problem size, and combining results. Master backtracking for constraint-based problems like permutations, combinations, and solving puzzles. Recognize when recursion is appropriate versus iteration. Understand implications of recursion depth and stack usage.
Practice Interview
Study Questions
Graph Representation and Graph Algorithms
Understand different graph representations: adjacency lists and adjacency matrices. Master graph traversal algorithms—depth-first search (DFS) and breadth-first search (BFS). Understand shortest path algorithms, cycle detection, topological sorting, and connected components. Know when to use each approach based on problem requirements.
Practice Interview
Study Questions
Binary Trees and Tree Traversal Algorithms
Master tree data structures and all traversal methods: inorder, preorder, postorder, and level-order (breadth-first search). Understand binary search tree properties and validation. Practice problems involving tree construction, modification, path finding, and level-based operations. Solve problems about tree balancing, lowest common ancestors, and serialization.
Practice Interview
Study Questions
Onsite Coding Interview 3
What to Expect
The third onsite technical round (approximately 45 minutes), this interview covers a diverse range of topics including hash tables, linked lists, dynamic programming basics, and other algorithmic concepts. The problem is typically medium difficulty with possible challenging elements. You'll write code on a whiteboard or Chromebook. This round assesses your versatility in selecting and implementing the right algorithm or data structure for different problem types, and often serves as your opportunity to demonstrate growth from previous rounds.
Tips & Advice
By the third technical round, apply lessons learned from previous interviews. For hash table problems, think about when hashing is optimal and how to handle collisions. For linked lists, be meticulous with pointer manipulations—consider drawing the list as you make changes. For dynamic programming problems, identify overlapping subproblems and think about memoization to cache results. Maintain the same professional coding standards from earlier rounds. This is your opportunity to demonstrate growth if earlier rounds had rough moments—show you listened to feedback and adapted. Stay composed and methodical even as complexity increases.
Focus Topics
Dynamic Programming Fundamentals and Memoization
Understand the concept of overlapping subproblems and optimal substructure. Practice basic dynamic programming problems: fibonacci, coin change, climbing stairs, and longest increasing subsequence. Understand memoization as a top-down optimization approach. Recognize when a problem can be solved more efficiently with DP.
Practice Interview
Study Questions
Incorporating Feedback and Demonstrating Growth
By round three, interviewers may point out improvements to your approach or coding style. Listen carefully to feedback, understand suggestions, and incorporate them into your solution. Show your willingness to learn and improve based on input. Demonstrate that you value collaborative refinement.
Practice Interview
Study Questions
Standard Algorithm Implementation
Be able to implement common algorithms from scratch: sorting algorithms (merge sort, quick sort), searching (binary search), and other frequently needed functions. Understand when to use built-in library functions versus implementing from scratch. Explain trade-offs in different implementations.
Practice Interview
Study Questions
Linked List Operations and Pointer Manipulation
Master linked list operations: insertion at any position, deletion, reversal, cycle detection, finding kth element from end, and merging lists. Understand pointer manipulation and be careful with null reference handling. Practice problems requiring multiple passes through the list and two-pointer techniques adapted for linked lists.
Practice Interview
Study Questions
Hash Tables and Efficient Lookup Problems
Understand hash table implementation fundamentals, collision handling strategies, and when hash tables provide O(1) average case performance. Master using hash tables for frequency counting, duplicate detection, two-sum type problems, and caching scenarios. Solve problems where hashing enables significant efficiency improvements.
Practice Interview
Study Questions
Onsite Behavioral Interview - Googliness
What to Expect
This 45-minute behavioral interview assesses cultural fit and alignment with Google's values, referred to as 'Googliness.' The interviewer will ask open-ended questions about your past experiences, how you handle challenges and failures, your collaboration style, learning approach, and whether you share Google's mission to organize the world's information and make it universally accessible. Unlike some companies with formal leadership principles, Google seeks to understand your thinking patterns, emotional intelligence, and intrinsic motivation. For entry-level candidates, the focus is on potential to grow, learning mindset, and collaborative spirit rather than past management experience.
Tips & Advice
Use the STAR method (Situation, Task, Action, Result) to structure answers with specific, concrete examples rather than abstract principles. Prepare 5-6 stories that demonstrate different qualities: successful collaboration, overcoming technical challenges, learning from failure, taking initiative beyond assigned tasks, handling disagreement, and contributing to team success. Research Google's actual products, services, and company mission—understand what they do and why. For entry-level, emphasize your learning ability, curiosity, eagerness for growth, and enthusiasm about contributing to meaningful work. Show you've reflected on how your work connects to something larger than yourself. Be authentic rather than polished—Google values genuine answers. For questions about conflicts, focus on what you learned and how you'd handle similar situations differently. Prepare thoughtful questions showing you've researched the company and thought about your fit.
Focus Topics
Inclusion, Diversity, and Ethical Thinking
Show your perspective on building inclusive products and environments. Discuss how you think about different user needs, accessibility considerations, and diverse perspectives. Demonstrate ethical thinking about technology's impact. For entry-level, show understanding that your code affects real users with varying needs and circumstances.
Practice Interview
Study Questions
Communication and Clear Explanation
Practice explaining technical concepts clearly to non-technical audiences. Show you can listen carefully to questions and provide relevant, focused answers. Demonstrate self-awareness about communication—adjust complexity and depth based on audience. Show you ask clarifying questions rather than making assumptions.
Practice Interview
Study Questions
Learning Mindset and Growth from Failure
For entry-level, this is particularly important. Share examples of learning new technologies independently, recovering gracefully from failures or mistakes, seeking feedback actively, and adapting approaches based on input. Describe challenging situations that pushed you to grow. Show you view setbacks as learning opportunities rather than defeats. Demonstrate excitement about stretching into unfamiliar areas.
Practice Interview
Study Questions
Proactive Problem-Solving and Taking Initiative
Share examples of identifying problems proactively without being told, proposing solutions, taking ownership of challenges, and persisting through obstacles. Discuss how you approach complex problems by breaking them down and exploring multiple approaches. Show intellectual curiosity and your drive to solve hard problems. Demonstrate initiative in learning new technologies or tackling unfamiliar domains.
Practice Interview
Study Questions
Collaboration and Cross-Functional Teamwork
Share specific examples of successfully working with diverse team members including people with different backgrounds, perspectives, and expertise. Describe how you contribute to team success, handle disagreements constructively, build productive relationships, and create inclusive environments. Demonstrate you listen actively to different viewpoints and value diverse perspectives. Show how you've supported teammates in achieving shared goals.
Practice Interview
Study Questions
Frequently Asked Software Engineer Interview Questions
You have a partial hotfix that restores some functionality but not all. Explain how you would preserve the working state, document temporary changes, and avoid introducing regressions before committing or rolling back. Include steps for creating a safe patch, capturing relevant logs/diagnostics, and writing a follow-up plan for the permanent fix.
Sample Answer
A partial hotfix, restoring some functionality but not all, needs careful handling so the working state isn't accidentally lost and the remaining gap is clearly tracked, not silently forgotten.
Approach
- Preserve the working state explicitly: commit the partial fix on its own clearly-labeled branch/commit rather than mixing it with further in-progress changes, so it can be safely deployed or rolled back to independently.
- Document exactly what's fixed and what isn't, in the commit message and in a tracked follow-up item, including which specific scenarios remain broken, so the gap doesn't get discovered again from scratch by whoever picks it up next.
- Capture logs/diagnostics before finalizing the partial fix, since once deployed, the remaining broken scenarios may become harder to reproduce (the environment has changed).
- Decide deploy versus rollback based on net impact: does the partial fix genuinely reduce customer impact for a real majority of cases, or does it introduce a confusing half-fixed state that's harder to reason about than either the original bug or a full rollback? If unsure, a clean rollback while completing the fix properly is usually safer than shipping a partial state that stakeholders might mistake for "done."
Writing the follow-up plan
State precisely what remains broken, why the partial fix doesn't cover it yet, and a concrete next step with an owner, rather than a vague "will investigate further" that has no forcing function to actually get finished.
Trade-offs and pitfalls
The main risk of shipping a partial fix is stakeholders treating "mostly fixed" as "fixed," closing out attention on the issue before the remaining gap is actually addressed; being explicit and specific about what's still broken in every status update is what prevents that.
Digit DP: Count numbers in the range [0, N] whose digit sum is divisible by k. Design and implement a digit-DP in C++ or Python using memoization over (pos, sum_mod_k, tight, leading_zero). Explain state transitions, how to handle leading zeros, and analyze the complexity (digits * k).
Sample Answer
To solve this we use digit DP: iterate digits from most-significant to least, tracking (pos, sum_mod_k, tight, leading_zero). DP returns count of valid numbers from pos to end given current sum modulo k. Leading zeros must not contribute to sum (but zero itself counts: if whole number is 0, sum 0 mod k). Tight limits digit choice to N's digit at pos.
Approach:
- Convert N to digit array.
- dfs(pos, sum_mod, tight, leading_zero) returns count.
- If pos == len(digits): return 1 if sum_mod == 0 and not (leading_zero==True and treat zero valid) — we count zero as valid so return 1 when sum_mod==0.
- For each digit d from 0 to (tight? digits[pos] : 9):
- next_leading_zero = leading_zero and d==0
- next_sum = sum_mod if next_leading_zero else (sum_mod + d) % k
- next_tight = tight and (d == digits[pos])
- accumulate dfs(pos+1, next_sum, next_tight, next_leading_zero)
- Memoize on (pos, sum_mod, tight, leading_zero) but tight and leading_zero are small (2 each). Use -1 cache when tight==False to allow reuse; when tight==True, can't reuse across different prefixes so include tight in key.
Python implementation:
def count_up_to(N, k):
digits = list(map(int, str(N)))
n = len(digits)
from functools import lru_cache
@lru_cache(None)
def dfs(pos, sum_mod, tight, leading_zero):
if pos == n:
return 1 if sum_mod % k == 0 else 0 # zero is counted because leading_zero may be True but sum_mod==0
limit = digits[pos] if tight else 9
res = 0
for d in range(0, limit + 1):
ntight = tight and (d == limit)
nleading = leading_zero and (d == 0)
nsum = sum_mod if nleading else (sum_mod + d) % k
res += dfs(pos + 1, nsum, ntight, nleading)
return res
return dfs(0, 0, True, True)
Key points:
- State: pos (0..digits-1), sum_mod (0..k-1), tight (0/1), leading_zero (0/1).
- Transition adds digit d; if still leading zeros, digit doesn't affect sum.
- Base: at end, count if sum_mod==0.
- Memoization: cache key size ~ digits * k * 2 * 2.
Complexity:
- Time: O(digits * k * 10) ~ O(digits * k) since inner loop 10 constant.
- Space: O(digits * k * 4) for memo.
Edge cases:
- N=0 handled: returns 1 when k divides 0.
- Large k relative to digits: still works; complexity scales with k.
Implement 'product of array except self' in Python: given nums, return an array output where output[i] is product of all elements except nums[i]. Do it without division in O(n) time and O(1) extra space (excluding output). Explain how prefix and suffix products work and why this pattern applies to computing leave-one-out features.
Sample Answer
Direct answer
Make two passes over the array without ever dividing. In the first pass, fill the output with the running PREFIX product (the product of everything to the left of each index). In the second pass, walk from the right and multiply each entry by a running SUFFIX product (the product of everything to the right of each index). After both passes, output[i] holds the product of every element except nums[i].
Structured elaboration
Approach
def product_except_self(nums):
n = len(nums)
output = [1] * n
prefix = 1
for i in range(n):
output[i] = prefix
prefix *= nums[i]
suffix = 1
for i in range(n - 1, -1, -1):
output[i] *= suffix
suffix *= nums[i]
return output
def naive_division(nums):
"""The disallowed shortcut, shown only to make its zero-element failure
concrete rather than asserted: multiply everything once, then divide by
each element in turn."""
total = 1
for x in nums:
total *= x
out = []
for x in nums:
if x == 0:
out.append(None) # division by the zero element is undefined
else:
out.append(total // x)
return out
This is O(n) time (two linear passes) and O(1) extra space, not counting the output array itself, exactly as the question specifies, since only two running scalar accumulators (prefix and suffix) are kept alongside the output.
Why prefix and suffix products work, and why no division is needed
Every position i's answer is exactly (product of nums[0..i-1]) * (product of nums[i+1..n-1]), the product of everything strictly to the left, times the product of everything strictly to the right. The first pass computes and stores the left-hand factor for every index in one sweep (writing it into output[i] before nums[i] itself is folded into the running prefix). The second pass computes the right-hand factor the same way while sweeping backward, multiplying it directly into the value already stored. Because the two factors are built and combined without ever needing nums[i] itself in the final expression, this naturally avoids the "divide the total product by nums[i]" approach, which is both explicitly disallowed here and would break outright on any zero in the input (division by zero, or worse, silently wrong results if a naive implementation tries to special-case just one zero).
Leave-one-out features (the question's explicit ask)
The prefix/suffix accumulation pattern used here is the general technique behind computing a "leave-one-out" aggregate for every position without recomputing the whole aggregate from scratch each time. The most common leave-one-out feature in practice is a SUM (for example, "total spend across a group, excluding this member"), computed the same way with running prefix and suffix sums instead of products; the product version shown here applies the identical idea whenever the aggregate you need excluded is a product rather than a sum, for example computing a joint likelihood or a normalization factor across a set of independent factors while excluding one factor at a time.
Worked example
Executed with python3 s84.py (both functions defined above):
product_except_self([1, 2, 3, 4]) = [24, 12, 8, 6]
manual check: [24, 12, 8, 6]
Manually: excluding index 0 leaves 2*3*4=24; excluding index 1 leaves 1*3*4=12; excluding index 2 leaves 1*2*4=8; excluding index 3 leaves 1*2*3=6. All four match the function's output exactly.
product_except_self([1, 2, 0, 4]) = [0, 0, 8, 0]
naive division-based version on the same input = [0, 0, None, 0] <- breaks on the zero element
With a zero present, every output except the one at the zero's own index is 0 (since every other output's product still includes the zero), while the output AT the zero's index is the product of everything else (1*2*4=8). A division-based approach (compute the total product, then divide by nums[i]) fails outright here: dividing by the zero element itself is undefined, shown above returning None rather than the correct value 8.
Trade-offs and pitfalls
- The division-based shortcut (multiply everything, then divide by
nums[i]for each output) is both explicitly disallowed by the question and fundamentally broken the moment any element is zero; the executed comparison above makes this concrete rather than asserted. - The two-pass prefix/suffix technique generalizes directly to any associative combining operation (sum, product, min, max, and so on) wherever a "leave-one-out" aggregate is needed without an inverse operation (subtraction for sum, division for product) available or safe to use.
- If the array can be very large and the values themselves can be large integers, the running prefix and suffix products can grow to very large magnitudes; in a fixed-width integer language (unlike Python's arbitrary-precision integers), this needs an explicit overflow check or a different numeric representation, which is worth naming even though Python itself does not hit this limit.
Design an algorithm to find the kth smallest element in an unsorted array using binary search on the value domain rather than selection algorithms. Explain how to count numbers <= mid in O(n) and discuss when this approach is advantageous compared to Quickselect.
Sample Answer
Approach: do binary search over the value domain [min_val, max_val]. For a candidate mid, count how many array elements are <= mid in O(n). If count >= k, the kth smallest is <= mid, so move high = mid; else low = mid + 1. Stop when low == high.
Code (Python):
def kth_smallest_by_value(nums, k):
if not 1 <= k <= len(nums):
raise ValueError("k out of range")
lo, hi = min(nums), max(nums)
while lo < hi:
mid = lo + (hi - lo) // 2
# count elements <= mid in O(n)
cnt = 0
for x in nums:
if x <= mid:
cnt += 1
if cnt >= k:
hi = mid
else:
lo = mid + 1
return lo
Key points:
- Counting is a single pass O(n) scan, constant extra space.
- When values are integers this returns the correct value; with floats you need epsilon or rank-aware checks.
- If duplicates exist, logic still holds because cnt tracks rank threshold.
Complexity:
- Time: O(n * log(R)) where R = (max_val - min_val + 1) for integers (or log(range/precision) for floats).
- Space: O(1).
When preferable to Quickselect:
- When you want guaranteed worst-case bounds (deterministic runtime depending on value range) rather than Quickselect's average O(n) but worst-case O(n^2).
- When value range is small relative to n (log R small) — e.g., integers in limited domain.
- When you cannot modify the array (this is non-destructive) or when data is streaming/external and you can only make sequential passes.
- When stability with many duplicates matters: value-binary-search handles ties cleanly.
Trade-offs:
- If value range is huge (like 64-bit random ints), log R may be large and Quickselect (average O(n)) is faster.
- For repeated queries, build summaries (counting sort, order-stat tree) may be better.
What is a code smell? Name five smells you encounter most often in a codebase that has been under deadline pressure for a while, and for each give a one-sentence remediation approach.
Sample Answer
Direct answer. A code smell is a surface signal, not a bug in itself, that usually points to a deeper structural problem: the code works today but will resist the next change. It's a heuristic for WHERE to look, not proof that something is wrong.
Five smells common under deadline pressure
- Long method -- a function that keeps growing because it's easier to add one more
ifthan to stop and restructure. Remediation: extract by responsibility (see S5) as soon as a function needs a comment to separate its 'sections.' - Duplicated code -- the same logic copy-pasted with small tweaks because extracting a shared abstraction felt slower under a deadline. Remediation: extract the shared part once there are two clear copies (the classic 'rule of three' guards against over-extracting on the first duplicate).
- God object/class -- one class or module that ends up owning unrelated responsibilities because it was the easiest place to bolt on 'just one more thing.' Remediation: split along 'reason to change' (SRP), migrating callers incrementally rather than in one big rewrite.
- Shotgun surgery -- a single conceptual change (e.g., adding a new payment method) requires touching a dozen files because the concept isn't encapsulated anywhere. Remediation: consolidate the scattered logic behind one seam (a class, interface, or module) so future changes touch one place.
- Primitive obsession -- passing raw strings/ints around for things that are really domain concepts (an email, a currency amount, a user ID), losing the validation and meaning a real type would carry. Remediation: introduce small value types/wrappers so invalid states become unrepresentable rather than merely 'usually correct.'
Why deadline pressure specifically produces these
Under pressure, the fastest LOCAL change is almost always to keep extending what's already there (one more branch, one more copy-paste, one more method on the class you already have open) rather than to pause and restructure. Each individual shortcut is locally rational; the smell accumulates because nobody's shortcut budget includes 'time to undo the last five shortcuts.'
Trade-offs and pitfalls
- Smells are a starting point for investigation, not an automatic verdict -- a long method that's a single linear sequence of well-named steps with no branching can be more readable than five tiny indirections that force you to jump around a file.
- Don't chase every smell with equal urgency; prioritize by where the churn and bug density actually are (see the complexity-metrics survivor for how to find that objectively) rather than refactoring whatever offends you first.
- Naming a smell is only useful if it's followed by a concrete remediation plan; 'this is a god object' without a proposed split is just a complaint.
A CI test fails intermittently under load but passes locally. Propose a systematic debugging plan to determine whether the problem is test flakiness, infrastructure instability, a race condition, or timing. Include what telemetry to collect, how to reproduce locally, and steps to isolate and fix the root cause.
Sample Answer
Situation: An intermittent CI test failure that never reproduces locally can come from flakiness, infra instability, race conditions, or timing/timeout sensitivity. Here's a systematic debugging plan to identify and fix the root cause.
- Triage & pattern analysis
- Collect CI failure history: frequency, time-of-day, node/agent IDs, commit hashes, PR vs master, test shard.
- Capture failing job artifacts (logs, test output, screenshots, core dumps, coverage).
- Note whether failures correlate with load spikes, high CPU/memory, or network issues.
- Telemetry to enable/collect
- Test-level: full stdout/stderr, stack traces, assertion locations, test durations, timestamps with millisecond precision.
- Infra: agent CPU, memory, disk I/O, network latency, process counts, container cgroup metrics.
- Tracing/profiling: distributed traces (span timings), thread dumps, GC logs, perf or pprof snapshots for native code.
- Environment: exact binary, dependency versions, environment vars, JVM/Node flags.
- Persist a reproducible VM/container image of the failing CI node if possible.
- Reproduce locally under load
- Re-run test in same OS/container image (use the CI image locally or in Docker).
- Mimic CI resources: limit CPU and memory, run with similar concurrency, use stress tools (stress-ng), network throttling (tc/netem).
- Run tests in loop and parallel to increase chance of exposing race/timing.
- Use CI-scale test runner (same test shard/scheduler).
- Increase log verbosity and enable timestamps.
- Narrow down cause
- Flaky test (test code): add deterministic seeds, assert pre/post conditions, isolate external dependencies by mocking, add extra logging around setup/teardown.
- Infrastructure instability: look for correlated infra metrics (OOM, disk full, flaky network). Re-run on different agents and compare.
- Race condition: run under race detectors/sanitizers (ThreadSanitizer, Go race, Java concurrency tools), enable heavy instrumentation (LockProfilers), capture thread dumps at failure.
- Timing-sensitive: replace real timers with controllable clocks (fake clocks), reduce test timeouts locally, introduce artificial delays to see sensitivity.
- Isolation strategy (binary search)
- Bisect commits to see when flakiness began.
- Minimize test: remove unrelated assertions, split test into smaller units to identify the failing section.
- Reproduce single-case under single-threaded mode to check concurrency dependency.
- Swap real dependencies with deterministic fakes to see if external systems cause nondeterminism.
- Fix approaches
- For race: add proper synchronization, atomic operations, or redesign to avoid shared mutable state in tests.
- For timing: increase conservative timeouts, use condition-based waits (poll with timeout) instead of fixed sleeps, use fake clocks.
- For infra: add retries with exponential backoff for known transient failures, pin CI workers or improve resource isolation, or tweak resource requests/limits.
- For flaky test logic: rewrite to be deterministic, ensure clean setup/teardown, avoid shared state between tests, and add idempotency.
- Validation & prevention
- Re-run tens/hundreds of attempts in CI and locally under stress to verify fix.
- Add telemetry and assertion checks to ensure invariant holds going forward.
- Add test-level flakiness gating (failing only after N consecutive failures) and alerting for infra anomalies.
- Add post-merge monitoring and a blame-minimizing bisect process for future regressions.
Example quick commands:
- Run with limited CPUs: docker run --cpus="0.5" ...
- Network jitter: tc qdisc add dev eth0 root netem delay 100ms 20ms
- Java thread dumps: jstack -l <pid> > dump.txt
- Go race: go test -race ./...
This plan identifies, reproduces, and isolates the root cause through telemetry, controlled experiments, and targeted fixes, then validates the fix under load before rolling back into CI.
A conversation touches on allegations that could become a harassment claim. As a software engineer leading the team or acting as a manager, what immediate steps do you take to protect individuals, preserve relevant evidence, involve HR/legal, and ensure the investigation process is handled fairly and discreetly?
Sample Answer
Situation: In a one-on-one I overhear allegations that could become a harassment claim involving team members.
Immediate actions I take (safety, evidence preservation, HR/legal involvement, fairness):
- Ensure immediate safety and support
- Separate parties if needed; offer private support to the person who raised the allegation.
- Clarify they’re heard, provide emergency resources (EAP, security) and explain next steps without pressuring them.
- Preserve evidence without investigating
- Ask the reporter to preserve any messages, screenshots, or recordings.
- Immediately notify IT/Security (on a need-to-know basis) to preserve relevant logs, chat history, emails, build/deploy logs, and take forensic snapshots if required; request a legal hold on related data.
- Avoid accessing or sharing alleged evidence beyond required parties to prevent contamination.
- Involve HR and Legal promptly
- Escalate to HR and Legal within the timeframe mandated by policy; provide a factual, limited written summary of what I heard and when.
- Recuse myself from fact-finding to avoid bias; request HR-led investigation and an impartial investigator.
- Protect confidentiality and prevent retaliation
- Limit disclosure to HR, Legal, and necessary leadership; document who is informed.
- Put interim measures in place (temporary reassignments, remote work, adjusted reporting lines) consistent with fairness and minimal disruption.
- Communicate zero-retaliation policy and monitor team dynamics.
- Ensure a fair, documented process
- Let HR handle interviews; offer technical assistance for evidence collection (timestamps, audit trails) when asked.
- Keep clear, dated records of my actions and communications.
- Follow up with HR on outcomes and ensure any remediation or team communications are coordinated by HR/Legal.
Why: These steps prioritize safety, maintain evidentiary integrity, respect confidentiality and due process, and reduce legal risk while supporting victims and preserving team trust.
Flatten a binary tree to a linked list in-place following preorder traversal. In Java, modify tree nodes' right pointers to the next node in preorder and set left pointers to null. Provide an O(n) time O(h) space solution and explain invariants.
Sample Answer
Approach: do a reverse preorder traversal (visit right, then left, then node) and maintain a previous node pointer. For each visited node, set node.right = prev and node.left = null, then update prev = node. This produces the preorder-linked-list in-place. Recursion depth equals tree height => O(h) space.
// Definition for a binary tree node.
class TreeNode {
int val;
TreeNode left, right;
TreeNode(int x) { val = x; }
}
public class Solution {
private TreeNode prev = null; // previously processed node in reverse preorder
public void flatten(TreeNode root) {
// reverse preorder: right -> left -> node
if (root == null) return;
flatten(root.right);
flatten(root.left);
// invariants maintained here:
// 1) 'prev' points to the head of the already-flattened list for the subtree
// 2) all nodes in that list have left == null and use right as next pointer
root.right = prev; // link current node to previously processed list
root.left = null; // ensure left is null as required
prev = root; // now current node becomes head of flattened list
}
}
Key invariants:
- After processing a node, the subtree rooted at that node is flattened into a list following preorder, with left pointers null and right pointers as next.
- 'prev' always references the head of the flattened list for nodes already visited (which are those that appear after the current node in preorder).
Complexity:
- Time: O(n) — each node visited once.
- Space: O(h) — recursion stack where h is tree height.
Edge cases:
- Empty tree (root == null).
- Single-node tree.
- Skewed trees (height = n) result in O(n) stack depth; iterative alternatives (Morris-style) can achieve O(1) extra space if required.
Implement removeNthFromEnd(head, n) in Python that removes the nth node from the end of a singly linked list in one pass using constant extra space. Use a dummy head to simplify edge cases and return the possibly new head of the list. Complexity should be O(L) time and O(1) extra space where L is the list length.
Sample Answer
Approach: use two pointers with a dummy head. Advance the fast pointer n+1 steps from dummy so the gap between fast and slow is n nodes; then move both until fast reaches end. Slow will be just before the node to remove — splice it out. This is one pass (single traversal), O(1) extra space.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def removeNthFromEnd(head: ListNode, n: int) -> ListNode:
"""
Remove the nth node from end in one pass using dummy node.
Returns the possibly new head.
"""
dummy = ListNode(0, head)
slow = dummy
fast = dummy
# Move fast n+1 steps ahead so slow ends at predecessor of target
for _ in range(n + 1):
fast = fast.next
# Move both until fast hits the end
while fast:
slow = slow.next
fast = fast.next
# slow.next is the node to remove; unlink it
to_remove = slow.next
slow.next = to_remove.next
# Optional: help GC
to_remove.next = None
return dummy.next
Key points:
- dummy handles removal of head uniformly.
- We advance fast n+1 to maintain gap; when fast is None, slow is at predecessor.
Time complexity: O(L). Space complexity: O(1).
Edge cases:
- Removing the head (n == length) — handled by dummy.
- n equals 1 (remove last node).
- Single-node list (result may be None).
- Assume n is valid (1 ≤ n ≤ length).
Compare a contiguous array and a singly linked list on random access, insertion/deletion at head/middle/tail, memory overhead, and cache locality. For a workload that is mostly random reads versus one that is mostly insertions and deletions in the middle, which would you pick and why?
Sample Answer
Direct answer
An array gives O(1) index-based random access and is cache-friendly, because its elements sit in one contiguous block of memory that the CPU can pull into cache together. A singly linked list gives O(1) insertion or deletion once you already hold a reference to the splice point, at the cost of O(n) traversal to reach any given position and extra per-node memory overhead. For a workload that is mostly random reads, pick an array (or dynamic array); for a workload that is mostly insertions and deletions in the middle where you already hold the relevant node reference, a linked list wins.
Structured elaboration
| Dimension | Array | Singly linked list |
|---|---|---|
| Random access by index | O(1) | O(n), must walk from the head |
| Insert/delete at head | O(n), shifts every remaining element | O(1), relink the head reference |
| Insert/delete at tail | O(1) amortized (averaged over a sequence of operations; dynamic array resize) | O(1) only if a tail reference is separately maintained, otherwise O(n) to reach it |
| Insert/delete in the middle | O(n), shifts elements | O(1) to relink, but only if you already hold a reference to the node just before the splice point; otherwise O(n) just to reach it |
| Memory overhead | None beyond the elements themselves, plus occasional unused resize slack | Each node carries at least one extra reference beyond its value, a larger overhead per element |
| Cache locality | Contiguous memory means sequential and even random access both benefit from data already sitting in cache | Each node is typically a separate heap allocation, so following references jumps around memory ("pointer chasing"), producing far more cache misses per traversal |
The contiguous-versus-non-contiguous memory allocation framing is exactly this same dimension stated differently: contiguous storage is what gives arrays both their cache locality and their index arithmetic; non-contiguous, per-node allocation is what gives linked lists their cheap local splicing at the cost of locality. In a garbage-collected (GC) language, this also affects collector pressure: many small linked-list node objects mean more individual objects for the garbage collector to track and scan, compared to one contiguous array allocation holding the same data.
Worked example
Consider inserting a new element in the middle of a ten-item collection, repeatedly, as items are typed into an editable list. With an array, each insertion must shift every element after the insertion point one slot over, an O(n) cost per insertion regardless of whether you know exactly where to insert. With a singly linked list, if the editing position is tracked by an existing reference to the node just before it (as a cursor would be in a text-editing context), each insertion is a pure O(1) relink; but if you only know the position as an index and must first walk from the head to reach it, the linked list gains nothing over the array for that access, since both now cost O(n) overall. This is why the deciding factor is not "array versus linked list" in the abstract, but whether the workload naturally hands you a reference to the splice point or only an index.
Trade-offs & pitfalls
Assuming "O(1) insertion" for a linked list means fast in absolute terms is a common mistake: reaching the splice point is usually the dominant cost unless a reference to it is already in hand from a prior traversal or an auxiliary index. Ignoring the per-element memory overhead ratio is another: a linked list of single integers can use several times the memory of the equivalent array, because the pointer overhead per node is fixed regardless of how small the stored value is. Modern hybrid structures such as a deque (double-ended queue) or a rope address parts of this trade-off by chunking data into contiguous blocks rather than choosing purely one extreme or the other.
Recommended Additional Resources
- LeetCode Premium - Comprehensive practice platform with 2000+ algorithmic problems. Focus on 'Google' tagged problems and medium-difficulty questions to match interview style.
- HackerRank - Structured coding challenges with learning paths for data structures and algorithms fundamentals.
- Cracking the Coding Interview by Gayle Laakmann McDowell - Essential reference written by former Google engineer covering interview strategies, data structures, algorithms, and behavioral preparation.
- Elements of Programming Interviews - Deep technical reference for complex algorithmic problems and advanced problem-solving patterns.
- Google Engineering Practices Documentation - Available online, provides insight into Google's coding standards, code review practices, and engineering philosophy.
- Interview.io - Practice platform offering mock interviews with real engineers and detailed feedback on technical and communication approach.
- Pramp - Free peer-to-peer mock interview platform enabling real-time practice and feedback from other engineers.
- YouTube Channels: William Fiset (algorithm visualization), Abdul Bari (data structures fundamentals), Back to Back SWE (problem-solving methodology)
- GitHub - Search for 'google coding interview' or 'google sde interview' repositories containing problem collections and solutions.
- Google Careers Website and Blog - Official resource for understanding Google's culture, team structure, products, and engineering values.
Search Results
Google Software Engineer Interview Process - Our Expert Guide
The interview journey includes coding challenges, system design interviews, and behavioral assessments. Knowing what to expect and how to tackle each stage can ...
Google Software Engineer Interview Guide (2025)
The process includes application review, recruiter screening, technical phone interviews, virtual onsite interviews, and the Hiring Committee ...
Google Software Engineer Interview Prep 2025 [Actionable]
This is a complete guide on the Google software engineer interview process. Learn how to cruise through the hiring process and win the coding rounds.
How to Crack Google SDE Interview in 2025 - YouTube
Google as a software engineer in 2025? This video breaks down everything you need to know — from resume building and referrals to DSA prep ...
Google Software Engineer Interview Guide | Sample Questions (2025)
Prepare for the Google Software Engineer interview with an inside look at the interview process and sample questions. Learn how to get a Software Engineer ...
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