This section includes Computer Networks Q&A commonly asked in Amazon interviews, explained clearly with real-world examples and interview-focused insights to help you build strong fundamentals and answer confidently.
1. Amazon Prime video buffers frequently on poor networks. Would you choose TCP or UDP, and how would you mitigate packet loss?
Video streaming cares more about smooth playback than perfect delivery. With TCP, every lost packet must be retransmitted in order, which increases buffering and delay. On poor networks, this causes the video to pause often. UDP avoids this because it does not wait for retransmissions, so the video can keep playing even if a few packets are lost.
Packet loss is handled at the application level instead of the transport layer. Techniques like adaptive bitrate streaming automatically lower video quality when the network is weak. Buffering ahead, forward error correction (FEC), and sending key frames more frequently also help recover from lost packets without stopping playback.
Real-life example:
When your internet slows down and the video quality drops from HD to SD but keeps playing without stopping, that’s UDP-style behavior combined with smart application-level handling to deal with packet loss.
2. Amazon uses QUIC over UDP in some services — what problems does it solve compared to TCP?
QUIC avoids the slow TCP + TLS handshake by combining them into a single step, so connections start faster. It also knows how to handle packet loss better, meaning one lost packet doesn’t block everything else like TCP does.
Real-life example:
When a video or page starts loading instantly on a mobile network, even while switching between Wi-Fi and data, QUIC helps keep the connection smooth and fast.
3. Difference between flow control and congestion control.
Flow Control manages data transfer between the sender and receiver.
It ensures the sender does not send data faster than the receiver can handle,
preventing buffer overflow.
Congestion Control manages data traffic inside the network.
It prevents the network from becoming overloaded when too many packets are sent at once.
| Basis |
Flow Control |
Congestion Control |
| Purpose |
Controls data flow between sender and receiver |
Controls data flow in the network |
| Focus |
Receiver’s capacity |
Network traffic load |
| Main Goal |
Prevents receiver buffer overflow |
Prevents network congestion |
| Problem Occurs When |
Receiver is slow |
Too many packets are in the network |
| Example |
Sender waits if receiver buffer is full |
Sender slows down when network is crowded |
4. An Amazon API has high latency only for certain regions — how do you debug at HTTP level?
When an Amazon API is slow only in some regions, the first step is to check the HTTP request and response details.
At the HTTP level, we focus on timing, headers, and network behavior to understand where the delay is happening.
How to Debug at HTTP Level
- Check DNS lookup time, connection time, and response time using curl or the browser Network tab
- Compare request and response headers from fast regions and slow regions
- Observe HTTP status codes, response size, and retry behavior
Real-Life Example
If users in India experience slow API responses while users in the US get fast responses,
the issue could be related to CDN routing or longer network paths.
By comparing HTTP timing values, we can identify whether the delay occurs before the request
reaches the server or during the server’s response.
5. Difference between A, AAAA, CNAME, ALIAS records.
A Record
Points a domain name to an IPv4 address.
Example: example.com → 93.184.216.34
AAAA Record
Points a domain name to an IPv6 address.
Example: example.com → 2606:2800:220:1:248:1893:25c8:1946
CNAME Record
Points one domain name to another domain name, not an IP address.
Example: www.example.com → example.com
ALIAS Record
Works like a CNAME but can be used on the root domain.
Example: example.com → loadbalancer.aws.com
6. What is the difference between HTTPS, TLS, and SSL?
SSL (Secure Sockets Layer)
SSL was the original technology used to secure data between a user and a server.
It is now outdated and no longer considered safe for modern websites.
TLS (Transport Layer Security)
TLS is the improved and secure version of SSL used today.
It encrypts data during transfer so attackers cannot read it.
HTTPS (HyperText Transfer Protocol Secure)
HTTPS is the secure version of HTTP that uses TLS.
It protects login details, payments, and personal information on websites.
Comparison Table
| Basis |
SSL |
TLS |
HTTPS |
| What it is |
Old security protocol |
Modern security protocol |
Secure web protocol |
| Status |
Deprecated |
Actively used |
Used on secure websites |
| Purpose |
Old data encryption |
Secure data encryption |
Secure website communication |
| Used by |
Older systems |
Modern applications |
Browsers & web servers |
| Example |
Legacy SSL certificate |
TLS encryption |
https://amazon.com |
7. Where Amazon Uses WebSockets and Where It Avoids Them?
Amazon uses WebSockets where real-time updates are required.
Examples include live order tracking, customer chat support, instant notifications,
and dashboards where data must update without refreshing the page.
WebSockets keep a continuous connection, making updates fast and smooth.
Amazon avoids WebSockets for normal API operations like browsing products,
searching items, or loading pages. These actions do not need a constant connection,
so regular HTTP requests are simpler, cheaper, and easier to handle at large scale.
Real-life example:
Live order status updates may use WebSockets, but product search or adding items
to the cart uses normal HTTP requests.
8. What happens when a DNS record is misconfigured in production?
When a DNS record is misconfigured in production, users may not be able to access the application or may be redirected to the wrong location. Since DNS decides how a domain connects to servers, even a small mistake can affect the entire system.
What usually happens:
- Website or API becomes unreachable (loading errors or timeouts)
- Users are sent to the wrong server or an outdated IP address
- Emails stop working due to incorrect MX records
- SSL warnings appear if the domain points to the wrong service
Real-life example:
If an e-commerce website changes its server IP but forgets to update the DNS A record, users may see the site as “down” even though the server is running properly. This can cause lost sales, failed payments, and customer complaints until the DNS issue is fixed.
9. How would you debug a DNS issue where the site works in the US but not in India?
When a website works in the US but not in India, the issue is usually related to
DNS resolution or regional routing. Since DNS servers can return
different results based on location, it’s important to check how the domain is resolving
in both regions.
How to Debug It:
- Check DNS resolution from India and US using tools like nslookup or online DNS checkers to see if both regions get the same IP address.
- Verify DNS propagation to confirm recent changes have updated globally.
- Check CDN or Geo-routing settings if the site uses services like Cloudflare or AWS Route 53.
- Look at TTL (Time to Live) to see if old DNS records are still cached in India.
- Test connectivity using ping or traceroute from an Indian server.
Real-life Example:
If a company updates its server IP but DNS has not fully propagated in India,
users there may still reach the old IP address. This makes the website fail
in India, while users in the US see it working normally.
Welcome to the Python Interview Questions section. This section covers commonly asked Python questions in Amazon interviews, explained in a simple and easy-to-understand manner to help you clearly understand each concept.
1. What problem does reference counting fail to solve, and how does Python fix it?
Reference counting works by keeping track of how many variables are pointing to an object. When the count becomes zero, the object is removed from memory. This method is simple and fast, but it has one major problem – it cannot handle circular references.
A circular reference happens when two or more objects refer to each other. For example, Object A refers to Object B, and Object B refers back to Object A. Even if no other part of the program is using them, their reference count never becomes zero. Because of that, memory is not freed, and this creates a memory leak.
Python solves this problem by adding something called a garbage collector. In addition to reference counting, Python runs a background process that looks for groups of objects that are only referencing each other and are not being used anywhere else in the program. When it finds such unused circular objects, it safely removes them from memory.
So in short, reference counting alone cannot clean circular references, but Python fixes this by combining reference counting with an automatic garbage collection system.
2. What happens internally when you do: a = b = []
When you write:
a = b = []
Python does not create two different lists. It creates only one empty list in memory. Then both variables a and b are made to point to that same list.
Internally, Python first creates the list object. After that, it assigns the reference of that list to b, and then assigns the same reference to a. So both variables are pointing to the same memory location.
Because of this, if you change the list using one variable, the change will also appear in the other variable.
a = b = []
a.append(10)
print("a:", a)
print("b:", b)
3. Difference between list, tuple, set, and dict internally?
| Type |
Internal Structure |
Ordered |
Mutable |
Lookup Speed |
| List |
Dynamic array |
Yes |
Yes |
O(1) by index |
| Tuple |
Immutable array |
Yes |
No |
O(1) by index |
| Set |
Hash table |
No |
Yes |
O(1) average |
| Dictionary |
Hash table (key-value) |
Yes (Python 3.7+) |
Yes |
O(1) average |
4. What happens when you pass a variable to a function in Python?
When you pass a variable to a function in Python, you are not passing the value and not passing a copy.
You are passing a reference to the object that the variable points to.
def update_data(num, nums):
num += 1 # creates a new integer object
nums.append(4) # modifies the same list object
print("Inside function:")
print("num =", num)
print("nums =", nums)
number = 10
numbers = [1, 2, 3]
update_data(number, numbers)
print("\nOutside function:")
print("number =", number)
print("numbers =", numbers)
5. Explain Python’s garbage collector.
Python’s garbage collector automatically frees memory by removing objects that are no longer needed.
It mainly uses reference counting, where an object is deleted when no variable points to it.
To handle objects that reference each other, Python uses a cyclic garbage collector that finds and removes those unused cycles.
The garbage collector works in generations, checking newer objects more often than older ones to keep programs fast.
Real-Life Example
In a web application, many temporary objects are created while handling a request. Once the request is finished, Python’s garbage collector removes those unused objects, freeing memory so the server can handle the next request smoothly.
6. What are Python descriptors?
Python descriptors are objects that control how attributes of a class are accessed, updated, or deleted.
They work by defining special methods like __get__, __set__, or __delete__.
Whenever you access an attribute, Python automatically calls these methods instead of directly reading or writing the value.
class Age:
def __get__(self, instance, owner):
return instance._age
def __set__(self, instance, value):
if value < 0:
raise ValueError("Age cannot be negative")
instance._age = value
class Person:
age = Age() # descriptor
p = Person()
p.age = 25 # calls __set__
print(p.age) # calls __get__
7. Difference between __new__ and __init__
__new__ and __init__ are both used when creating objects in Python, but they do different jobs.
__new__ is responsible for creating the object.
It is called first and returns a new instance of the class. This method is mainly used when you need control over object creation, such as in immutable objects.
__init__ is responsible for initializing the object.
It runs after __new__ and sets or modifies the object’s attributes.
class Example:
def __new__(cls):
print("__new__ called")
return super().__new__(cls)
def __init__(self):
print("__init__ called")
obj = Example()
8. What is monkey patching?
Monkey patching means changing or adding behavior to existing code at runtime, without modifying the original source code.
In Python, this usually happens when you replace a function or method of a class or module while the program is already running.
class Printer:
def show(self):
print("Original printer output")
p = Printer()
p.show()
# monkey patching the method
def new_show():
print("Modified printer output")
p.show = new_show
p.show()
A decorator in Python is simply a function that takes another function as input and returns a new function with extra behavior added to it.
Instead of changing the original function’s code, the decorator wraps around it.
Using @ is just a shortcut. Even without @, the logic stays the same.
Real-Life Example
Decorators are often used for logging, timing functions, checking user permissions, or validating inputs, without touching the original function code.
10. Difference between shallow copy and deep copy.
A shallow copy creates a new object but does not copy nested objects.
A deep copy creates a new object and also copies all nested objects inside it.
| Shallow Copy |
Deep Copy |
| Copies only the outer object |
Copies both outer and inner objects |
| Nested objects are shared |
Nested objects are independent |
| Faster and uses less memory |
Slower and uses more memory |
Real-Life Example
Shallow copy is like copying a folder shortcut — both shortcuts point to the same files.
Deep copy is like copying the entire folder — changes in one don’t affect the other.
Welcome to the DSA in C++ Interview Questions section. This section covers commonly asked Data Structures and Algorithms questions in C++, frequently seen in top tech company interviews, explained in a simple and easy-to-understand way to help you clearly understand the logic and approach behind each problem.
1. Create a new array where each index stores the product of all elements except the element at that index. You must solve the problem without using division and in O(n) time complexity.
Instead of multiplying all elements for every index (which is slow), we split the work into two parts.
- First, we calculate the product of all elements on the left side of each index.
- Then, we calculate the product of all elements on the right side of each index.
Finally, we multiply the left and right products to get the result for each position.
#include <iostream>
#include <vector>
using namespace std;
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size();
vector<int> result(n, 1);
int left = 1;
for (int i = 0; i < n; i++) {
result[i] = left;
left *= nums[i];
}
int right = 1;
for (int i = n - 1; i >= 0; i--) {
result[i] *= right;
right *= nums[i];
}
return result;
}
int main() {
vector<int> nums = {1, 2, 3, 4};
vector<int> output = productExceptSelf(nums);
cout << "Input: ";
for (int x : nums) cout << x << " ";
cout << "\nOutput: ";
for (int x : output) cout << x << " ";
return 0;
}
2. You are given an array of positive numbers and a target value. Your task is to find the smallest length of a continuous subarray whose sum is greater than or equal to the target.
Instead of checking every possible subarray, we use a sliding window.
We keep adding numbers to the window until the sum becomes equal to or greater than the target.
Once that happens, we try to shrink the window from the left to find the smallest possible size.
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int minSubArrayLen(int target, vector<int>& nums) {
int left = 0;
int sum = 0;
int minLen = INT_MAX;
for (int right = 0; right < nums.size(); right++) {
sum += nums[right];
while (sum >= target) {
minLen = min(minLen, right - left + 1);
sum -= nums[left];
left++;
}
}
return (minLen == INT_MAX) ? 0 : minLen;
}
int main() {
vector<int> nums = {2, 3, 1, 2, 4, 3};
int target = 7;
cout << "Output: " << minSubArrayLen(target, nums);
return 0;
}
3. You are given a string. Your task is to find the length of the longest substring that contains no repeating characters. A substring must be continuous.
Instead of checking all substrings, we use a sliding window.
We move a window across the string and keep track of characters we have already seen.
If a character repeats, we move the left side of the window forward until the substring becomes unique again.
This approach scans the string only once, so it is fast.
#include <iostream>
#include <unordered_set>
using namespace std;
int longestUniqueSubstring(string s) {
unordered_set<char> window;
int left = 0, maxLength = 0;
for (int right = 0; right < s.length(); right++) {
while (window.count(s[right])) {
window.erase(s[left]);
left++;
}
window.insert(s[right]);
maxLength = max(maxLength, right - left + 1);
}
return maxLength;
}
int main() {
string s = "abcabcbb"; // Input string
int result = longestUniqueSubstring(s);
cout << "Input: " << s << endl;
cout << "Output: " << result << endl;
return 0;
}
4. You are given a list of intervals, where each interval has a start and an end value. If any intervals overlap, merge them into a single interval and return the final list. Intervals that do not overlap should remain as they are.
First, we sort the intervals based on their starting values.
Then we go through them one by one and compare the current interval with the last merged interval.
- If they overlap, we merge them.
- If they don’t overlap, we add the interval as it is.
This way, all overlapping intervals are combined efficiently in a single pass.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<vector<int>> mergeIntervals(vector<vector<int>>& intervals) {
vector<vector<int>> result;
if (intervals.empty())
return result;
sort(intervals.begin(), intervals.end());
result.push_back(intervals[0]);
for (int i = 1; i < intervals.size(); i++) {
if (intervals[i][0] <= result.back()[1]) {
result.back()[1] = max(result.back()[1], intervals[i][1]);
} else {
result.push_back(intervals[i]);
}
}
return result;
}
int main() {
vector<vector<int>> intervals = {{1,3}, {2,6}, {8,10}, {15,18}};
vector<vector<int>> output = mergeIntervals(intervals);
cout << "Input Intervals: ";
for (auto &i : intervals)
cout << "[" << i[0] << "," << i[1] << "] ";
cout << "\nMerged Intervals: ";
for (auto &i : output)
cout << "[" << i[0] << "," << i[1] << "] ";
return 0;
}
5. You are given a square matrix (n × n). Rotate the matrix 90 degrees clockwise, in place, meaning you should not use any extra matrix for the result.
To rotate the matrix in place, we follow two clear steps:
- Transpose the matrix
This swaps rows with columns.
- Reverse each row
This converts the transposed matrix into a 90-degree rotated matrix.
These two steps modify the matrix directly, without using any extra space.
#include <iostream>
#include <vector>
using namespace std;
void rotateMatrix(vector<vector<int>>& matrix) {
int n = matrix.size();
// Step 1: Transpose the matrix
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
swap(matrix[i][j], matrix[j][i]);
}
}
// Step 2: Reverse each row
for (int i = 0; i < n; i++) {
reverse(matrix[i].begin(), matrix[i].end());
}
}
int main() {
vector<vector<int>> matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
rotateMatrix(matrix);
cout << "Rotated Matrix:\n";
for (auto &row : matrix) {
for (int val : row)
cout << val << " ";
cout << endl;
}
return 0;
}
6. You are given a main string and a pattern string; find all starting indices in the main string where a substring is an anagram of the pattern. For example, if the string is "cbaebabacd" and the pattern is "abc", the output is [0, 6] because "cba" and "bac" are valid anagrams of "abc".
An anagram means the same characters with the same frequency but in a different order.
We slide a window of the same length as the pattern string across the main string and compare character counts.
Whenever the counts match, we store the starting index.
This approach is fast because we reuse previous calculations instead of checking from scratch every time.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
vector<int> findAnagrams(string s, string p) {
vector<int> result;
if (s.size() < p.size()) return result;
vector<int> countP(26, 0), countS(26, 0);
for (char c : p)
countP[c - 'a']++;
int windowSize = p.size();
for (int i = 0; i < s.size(); i++) {
countS[s[i] - 'a']++;
if (i >= windowSize)
countS[s[i - windowSize] - 'a']--;
if (countS == countP)
result.push_back(i - windowSize + 1);
}
return result;
}
int main() {
string s = "cbaebabacd";
string p = "abc";
vector<int> output = findAnagrams(s, p);
cout << "Input String: " << s << endl;
cout << "Pattern: " << p << endl;
cout << "Output Indices: ";
for (int index : output)
cout << index << " ";
return 0;
}
7. You are given an array of numbers and a target value. Find two different indices such that the numbers at those indices add up to the target, and return the indices.
Instead of checking every pair, we store numbers in a hash map while scanning the array.
For each number, we check if the remaining value (target − current number) already exists.
This gives a fast solution in one pass.
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> seen;
for (int i = 0; i < nums.size(); i++) {
int need = target - nums[i];
if (seen.count(need))
return {seen[need], i};
seen[nums[i]] = i;
}
return {};
}
int main() {
vector<int> nums = {2, 7, 11, 15};
int target = 9;
vector<int> result = twoSum(nums, target);
cout << "Output: [" << result[0] << ", " << result[1] << "]";
return 0;
}
8. You are given an array of integers and a number k. Count how many continuous subarrays have a total sum equal to k.
We use a prefix sum and a hash map.
As we move through the array, we store how many times a prefix sum has appeared.
If currentSum - k exists in the map, it means a subarray with sum k is found.
This avoids checking all subarrays and works in one pass.
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
int subarraySum(vector<int>& nums, int k) {
unordered_map<int, int> prefixCount;
prefixCount[0] = 1;
int sum = 0, count = 0;
for (int num : nums) {
sum += num;
if (prefixCount.count(sum - k))
count += prefixCount[sum - k];
prefixCount[sum]++;
}
return count;
}
int main() {
vector<int> nums = {1, 1, 1};
int k = 2;
cout << "Output: " << subarraySum(nums, k);
return 0;
}
9. You are given an unsorted array of integers. Find the length of the longest sequence of consecutive numbers that appear in the array. The solution must run in O(n) time.
We put all numbers into a hash set so lookup is fast.
Then we only start counting when a number has no previous consecutive number (number − 1).
From there, we keep checking the next numbers until the sequence breaks.
Each number is processed once, so the solution stays linear.
#include <iostream>
#include <vector>
#include <unordered_set>
using namespace std;
int longestConsecutive(vector<int>& nums) {
unordered_set<int> s(nums.begin(), nums.end());
int longest = 0;
for (int num : s) {
if (!s.count(num - 1)) {
int current = num;
int length = 1;
while (s.count(current + 1)) {
current++;
length++;
}
longest = max(longest, length);
}
}
return longest;
}
int main() {
vector<int> nums = {100, 4, 200, 1, 3, 2};
cout << "Output: " << longestConsecutive(nums);
return 0;
}
Welcome to the Java Interview Questions section. This section covers commonly asked Java interview questions, frequently seen in top tech company interviews, explained in a simple and easy-to-understand way to help you clearly understand core Java concepts and how they are used in real applications.
1. How does Java achieve platform independence?
Java achieves platform independence by compiling source code into bytecode, not machine code.
This bytecode can run on any system that has a Java Virtual Machine (JVM), no matter the operating system.
The JVM reads the bytecode and converts it into machine-specific instructions.
To improve performance, the JIT (Just-In-Time) compiler translates frequently used bytecode into native machine code at runtime, making Java programs run faster.
2. Difference between == and .equals() in Java
In Java, == compares object references, meaning it checks whether both variables point to the same memory location.
The .equals() method compares actual values or content inside the objects.
For String objects, Java uses a String pool. If two strings are created using string literals, they may point to the same memory location, so == can return true.
But if strings are created using new, they are stored separately in memory, and == will return false even if the content is the same, while .equals() will return true.
3. What happens internally when you create a String in Java?What happens internally when you create a String in Java? String s = "meta"; String s2 = new String("meta");
When Java sees String s = “meta”;, it first checks the
String pool. If "meta" already exists, Java
simply points s to that object. If not, Java creates one String
object in the pool and uses it.
Now look at this line:
String s2 = new String("meta");
Here, Java always creates a new String object in heap memory,
even if "meta" already exists in the String pool. So s2
points to a different object.
"meta" → reused from the String pool
new String("meta") → creates a new object in heap
Java uses the String pool to save memory and improve performance, while
new String() is used when you explicitly need a separate object.
4. Why is String immutable in Java?
Java Strings are immutable, which means once a String is created, it cannot be changed. This design choice solves several important problems.
Thread safety:
Because Strings cannot be modified, multiple threads can use the same String object safely without synchronization. There is no risk that one thread will change the value while another is using it.
Security:
Strings are widely used in sensitive areas like file paths, database URLs, and passwords. If Strings were mutable, someone could change these values after they are used, which would be a serious security risk.
Hashcode caching:
Strings are commonly used as keys in hash-based collections like HashMap. Since a String never changes, its hashcode can be calculated once and reused, which makes lookups faster and more reliable.
5. Difference between final, finally, and finalize()
final
final is a keyword used to prevent changes. A final variable cannot be reassigned, a final method cannot be overridden, and a final class cannot be inherited.
finally
finally is a block used with try-catch. It always runs and is mainly used for cleanup like closing files or database connections.
finalize()
finalize() is a method called by the garbage collector before an object is removed, but it is rarely used and not reliable.
| Term |
Used For |
Purpose |
| final |
Keyword |
Prevents modification |
| finally |
Block |
Always executes after try-catch |
| finalize() |
Method |
Runs before garbage collection |