A list is Python's dynamic array: an ordered, index-addressable, resizable collection (the same idea as C++ std::vector, Java ArrayList and the JavaScript Array). Arrays are the foundation that almost every other data structure is built on, so knowing exactly what is cheap and what is expensive on one is the first DSA skill.
list = [ 10, 20, 30, 40, 50 ]
index 0 1 2 3 4
-ve index -5 -4 -3 -2 -1How a dynamic array grows
The list pre-allocates a little extra room. append writes into that room in O(1). Only when the room is gone does the list resize:
Allocate a bigger block
Typically a multiple of the current capacity (×2 in most textbooks and in C++
vector; CPython uses a gentler ≈1.125× plus a constant, JavaArrayListuses 1.5×).Copy
Move all
nexisting elements into the new block — this is theO(n)step.Free the old block
The list now points at the new memory and has spare capacity again.
capacity 4 (full) append(50) → grow to capacity 8, copy 4, write 1
┌────┬────┬────┬────┐ ┌────┬────┬────┬────┬────┬────┬────┬────┐
│ 10 │ 20 │ 30 │ 40 │ → │ 10 │ 20 │ 30 │ 40 │ 50 │ │ │ │
└────┴────┴────┴────┘ └────┴────┴────┴────┴────┴────┴────┴────┘
↑ next 3 appends are O(1)With doubling, inserting n elements costs at most n + (1 + 2 + 4 + … + n) < 3n element writes → O(1) amortised per append
| Python | C++ | Java | JavaScript | |
|---|---|---|---|---|
| Type | list | std::vector<T> | ArrayList<T> | Array |
| Fixed-size cousin | array.array, NumPy | T arr[n], std::array | int[] arr | typed arrays |
| Mixed element types | yes (references) | no | no (one T) | yes |
| Negative indices | yes | no | no | arr.at(-1) only |
Operations and their complexities
| Operation | Python | Time | Why |
|---|---|---|---|
| Read / write by index | l[i], l[i] = x | O(1) | address arithmetic |
| Append at end | l.append(x) | O(1) amortised | spare capacity; rare copy |
| Pop from end | l.pop() | O(1) | nothing to shift |
| Insert at index | l.insert(i, x) | O(n) | shifts n - i elements right |
| Delete by index / value | del l[i], l.pop(i), l.remove(x) | O(n) | shifts elements left (remove also searches) |
| Search | x in l, l.index(x), l.count(x) | O(n) | linear scan |
| Length | len(l) | O(1) | stored with the list |
| Min / max / sum | min(l), max(l), sum(l) | O(n) | one pass |
| Reverse | l.reverse(), l[::-1] | O(n) | swap from both ends |
| Sort | l.sort(), sorted(l) | O(n log n) | Timsort |
| Slice | l[a:b] | O(b − a) | copies the slice |
Indexing and negative indices
Python lets you count from the back with negative indices; the other languages need n - k. Reading past either end is an IndexError in Python, an exception in Java, undefined in JavaScript and undefined behaviour in C++ (v.at(i) checks, v[i] does not).
l = [10, 20, 30, 40, 50]
print("first:", l[0])
print("fourth:", l[3])
print("last:", l[-1]) # negative index counts from the end
print("third from end:", l[-3])
print("length:", len(l))#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {10, 20, 30, 40, 50};
int n = v.size();
cout << "first: " << v[0] << "\n";
cout << "fourth: " << v[3] << "\n";
cout << "last: " << v[n - 1] << "\n"; // no negative indices: use n - k
cout << "third from end: " << v[n - 3] << "\n";
cout << "length: " << n << "\n";
}import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> l = new ArrayList<>(List.of(10, 20, 30, 40, 50));
int n = l.size();
System.out.println("first: " + l.get(0));
System.out.println("fourth: " + l.get(3));
System.out.println("last: " + l.get(n - 1)); // no negative indices: use n - k
System.out.println("third from end: " + l.get(n - 3));
System.out.println("length: " + n);
}
}const l = [10, 20, 30, 40, 50];
const n = l.length;
console.log("first:", l[0]);
console.log("fourth:", l[3]);
console.log("last:", l[n - 1]); // l.at(-1) also works
console.log("third from end:", l.at(-3));
console.log("length:", n);first: 10
fourth: 40
last: 50
third from end: 30
length: 5Adding and searching
append goes to the end; insert makes room at an index by shifting. Membership (in), count and index all scan from the left — there is no faster way on an unsorted array.
l = [10, 20, 30, 40, 50]
l.append(30) # add at the end — amortised O(1)
print(*l)
l.insert(1, 60) # insert at index 1 — O(n): shifts everything after it
print(*l)
print("contains 60:", "true" if 60 in l else "false") # linear search
print("count of 30:", l.count(30))
print("first 30 at:", l.index(30)) # ValueError if absent
print("30 at or after index 4:", l.index(30, 4)) # search l[4:] only#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
void show(const vector<int>& v) {
for (int i = 0; i < (int)v.size(); i++) cout << (i ? " " : "") << v[i];
cout << "\n";
}
int main() {
vector<int> v = {10, 20, 30, 40, 50};
v.push_back(30); // add at the end — amortised O(1)
show(v);
v.insert(v.begin() + 1, 60); // insert at index 1 — O(n)
show(v);
bool has = find(v.begin(), v.end(), 60) != v.end();
cout << "contains 60: " << (has ? "true" : "false") << "\n";
cout << "count of 30: " << count(v.begin(), v.end(), 30) << "\n";
cout << "first 30 at: " << (find(v.begin(), v.end(), 30) - v.begin()) << "\n";
cout << "30 at or after index 4: " << (find(v.begin() + 4, v.end(), 30) - v.begin()) << "\n";
}import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
static String show(List<Integer> l) {
StringBuilder sb = new StringBuilder();
for (int x : l) sb.append(x).append(' ');
return sb.toString().trim();
}
public static void main(String[] args) {
List<Integer> l = new ArrayList<>(List.of(10, 20, 30, 40, 50));
l.add(30); // add at the end — amortised O(1)
System.out.println(show(l));
l.add(1, 60); // insert at index 1 — O(n)
System.out.println(show(l));
System.out.println("contains 60: " + l.contains(60));
System.out.println("count of 30: " + Collections.frequency(l, 30));
System.out.println("first 30 at: " + l.indexOf(30)); // -1 if absent
System.out.println("30 at or after index 4: " + (l.subList(4, l.size()).indexOf(30) + 4));
}
}const l = [10, 20, 30, 40, 50];
l.push(30); // add at the end — amortised O(1)
console.log(l.join(" "));
l.splice(1, 0, 60); // insert at index 1 — O(n)
console.log(l.join(" "));
console.log("contains 60:", l.includes(60));
console.log("count of 30:", l.filter(x => x === 30).length);
console.log("first 30 at:", l.indexOf(30)); // -1 if absent
console.log("30 at or after index 4:", l.indexOf(30, 4));10 20 30 40 50 30
10 60 20 30 40 50 30
contains 60: true
count of 30: 2
first 30 at: 3
30 at or after index 4: 6Removing elements
Three ways to remove in Python: remove(x) by value (first match), pop(i) by index returning the element, del l[i] / del l[a:b] by index or slice. Everything except popping from the end is O(n).
l = [10, 20, 30, 40, 50, 60, 70]
l.remove(30) # first 30, by value — O(n); ValueError if absent
print(*l)
print("popped:", l.pop()) # remove and return the last — O(1)
print(*l)
print("popped at 2:", l.pop(2)) # by index — O(n)
print(*l)
del l[1] # delete by index
print(*l)
del l[0:2] # delete a slice
print(*l)#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
void show(const vector<int>& v) {
for (int i = 0; i < (int)v.size(); i++) cout << (i ? " " : "") << v[i];
cout << "\n";
}
int main() {
vector<int> v = {10, 20, 30, 40, 50, 60, 70};
v.erase(find(v.begin(), v.end(), 30)); // first 30, by value — O(n)
show(v);
int last = v.back(); v.pop_back(); // pop_back() returns nothing
cout << "popped: " << last << "\n";
show(v);
int at2 = v[2]; v.erase(v.begin() + 2); // by index — O(n)
cout << "popped at 2: " << at2 << "\n";
show(v);
v.erase(v.begin() + 1); // delete by index
show(v);
v.erase(v.begin(), v.begin() + 2); // delete the range [0, 2)
show(v);
}import java.util.ArrayList;
import java.util.List;
public class Main {
static String show(List<Integer> l) {
StringBuilder sb = new StringBuilder();
for (int x : l) sb.append(x).append(' ');
return sb.toString().trim();
}
public static void main(String[] args) {
List<Integer> l = new ArrayList<>(List.of(10, 20, 30, 40, 50, 60, 70));
l.remove(Integer.valueOf(30)); // by value — remove(int) would mean "by index"!
System.out.println(show(l));
System.out.println("popped: " + l.remove(l.size() - 1)); // remove the last
System.out.println(show(l));
System.out.println("popped at 2: " + l.remove(2)); // by index — O(n)
System.out.println(show(l));
l.remove(1);
System.out.println(show(l));
l.subList(0, 2).clear(); // delete the range [0, 2)
System.out.println(show(l));
}
}const l = [10, 20, 30, 40, 50, 60, 70];
l.splice(l.indexOf(30), 1); // first 30, by value — O(n)
console.log(l.join(" "));
console.log("popped:", l.pop()); // remove and return the last — O(1)
console.log(l.join(" "));
console.log("popped at 2:", l.splice(2, 1)[0]); // by index — O(n)
console.log(l.join(" "));
l.splice(1, 1); // delete by index
console.log(l.join(" "));
l.splice(0, 2); // delete 2 elements from index 0
console.log(l.join(" "));10 20 40 50 60 70
popped: 70
10 20 40 50 60
popped at 2: 40
10 20 50 60
10 50 60
60min, max, sum, reverse and sort
l = [40, 10, 50, 20, 30]
print("min:", min(l))
print("max:", max(l))
print("sum:", sum(l))
l.reverse() # in place; reversed(l) gives an iterator instead
print("reversed:", *l)
l.sort() # in place, O(n log n); sorted(l) returns a new list
print("sorted:", *l)
l.sort(reverse=True)
print("sorted descending:", *l)#include <algorithm>
#include <functional>
#include <iostream>
#include <numeric>
#include <string>
#include <vector>
using namespace std;
void show(const string& label, const vector<int>& v) {
cout << label;
for (int x : v) cout << " " << x;
cout << "\n";
}
int main() {
vector<int> v = {40, 10, 50, 20, 30};
cout << "min: " << *min_element(v.begin(), v.end()) << "\n";
cout << "max: " << *max_element(v.begin(), v.end()) << "\n";
cout << "sum: " << accumulate(v.begin(), v.end(), 0) << "\n";
reverse(v.begin(), v.end());
show("reversed:", v);
sort(v.begin(), v.end()); // O(n log n)
show("sorted:", v);
sort(v.begin(), v.end(), greater<int>());
show("sorted descending:", v);
}import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
static String show(List<Integer> l) {
StringBuilder sb = new StringBuilder();
for (int x : l) sb.append(' ').append(x);
return sb.toString();
}
public static void main(String[] args) {
List<Integer> l = new ArrayList<>(List.of(40, 10, 50, 20, 30));
System.out.println("min: " + Collections.min(l));
System.out.println("max: " + Collections.max(l));
int sum = 0;
for (int x : l) sum += x;
System.out.println("sum: " + sum);
Collections.reverse(l);
System.out.println("reversed:" + show(l));
Collections.sort(l); // O(n log n)
System.out.println("sorted:" + show(l));
l.sort(Collections.reverseOrder());
System.out.println("sorted descending:" + show(l));
}
}const l = [40, 10, 50, 20, 30];
console.log("min:", Math.min(...l));
console.log("max:", Math.max(...l));
console.log("sum:", l.reduce((a, b) => a + b, 0));
l.reverse(); // in place
console.log("reversed:", l.join(" "));
l.sort((a, b) => a - b); // ALWAYS pass a comparator for numbers
console.log("sorted:", l.join(" "));
l.sort((a, b) => b - a);
console.log("sorted descending:", l.join(" "));min: 10
max: 50
sum: 150
reversed: 30 20 50 10 40
sorted: 10 20 30 40 50
sorted descending: 50 40 30 20 10Two language-specific traps worth seeing once:
l = [10, 'twenty', 30] # a list may mix types …
print(sum(l)) # … but sum(), sort(), min() need comparable/addable elementsTypeError: unsupported operand type(s) for +: 'int' and 'str'console.log([10, 9, 1].sort().join(" ")); // compares as strings: "10" < "9"
console.log([10, 9, 1].sort((a, b) => a - b).join(" "));1 10 9
1 9 10Slicing
l[start:stop:step] returns a new list with the elements from start up to but not including stop, every step-th one. Every part is optional; negative indices and a negative step work too. Slicing also works on tuples and strings.
| Slice | Meaning | Result for [10, 20, 11, 40, 15, 19] |
|---|---|---|
l[0:5] | indices 0..4 | [10, 20, 11, 40, 15] |
l[0:5:2] | indices 0, 2, 4 | [10, 11, 15] |
l[:] | a shallow copy of everything | [10, 20, 11, 40, 15, 19] |
l[3:] | from index 3 to the end | [40, 15, 19] |
l[:3] | the first three | [10, 20, 11] |
l[-2:] | the last two | [15, 19] |
l[::-1] | reversed copy | [19, 15, 40, 11, 20, 10] |
l = [10, 20, 11, 40, 15, 19]
print(l[0:5]) # [10, 20, 11, 40, 15]
print(l[0:5:2]) # [10, 11, 15]
print(l[:]) # [10, 20, 11, 40, 15, 19]
print(l[3:]) # [40, 15, 19]
print(l[:3]) # [10, 20, 11]
print(l[-2:]) # [15, 19]
print(l[::-1]) # [19, 15, 40, 11, 20, 10]
print(l[10:]) # [] ← out-of-range slices are clipped, never an errorA slice is a copy: equal in value (==) but a different object (is). Note that l in dup asks whether the whole list l is one of dup's elements — not whether the elements overlap.
l = [10, 20, 11, 40, 15, 19]
dup = l[:] # same as l.copy() or list(l)
print(dup == l) # True ← same contents
print(dup is l) # False ← different objects in memory
dup[0] = 99
print(l[0]) # 10 ← changing the copy leaves l alone
print(l in dup) # False ← is the list l an *element* of dup? no
t1 = (10, 20, 30) # tuples slice exactly the same way …
print(t1[1:]) # (20, 30)
print(t1 == (10, 20, 30)) # True
print(10 in t1) # True| Idiom | Python | C++ | Java | JavaScript |
|---|---|---|---|---|
| Sub-range copy | l[a:b] | vector<int>(v.begin()+a, v.begin()+b) | new ArrayList<>(l.subList(a, b)) | arr.slice(a, b) |
| Copy | l[:], l.copy() | vector<int> c = v; | new ArrayList<>(l) | [...arr], arr.slice() |
| Last k | l[-k:] | vector<int>(v.end()-k, v.end()) | l.subList(n-k, n) | arr.slice(-k) |
Comprehensions
A comprehension builds a new collection from any iterable (a list, a range, a string, a file …) in one expression: [expression for item in iterable if condition]. It is the Pythonic replacement for the “make an empty list, loop, append” pattern — same O(n) time, less code.
l = [x for x in range(11)]
print(l) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print([i for i in range(11) if i % 2 == 0]) # [0, 2, 4, 6, 8, 10]
print([x for x in range(11) if x % 2 == 1]) # [1, 3, 5, 7, 9]
print([x * x for x in range(11)]) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
s = "ThrishankKuntimaddi"
print([ch for ch in s if ch in 'aeiou']) # ['i', 'a', 'u', 'i', 'a', 'i']
names = ['Thrishank', 'Kuntimaddi', 'ThrishankKuntimaddi', 'Nani']
print([n for n in names if n.startswith("T")]) # ['Thrishank', 'ThrishankKuntimaddi']
print([n.lower() for n in names if n.startswith("T")]) # ['thrishank', 'thrishankkuntimaddi']The two list problems below, written as comprehensions:
l = [10, 20, 11, 40, 15, 19]
def get_smaller(l, x):
return [e for e in l if e < x]
def get_even_odd(l):
even = [i for i in l if i % 2 == 0]
odd = [i for i in l if i % 2 == 1]
return even, odd
print(get_smaller(l, 21)) # [10, 20, 11, 15, 19]
even, odd = get_even_odd(l)
print(even) # [10, 20, 40]
print(odd) # [11, 15, 19]Curly braces give a set or dict comprehension; zip pairs two lists up so dict(zip(keys, values)) builds a mapping.
print({x for x in range(11)}) # {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} ← set: no duplicates, no order guarantee
print({x: x * 2 for x in range(6)}) # {0: 0, 1: 2, 2: 4, 3: 6, 4: 8, 5: 10}
l1 = [10, 20, 30]
l2 = ['a', 'b', 'c']
print(dict(zip(l1, l2))) # {10: 'a', 20: 'b', 30: 'c'}The same filter → map idea exists everywhere; only Python has the dedicated syntax. Squares of the even numbers in 0..10:
squares = [x * x for x in range(11) if x % 2 == 0]
print(*squares)#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> squares;
for (int x = 0; x <= 10; x++) // no comprehension: a plain loop
if (x % 2 == 0) squares.push_back(x * x);
for (int i = 0; i < (int)squares.size(); i++) cout << (i ? " " : "") << squares[i];
cout << "\n";
}import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
String squares = IntStream.rangeClosed(0, 10)
.filter(x -> x % 2 == 0)
.map(x -> x * x)
.mapToObj(String::valueOf)
.collect(Collectors.joining(" "));
System.out.println(squares);
}
}const squares = Array.from({ length: 11 }, (_, i) => i)
.filter(x => x % 2 === 0)
.map(x => x * x);
console.log(squares.join(" "));0 4 16 36 64 100Problem: average (mean) of a list
def average(l):
total = 0
for x in l:
total += x
return total / len(l) # / is true division: 100 / 4 → 25.0 (sum(l) / len(l) is the idiom)
for l in ([10, 20, 30, 40], [3, 4, 4]):
print(f"{average(l):.1f}")#include <iomanip>
#include <iostream>
#include <vector>
using namespace std;
double average(const vector<int>& v) {
long long total = 0;
for (int x : v) total += x;
return (double)total / v.size(); // cast first — integer division would give 25 and 3
}
int main() {
cout << fixed << setprecision(1);
cout << average({10, 20, 30, 40}) << "\n";
cout << average({3, 4, 4}) << "\n";
}public class Main {
static double average(int[] a) {
long total = 0;
for (int x : a) total += x;
return (double) total / a.length; // cast first — integer division would give 25 and 3
}
public static void main(String[] args) {
System.out.println(String.format("%.1f", average(new int[]{10, 20, 30, 40})));
System.out.println(String.format("%.1f", average(new int[]{3, 4, 4})));
}
}function average(arr) {
let total = 0;
for (const x of arr) total += x;
return total / arr.length; // JS numbers are doubles: 100 / 4 = 25, 11 / 3 = 3.666…
}
for (const arr of [[10, 20, 30, 40], [3, 4, 4]])
console.log(average(arr).toFixed(1));25.0
3.7Time O(n) · Space O(1)
Problem: separate even and odd numbers
def get_even_odd(l):
even, odd = [], []
for x in l:
if x % 2 == 0:
even.append(x)
else:
odd.append(x)
return even, odd
even, odd = get_even_odd([10, 20, 11, 40, 15, 19])
print("even:", *even)
print("odd:", *odd)#include <iostream>
#include <string>
#include <vector>
using namespace std;
void show(const string& label, const vector<int>& v) {
cout << label;
for (int x : v) cout << " " << x;
cout << "\n";
}
int main() {
vector<int> l = {10, 20, 11, 40, 15, 19};
vector<int> even, odd;
for (int x : l) {
if (x % 2 == 0) even.push_back(x);
else odd.push_back(x);
}
show("even:", even);
show("odd:", odd);
}import java.util.ArrayList;
import java.util.List;
public class Main {
static String show(List<Integer> l) {
StringBuilder sb = new StringBuilder();
for (int x : l) sb.append(' ').append(x);
return sb.toString();
}
public static void main(String[] args) {
int[] l = {10, 20, 11, 40, 15, 19};
List<Integer> even = new ArrayList<>(), odd = new ArrayList<>();
for (int x : l) {
if (x % 2 == 0) even.add(x);
else odd.add(x);
}
System.out.println("even:" + show(even));
System.out.println("odd:" + show(odd));
}
}function getEvenOdd(arr) {
const even = [], odd = [];
for (const x of arr) {
if (x % 2 === 0) even.push(x);
else odd.push(x);
}
return [even, odd];
}
const [even, odd] = getEvenOdd([10, 20, 11, 40, 15, 19]);
console.log("even:", even.join(" "));
console.log("odd:", odd.join(" "));even: 10 20 40
odd: 11 15 19Time O(n) · Space O(n)
Problem: elements smaller than x
def get_smaller(l, x):
res = []
for e in l:
if e < x:
res.append(e)
return res # or simply: [e for e in l if e < x]
print("smaller than 21:", *get_smaller([10, 20, 11, 40, 15, 19], 21))#include <iostream>
#include <vector>
using namespace std;
vector<int> getSmaller(const vector<int>& l, int x) {
vector<int> res;
for (int e : l)
if (e < x) res.push_back(e);
return res;
}
int main() {
vector<int> res = getSmaller({10, 20, 11, 40, 15, 19}, 21);
cout << "smaller than 21:";
for (int e : res) cout << " " << e;
cout << "\n";
}import java.util.ArrayList;
import java.util.List;
public class Main {
static List<Integer> getSmaller(int[] l, int x) {
List<Integer> res = new ArrayList<>();
for (int e : l)
if (e < x) res.add(e);
return res;
}
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("smaller than 21:");
for (int e : getSmaller(new int[]{10, 20, 11, 40, 15, 19}, 21)) sb.append(' ').append(e);
System.out.println(sb);
}
}function getSmaller(arr, x) {
const res = [];
for (const e of arr)
if (e < x) res.push(e);
return res; // or simply: arr.filter(e => e < x)
}
console.log("smaller than 21:", getSmaller([10, 20, 11, 40, 15, 19], 21).join(" "));smaller than 21: 10 20 11 15 19Time O(n) · Space O(n)
Problem: largest element
The naive approach checks, for every x, whether no element is bigger than it. Python's for … else runs the else only when the inner loop never hit break — handy, but the method is O(n²):
def get_max_naive(l):
for x in l:
for y in l:
if y > x:
break # x is not the largest
else:
return x # the inner loop never broke → nothing is bigger
return None
print(get_max_naive([10, 5, 20, 8]))20def get_max(l):
if not l:
return None
res = l[0]
for i in range(1, len(l)):
if l[i] > res:
res = l[i]
return res # built-in: max(l) — but it also raises on an empty list
print("largest:", get_max([10, 5, 20, 8]))
print("largest:", get_max([]))#include <iostream>
#include <optional>
#include <vector>
using namespace std;
optional<int> getMax(const vector<int>& l) {
if (l.empty()) return nullopt;
int res = l[0];
for (int i = 1; i < (int)l.size(); i++)
if (l[i] > res) res = l[i];
return res; // library: *max_element(l.begin(), l.end())
}
void show(optional<int> r) {
if (r) cout << "largest: " << *r << "\n";
else cout << "largest: None\n";
}
int main() {
show(getMax({10, 5, 20, 8}));
show(getMax({}));
}public class Main {
static Integer getMax(int[] l) {
if (l.length == 0) return null;
int res = l[0];
for (int i = 1; i < l.length; i++)
if (l[i] > res) res = l[i];
return res;
}
static void show(Integer r) {
System.out.println("largest: " + (r == null ? "None" : r.toString()));
}
public static void main(String[] args) {
show(getMax(new int[]{10, 5, 20, 8}));
show(getMax(new int[]{}));
}
}function getMax(arr) {
if (arr.length === 0) return null;
let res = arr[0];
for (let i = 1; i < arr.length; i++)
if (arr[i] > res) res = arr[i];
return res; // Math.max(...arr) works too, but gives -Infinity for []
}
const show = r => console.log("largest:", r === null ? "None" : r);
show(getMax([10, 5, 20, 8]));
show(getMax([]));largest: 20
largest: NoneTime O(n) · Space O(1)
Problem: second largest element
Two-pass version: find the largest, then the largest among elements not equal to it. Correct and O(n), but it reads the list twice:
def second_largest_two_pass(l):
if len(l) <= 1:
return None
largest = max(l)
second = None
for x in l:
if x != largest: # skip every copy of the maximum
if second is None or x > second:
second = x
return second
print(second_largest_two_pass([10, 20, 4, 5, 6, 1]))
print(second_largest_two_pass([10, 10, 10]))10
Nonedef second_largest(l):
if len(l) <= 1:
return None
largest, second = l[0], None
for x in l[1:]:
if x > largest:
second, largest = largest, x # old largest drops to second place
elif x != largest:
if second is None or x > second:
second = x
return second
for l in ([10, 20, 4, 5, 6, 1], [10, 10, 10], [20, 30, 20, 30]):
print("second largest:", second_largest(l))#include <iostream>
#include <optional>
#include <vector>
using namespace std;
optional<int> secondLargest(const vector<int>& l) {
if (l.size() <= 1) return nullopt;
int largest = l[0];
optional<int> second;
for (int i = 1; i < (int)l.size(); i++) {
int x = l[i];
if (x > largest) {
second = largest; // old largest drops to second place
largest = x;
} else if (x != largest) {
if (!second || x > *second) second = x;
}
}
return second;
}
int main() {
for (vector<int> l : {vector<int>{10, 20, 4, 5, 6, 1}, {10, 10, 10}, {20, 30, 20, 30}}) {
optional<int> r = secondLargest(l);
if (r) cout << "second largest: " << *r << "\n";
else cout << "second largest: None\n";
}
}public class Main {
static Integer secondLargest(int[] l) {
if (l.length <= 1) return null;
int largest = l[0];
Integer second = null;
for (int i = 1; i < l.length; i++) {
int x = l[i];
if (x > largest) {
second = largest; // old largest drops to second place
largest = x;
} else if (x != largest) {
if (second == null || x > second) second = x;
}
}
return second;
}
public static void main(String[] args) {
int[][] tests = {{10, 20, 4, 5, 6, 1}, {10, 10, 10}, {20, 30, 20, 30}};
for (int[] l : tests) {
Integer r = secondLargest(l);
System.out.println("second largest: " + (r == null ? "None" : r.toString()));
}
}
}function secondLargest(arr) {
if (arr.length <= 1) return null;
let largest = arr[0], second = null;
for (let i = 1; i < arr.length; i++) {
const x = arr[i];
if (x > largest) {
second = largest; // old largest drops to second place
largest = x;
} else if (x !== largest) {
if (second === null || x > second) second = x;
}
}
return second;
}
for (const arr of [[10, 20, 4, 5, 6, 1], [10, 10, 10], [20, 30, 20, 30]])
console.log("second largest:", secondLargest(arr) === null ? "None" : secondLargest(arr));second largest: 10
second largest: None
second largest: 20Time O(n) · Space O(1)
Problem: check if a list is sorted
Naive: sort a copy and compare — O(n log n) time and O(n) extra space just to answer a yes/no question:
def is_sorted_naive(l):
return sorted(l) == l
print("Yes" if is_sorted_naive([10, 20, 30]) else "No")
print("Yes" if is_sorted_naive([29, 24, 32]) else "No")Yes
Nodef is_sorted(l):
for i in range(1, len(l)):
if l[i] < l[i - 1]: # one inversion is enough to say No
return False
return True
for l in ([10, 20, 30], [29, 24, 32], [7, 7, 7], []):
print(" ".join(map(str, l)) + ":", "Yes" if is_sorted(l) else "No")#include <iostream>
#include <vector>
using namespace std;
bool isSorted(const vector<int>& l) {
for (int i = 1; i < (int)l.size(); i++)
if (l[i] < l[i - 1]) return false; // one inversion is enough to say No
return true; // library: is_sorted(l.begin(), l.end())
}
int main() {
for (vector<int> l : {vector<int>{10, 20, 30}, {29, 24, 32}, {7, 7, 7}, {}}) {
for (int i = 0; i < (int)l.size(); i++) cout << (i ? " " : "") << l[i];
cout << ": " << (isSorted(l) ? "Yes" : "No") << "\n";
}
}public class Main {
static boolean isSorted(int[] l) {
for (int i = 1; i < l.length; i++)
if (l[i] < l[i - 1]) return false; // one inversion is enough to say No
return true;
}
public static void main(String[] args) {
int[][] tests = {{10, 20, 30}, {29, 24, 32}, {7, 7, 7}, {}};
for (int[] l : tests) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < l.length; i++) sb.append(i > 0 ? " " : "").append(l[i]);
System.out.println(sb + ": " + (isSorted(l) ? "Yes" : "No"));
}
}
}function isSorted(arr) {
for (let i = 1; i < arr.length; i++)
if (arr[i] < arr[i - 1]) return false; // one inversion is enough to say No
return true;
}
for (const arr of [[10, 20, 30], [29, 24, 32], [7, 7, 7], []])
console.log(arr.join(" ") + ":", isSorted(arr) ? "Yes" : "No");10 20 30: Yes
29 24 32: No
7 7 7: Yes
: YesTime O(n) · Space O(1)
Problem: reverse a list
Python has four ready-made ways; reverse() works in place, the other three produce a new list:
l = [10, 20, 30]
l.reverse() # Method 1: in place
print(l) # [30, 20, 10]
l = [10, 20, 30]
print(l[::-1]) # [30, 20, 10] ← Method 2: reversed copy via slicing
print(list(reversed(l))) # [30, 20, 10] ← Method 3: reversed() iterator → list
res = [] # Method 4: walk from the back
for i in range(len(l) - 1, -1, -1):
res.append(l[i])
print(res) # [30, 20, 10]s e s e s e
↓ ↓ ↓ ↓ ↓ ↓
10 20 30 40 50 → 50 20 30 40 10 → 50 40 30 20 10
swap l[0], l[4] swap l[1], l[3] s ≥ e → stopdef reverse(l):
s, e = 0, len(l) - 1
while s < e:
l[s], l[e] = l[e], l[s] # swap the ends
s += 1
e -= 1
l = [10, 20, 30, 40, 50]
reverse(l)
print("reversed:", *l)#include <iostream>
#include <utility>
#include <vector>
using namespace std;
void reverseList(vector<int>& l) {
int s = 0, e = l.size() - 1;
while (s < e) {
swap(l[s], l[e]); // swap the ends
s++;
e--;
} // library: reverse(l.begin(), l.end())
}
int main() {
vector<int> l = {10, 20, 30, 40, 50};
reverseList(l);
cout << "reversed:";
for (int x : l) cout << " " << x;
cout << "\n";
}public class Main {
static void reverse(int[] l) {
int s = 0, e = l.length - 1;
while (s < e) {
int tmp = l[s]; l[s] = l[e]; l[e] = tmp; // swap the ends
s++;
e--;
}
}
public static void main(String[] args) {
int[] l = {10, 20, 30, 40, 50};
reverse(l);
StringBuilder sb = new StringBuilder("reversed:");
for (int x : l) sb.append(' ').append(x);
System.out.println(sb);
}
}function reverse(arr) {
let s = 0, e = arr.length - 1;
while (s < e) {
[arr[s], arr[e]] = [arr[e], arr[s]]; // swap the ends
s++;
e--;
} // library: arr.reverse()
}
const l = [10, 20, 30, 40, 50];
reverse(l);
console.log("reversed:", l.join(" "));reversed: 50 40 30 20 10Time O(n) · Space O(1)
| Python | C++ | Java | JavaScript | |
|---|---|---|---|---|
| In place | l.reverse() | reverse(v.begin(), v.end()) | Collections.reverse(list) | arr.reverse() |
| New reversed copy | l[::-1], list(reversed(l)) | vector<int>(v.rbegin(), v.rend()) | copy, then reverse | arr.toReversed(), [...arr].reverse() |
Problem: remove duplicates from a sorted array
Because the array is sorted, all copies of a value sit next to each other, so a duplicate is simply an element equal to the one just before it. The naive version writes the distinct values into a second array — correct, but O(n) extra space:
def remove_duplicates_naive(arr):
temp = [arr[0]]
for i in range(1, len(arr)):
if arr[i] != temp[-1]: # differs from the last kept value → new distinct value
temp.append(arr[i])
return temp
res = remove_duplicates_naive([10, 20, 20, 30, 30, 30, 30])
print(*res)
print("size:", len(res))10 20 30
size: 3arr = 10 20 20 30 30 30 30 read i, compare with arr[res-1]
↑res=1
i=1: 20 ≠ 10 → arr[1]=20, res=2
i=2: 20 = 20 → skip
i=3: 30 ≠ 20 → arr[2]=30, res=3
i=4..6: 30 = 30 → skip
arr = 10 20 30 | 30 30 30 30 answer = first res = 3 slotsdef remove_duplicates(arr):
if not arr:
return 0
res = 1 # arr[0] is always kept
for i in range(1, len(arr)):
if arr[i] != arr[res - 1]:
arr[res] = arr[i] # write the next distinct value
res += 1
return res
arr = [10, 20, 20, 30, 30, 30, 30]
n = remove_duplicates(arr)
print(*arr[:n])
print("size:", n)#include <iostream>
#include <vector>
using namespace std;
int removeDuplicates(vector<int>& arr) {
if (arr.empty()) return 0;
int res = 1; // arr[0] is always kept
for (int i = 1; i < (int)arr.size(); i++) {
if (arr[i] != arr[res - 1])
arr[res++] = arr[i]; // write the next distinct value
}
return res; // library: unique(arr.begin(), arr.end()) - arr.begin()
}
int main() {
vector<int> arr = {10, 20, 20, 30, 30, 30, 30};
int n = removeDuplicates(arr);
for (int i = 0; i < n; i++) cout << (i ? " " : "") << arr[i];
cout << "\n";
cout << "size: " << n << "\n";
}public class Main {
static int removeDuplicates(int[] arr) {
if (arr.length == 0) return 0;
int res = 1; // arr[0] is always kept
for (int i = 1; i < arr.length; i++) {
if (arr[i] != arr[res - 1])
arr[res++] = arr[i]; // write the next distinct value
}
return res;
}
public static void main(String[] args) {
int[] arr = {10, 20, 20, 30, 30, 30, 30};
int n = removeDuplicates(arr);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) sb.append(i > 0 ? " " : "").append(arr[i]);
System.out.println(sb);
System.out.println("size: " + n);
}
}function removeDuplicates(arr) {
if (arr.length === 0) return 0;
let res = 1; // arr[0] is always kept
for (let i = 1; i < arr.length; i++) {
if (arr[i] !== arr[res - 1])
arr[res++] = arr[i]; // write the next distinct value
}
return res;
}
const arr = [10, 20, 20, 30, 30, 30, 30];
const n = removeDuplicates(arr);
console.log(arr.slice(0, n).join(" "));
console.log("size:", n);10 20 30
size: 3Time O(n) · Space O(1)
Problem: left rotate by one
A left (counter-clockwise) rotation moves the first element to the end and every other element one step to the left. Python can do it with slicing or pop/append:
l = [10, 20, 30, 40]
print(l[1:] + l[0:1]) # [20, 30, 40, 10] ← Method 1: new list from two slices
l = [10, 20, 30, 40]
l.append(l.pop(0)) # Method 2: pop the front (O(n) shift), push it at the back
print(l) # [20, 30, 40, 10]def rotate_by_one(l):
n = len(l)
if n == 0:
return
first = l[0] # save the element that falls off the front
for i in range(1, n):
l[i - 1] = l[i] # shift left
l[n - 1] = first # it re-enters at the back
l = [10, 20, 30, 40, 50]
rotate_by_one(l)
print("rotated:", *l)#include <iostream>
#include <vector>
using namespace std;
void rotateByOne(vector<int>& l) {
int n = l.size();
if (n == 0) return;
int first = l[0]; // save the element that falls off the front
for (int i = 1; i < n; i++) l[i - 1] = l[i]; // shift left
l[n - 1] = first; // it re-enters at the back
} // library: rotate(l.begin(), l.begin() + 1, l.end())
int main() {
vector<int> l = {10, 20, 30, 40, 50};
rotateByOne(l);
cout << "rotated:";
for (int x : l) cout << " " << x;
cout << "\n";
}public class Main {
static void rotateByOne(int[] l) {
int n = l.length;
if (n == 0) return;
int first = l[0]; // save the element that falls off the front
for (int i = 1; i < n; i++) l[i - 1] = l[i]; // shift left
l[n - 1] = first; // it re-enters at the back
}
public static void main(String[] args) {
int[] l = {10, 20, 30, 40, 50};
rotateByOne(l);
StringBuilder sb = new StringBuilder("rotated:");
for (int x : l) sb.append(' ').append(x);
System.out.println(sb);
}
}function rotateByOne(arr) {
const n = arr.length;
if (n === 0) return;
const first = arr[0]; // save the element that falls off the front
for (let i = 1; i < n; i++) arr[i - 1] = arr[i]; // shift left
arr[n - 1] = first; // it re-enters at the back
}
const l = [10, 20, 30, 40, 50];
rotateByOne(l);
console.log("rotated:", l.join(" "));rotated: 20 30 40 50 10Time O(n) · Space O(1)
Collecting input into a list: chicken or duck
A small competitive-programming exercise from the notes that shows the standard pattern of reading T test cases into a list first and processing them afterwards. A chicken has X legs, a duck Y legs; given Z legs in total, could they all be chickens (Z % X == 0), all ducks, either (Any) or neither (Null)?
import sys
data = sys.stdin.read().split() # read everything at once, split on whitespace
t = int(data[0])
cases = [] # collect every test case in a list first
pos = 1
for _ in range(t):
x, y, z = int(data[pos]), int(data[pos + 1]), int(data[pos + 2])
cases.append((x, y, z))
pos += 3
print("collected", len(cases), "cases")
for x, y, z in cases:
chickens = z % x == 0
ducks = z % y == 0
if chickens and ducks:
print("Any")
elif chickens:
print("Chicken")
elif ducks:
print("Duck")
else:
print("Null")#include <array>
#include <iostream>
#include <vector>
using namespace std;
int main() {
int t;
cin >> t;
vector<array<int, 3>> cases; // collect every test case in a list first
for (int i = 0; i < t; i++) {
int x, y, z;
cin >> x >> y >> z;
cases.push_back({x, y, z});
}
cout << "collected " << cases.size() << " cases\n";
for (auto& c : cases) {
bool chickens = c[2] % c[0] == 0;
bool ducks = c[2] % c[1] == 0;
if (chickens && ducks) cout << "Any\n";
else if (chickens) cout << "Chicken\n";
else if (ducks) cout << "Duck\n";
else cout << "Null\n";
}
}import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int t = in.nextInt();
List<int[]> cases = new ArrayList<>(); // collect every test case in a list first
for (int i = 0; i < t; i++)
cases.add(new int[]{in.nextInt(), in.nextInt(), in.nextInt()});
System.out.println("collected " + cases.size() + " cases");
for (int[] c : cases) {
boolean chickens = c[2] % c[0] == 0;
boolean ducks = c[2] % c[1] == 0;
if (chickens && ducks) System.out.println("Any");
else if (chickens) System.out.println("Chicken");
else if (ducks) System.out.println("Duck");
else System.out.println("Null");
}
}
}import { readFileSync } from "node:fs";
const data = readFileSync(0, "utf8").trim().split(/\s+/).map(Number); // fd 0 = stdin
const t = data[0];
const cases = []; // collect every test case in a list first
for (let i = 0; i < t; i++)
cases.push(data.slice(1 + 3 * i, 4 + 3 * i));
console.log("collected", cases.length, "cases");
for (const [x, y, z] of cases) {
const chickens = z % x === 0;
const ducks = z % y === 0;
if (chickens && ducks) console.log("Any");
else if (chickens) console.log("Chicken");
else if (ducks) console.log("Duck");
else console.log("Null");
}collected 4 cases
Any
Chicken
Duck
NullTime O(T) · Space O(T)
Interview Q&A
Why is appending to a dynamic array O(1) amortised and not O(1) worst case?
Array vs linked list — when would you pick each?
What is the difference between l.sort() and sorted(l)? Between l.reverse() and l[::-1]?
Why does [[0] * 3] * 3 behave strangely?
How do you find the second largest in one pass, and what about duplicates?
Removing duplicates: why does the in-place solution need sorted input?