Software Engineer Technical Interview Questions & Answers (2026)
Technical coding interviews evaluate your ability to solve algorithmic problems, write clean code, analyze time/space complexity, and communicate your thinking process. Top companies (FAANG, unicorns) typically run 2-3 coding rounds plus 1 system des...
Software engineer technical interviews test coding ability, system design thinking, and problem-solving under pressure. This guide covers the most common coding interview patterns, how to structure your approach, and example solutions.
Overview
Technical coding interviews evaluate your ability to solve algorithmic problems, write clean code, analyze time/space complexity, and communicate your thinking process. Top companies (FAANG, unicorns) typically run 2-3 coding rounds plus 1 system design round. The key to success is structured problem-solving: clarify the problem, plan your approach, implement, then test — all while explaining your reasoning aloud.
Technical Interview Questions for Software Engineer Roles
Q1: Given an array of integers, find two numbers that add up to a specific target. Return their indices.
What they're really asking: This classic problem tests hash map usage, trade-off analysis between brute-force and optimized solutions, and your ability to handle edge cases (duplicates, negative numbers).
How to answer: Start with the brute-force O(n²) approach, then optimize to O(n) using a hash map. Discuss edge cases and space-time trade-offs.
See example answer
I'd first clarify: can there be multiple valid pairs? Can the same element be used twice? Assuming one unique solution and no element reuse. The brute-force approach checks every pair: two nested loops, O(n²) time, O(1) space. To optimize, I'd use a hash map storing each number's complement (target - num) as I iterate. For each number, I check if it exists in the map. If yes, return both indices. If no, add current number and index to the map. This gives O(n) time, O(n) space. Implementation: create an empty dict, iterate with enumerate, for each num check if target-num is in dict, if so return [dict[target-num], i], else dict[num] = i. Edge cases: empty array returns None, single element returns None. I'd test with [2,7,11,15] target=9, expecting [0,1]. Then test with negative numbers [-3,4,3,90] target=0, expecting [0,2]. The hash map approach is optimal because you can't do better than O(n) when you need to examine every element at least once.
Q2: Design a LRU (Least Recently Used) cache with O(1) get and put operations.
What they're really asking: This tests your knowledge of data structure composition — combining a hash map with a doubly-linked list. It also evaluates your ability to handle capacity management and maintain ordering efficiently.
How to answer: Explain the data structure choice (hash map + doubly-linked list), implement the core operations, and discuss thread safety considerations.
See example answer
An LRU cache needs O(1) lookup (hash map) and O(1) insertion/removal at both ends (doubly-linked list). I'd combine them: the hash map stores key → node pointer, and the doubly-linked list maintains access order with most recent at the head. For get(key): if key exists in the map, move its node to the head of the list (O(1) with direct pointer) and return the value. If not, return -1. For put(key, value): if key exists, update value and move to head. If key is new and at capacity, remove the tail node (least recently used), delete its key from the map, then add new node at head and to map. I'd implement a Node class with key, value, prev, next. The LRUCache class has capacity, map, and sentinel head/tail nodes for clean edge-case handling. The sentinel nodes eliminate null checks when adding/removing. I'd test with capacity=2: put(1,1), put(2,2), get(1) returns 1 and moves key 1 to front, put(3,3) evicts key 2 as LRU. Thread safety: in production, I'd wrap operations with a lock or use a concurrent data structure.
Q3: Given a binary tree, find the lowest common ancestor of two given nodes.
What they're really asking: This tests tree traversal skills, recursive thinking, and the ability to handle various cases (one node is ancestor of the other, nodes in different subtrees, node not in tree).
How to answer: Start with the recursive DFS approach, handle edge cases, then discuss optimization for a BST variant.
See example answer
First, I'd clarify: is this a binary search tree or general binary tree? For a general binary tree, I'd use recursive DFS. Base cases: if root is None, return None. If root equals either target node, return root. Recurse on left and right subtrees. If both return non-None, root is the LCA (the two nodes are in different subtrees). If only one returns non-None, that result is the LCA (both nodes are in the same subtree, and we found the higher one first). Implementation: def lca(root, p, q): if not root or root == p or root == q return root. left = lca(root.left, p, q). right = lca(root.right, p, q). if left and right return root. return left or right. Time: O(n) visiting each node once. Space: O(h) for recursion stack where h is height. For a BST, we can optimize by comparing values: if both nodes are less than root, go left; if both greater, go right; otherwise root is LCA. I'd test with a balanced tree where p and q are in different subtrees, then test where one is an ancestor of the other.
Q4: Implement a function to detect if a linked list has a cycle and find the start of the cycle.
What they're really asking: This tests Floyd's cycle detection algorithm, pointer manipulation, and mathematical reasoning about why the algorithm works.
How to answer: Explain Floyd's tortoise and hare algorithm, prove why it works, implement both detection and cycle start finding.
See example answer
I'd use Floyd's tortoise and hare algorithm. Two pointers: slow moves 1 step, fast moves 2 steps. If there's a cycle, they'll meet. If fast reaches null, no cycle. Once they meet, to find the cycle start: reset one pointer to head and move both at speed 1. They'll meet at the cycle start. The math: if the non-cycle length is 'a' and the cycle length is 'c', when they first meet, slow has traveled a+b steps and fast has traveled a+b+nc for some n. Since fast = 2*slow: a+b+nc = 2(a+b), so nc = a+b, meaning a = nc-b. Moving a steps from the meeting point equals moving nc-b steps in the cycle, which lands at the cycle start. Implementation: def detectCycle(head): slow = fast = head. while fast and fast.next: slow = slow.next, fast = fast.next.next. if slow == fast: slow = head, while slow != fast: slow = slow.next, fast = fast.next, return slow. return None. Time: O(n), Space: O(1). I'd test with a cycle at position 2 in a 5-node list, a single-node cycle, and no cycle.
Q5: Given a string, find the length of the longest substring without repeating characters.
What they're really asking: This tests the sliding window technique, hash set/map usage, and the ability to optimize from O(n²) to O(n) with the right data structure.
How to answer: Explain the sliding window approach with a hash map tracking character positions. Walk through the algorithm step by step.
See example answer
I'd use a sliding window with a hash map storing each character's most recent index. Maintain a left pointer for the window start and iterate with right pointer. For each character at right: if it's in the map and its stored index >= left, move left to stored_index + 1 (shrink window to exclude the duplicate). Update the map with the current character's position. Track max_length = max(max_length, right - left + 1). Implementation: def lengthOfLongestSubstring(s): char_map = {}, left = 0, max_len = 0. for right, char in enumerate(s): if char in char_map and char_map[char] >= left: left = char_map[char] + 1. char_map[char] = right. max_len = max(max_len, right - left + 1). return max_len. Time: O(n) single pass. Space: O(min(n, alphabet_size)). Test with 'abcabcbb' → 3 ('abc'), 'bbbbb' → 1 ('b'), 'pwwkew' → 3 ('wke'). The key insight is that when we find a duplicate, we don't need to move left one step at a time — we can jump directly past the previous occurrence.
Ace the interview — but first, get past ATS screening. Make sure your resume reaches the hiring manager with Ajusta's 5-component ATS scoring — 500 free credits, no card required.
Optimize Your Resume Free →Preparation Tips
- Practice 2-3 LeetCode problems daily for at least 4-6 weeks before interviews, focusing on patterns (sliding window, two pointers, BFS/DFS, dynamic programming) rather than memorizing solutions
- Always talk through your thinking process: clarify the problem, consider edge cases, plan your approach, then implement. Silence is the biggest red flag in coding interviews
- Master time/space complexity analysis — interviewers always ask 'what's the complexity?' after you solve a problem
- Practice coding on a whiteboard or in a plain text editor without autocomplete — interview environments don't have IDE features
- Study the company's interview process: some focus on algorithms (FAANG), others on practical coding (startups), and some use take-home assignments
- Review your solutions after practice: can you optimize further? What if the input was 10x larger? 1000x larger?
Common Mistakes to Avoid
- Jumping into code immediately without clarifying the problem, discussing edge cases, or planning your approach
- Writing code silently — interviewers can't evaluate your thinking if you don't explain it
- Not testing your solution with examples after implementation — always walk through at least 2 test cases
- Over-optimizing too early: start with a working solution, then optimize. A correct O(n²) solution beats a broken O(n) attempt
- Panicking when stuck: it's normal to struggle. Take a breath, think about what you know, and try a different approach
- Ignoring edge cases: empty input, single element, all same elements, negative numbers, integer overflow
Research Checklist
Before your technical interview, make sure you have researched:
- Research the company's interview format: how many coding rounds, difficulty level, and preferred languages
- Check LeetCode discuss forums and Glassdoor for recent interview questions at the specific company
- Understand whether the company uses online judges (HackerRank, CodeSignal) or whiteboard/collaborative coding
- Know the company's tech stack to choose an appropriate coding language for the interview
- Research the company's engineering blog for technical problems they solve — interview questions often reflect real challenges
- Practice with the same tools you'll use in the interview (CoderPad, Google Docs, whiteboard)
Questions to Ask Your Interviewer
- What does a typical day look like for engineers on this team?
- How does the team approach code review? What tools do you use?
- What's the testing philosophy? What percentage of code coverage do you target?
- How do you handle technical debt? Is there dedicated time for refactoring?
- What's the deployment process like? How often do you ship to production?
- What engineering challenges are you currently working on that you're most excited about?
How Your Resume Connects to the Interview
Your technical interview performance should be supported by a resume that demonstrates real coding experience. Ajusta ensures your software engineering resume includes the specific programming languages, frameworks, and system design keywords that ATS systems filter for, getting you past the initial screen so you can demonstrate your coding skills in the interview.