Skip to content
  • List · DSA Basics

List

LearnViaNotes @learnvianotes· 39 min read

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.

Positive indices count from the front, negative ones (Python only) from the back: l[-1] is the last element
list   = [ 10,  20,  30,  40,  50 ]
index      0    1    2    3    4
-ve index -5   -4   -3   -2   -1

How 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:

  1. 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, Java ArrayList uses 1.5×).

  2. Copy

    Move all n existing elements into the new block — this is the O(n) step.

  3. Free the old block

    The list now points at the new memory and has spare capacity again.

Doubling: after an expensive copy there is room for as many cheap appends as elements copied
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

PythonC++JavaJavaScript
Typeliststd::vector<T>ArrayList<T>Array
Fixed-size cousinarray.array, NumPyT arr[n], std::arrayint[] arrtyped arrays
Mixed element typesyes (references)nono (one T)yes
Negative indicesyesnonoarr.at(-1) only

Operations and their complexities

OperationPythonTimeWhy
Read / write by indexl[i], l[i] = xO(1)address arithmetic
Append at endl.append(x)O(1) amortisedspare capacity; rare copy
Pop from endl.pop()O(1)nothing to shift
Insert at indexl.insert(i, x)O(n)shifts n - i elements right
Delete by index / valuedel l[i], l.pop(i), l.remove(x)O(n)shifts elements left (remove also searches)
Searchx in l, l.index(x), l.count(x)O(n)linear scan
Lengthlen(l)O(1)stored with the list
Min / max / summin(l), max(l), sum(l)O(n)one pass
Reversel.reverse(), l[::-1]O(n)swap from both ends
Sortl.sort(), sorted(l)O(n log n)Timsort
Slicel[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).

Python
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))
C++
#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";
}
Java
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);
    }
}
JavaScript
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);
Output
first: 10
fourth: 40
last: 50
third from end: 30
length: 5

Adding 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.

Python
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
C++
#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";
}
Java
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));
    }
}
JavaScript
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));
Output
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: 6

Removing 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).

Python
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)
C++
#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);
}
Java
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));
    }
}
JavaScript
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(" "));
Output
10 20 40 50 60 70
popped: 70
10 20 40 50 60
popped at 2: 40
10 20 50 60
10 50 60
60

min, max, sum, reverse and sort

Python
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)
C++
#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);
}
Java
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));
    }
}
JavaScript
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(" "));
Output
min: 10
max: 50
sum: 150
reversed: 30 20 50 10 40
sorted: 10 20 30 40 50
sorted descending: 50 40 30 20 10

Two language-specific traps worth seeing once:

Python
l = [10, 'twenty', 30]        # a list may mix types …
print(sum(l))                 # … but sum(), sort(), min() need comparable/addable elements
Output
TypeError: unsupported operand type(s) for +: 'int' and 'str'
JavaScript
console.log([10, 9, 1].sort().join(" "));             // compares as strings: "10" < "9"
console.log([10, 9, 1].sort((a, b) => a - b).join(" "));
Output
1 10 9
1 9 10

Slicing

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.

SliceMeaningResult 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]
Python
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 error

A 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.

Python
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
IdiomPythonC++JavaJavaScript
Sub-range copyl[a:b]vector<int>(v.begin()+a, v.begin()+b)new ArrayList<>(l.subList(a, b))arr.slice(a, b)
Copyl[:], l.copy()vector<int> c = v;new ArrayList<>(l)[...arr], arr.slice()
Last kl[-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.

Python
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:

Python
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.

Python
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:

Python
squares = [x * x for x in range(11) if x % 2 == 0]
print(*squares)
C++
#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";
}
Java
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);
    }
}
JavaScript
const squares = Array.from({ length: 11 }, (_, i) => i)
    .filter(x => x % 2 === 0)
    .map(x => x * x);
console.log(squares.join(" "));
Output
0 4 16 36 64 100

Problem: average (mean) of a list

Python
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}")
C++
#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";
}
Java
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})));
    }
}
JavaScript
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));
Output
25.0
3.7

Time O(n) · Space O(1)

Problem: separate even and odd numbers

Python
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)
C++
#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);
}
Java
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));
    }
}
JavaScript
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(" "));
Output
even: 10 20 40
odd: 11 15 19

Time O(n) · Space O(n)

Problem: elements smaller than x

Python
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))
C++
#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";
}
Java
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);
    }
}
JavaScript
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(" "));
Output
smaller than 21: 10 20 11 15 19

Time 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²):

Python
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]))
Output
20
Python
def 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([]))
C++
#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({}));
}
Java
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[]{}));
    }
}
JavaScript
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([]));
Output
largest: 20
largest: None

Time 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:

Python
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]))
Output
10
None
Python
def 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))
C++
#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";
    }
}
Java
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()));
        }
    }
}
JavaScript
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));
Output
second largest: 10
second largest: None
second largest: 20

Time 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:

Python
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")
Output
Yes
No
Python
def 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")
C++
#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";
    }
}
Java
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"));
        }
    }
}
JavaScript
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");
Output
10 20 30: Yes
29 24 32: No
7 7 7: Yes
: Yes

Time 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:

Python
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]
Two pointers meet in the middle after ⌊n/2⌋ swaps
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 → stop
Python
def 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)
C++
#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";
}
Java
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);
    }
}
JavaScript
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(" "));
Output
reversed: 50 40 30 20 10

Time O(n) · Space O(1)

PythonC++JavaJavaScript
In placel.reverse()reverse(v.begin(), v.end())Collections.reverse(list)arr.reverse()
New reversed copyl[::-1], list(reversed(l))vector<int>(v.rbegin(), v.rend())copy, then reversearr.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:

Python
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))
Output
10 20 30
size: 3
Trace of the in-place version
arr = 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 slots
Python
def 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)
C++
#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";
}
Java
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);
    }
}
JavaScript
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);
Output
10 20 30
size: 3

Time 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:

Python
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]
Python
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)
C++
#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";
}
Java
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);
    }
}
JavaScript
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(" "));
Output
rotated: 20 30 40 50 10

Time 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)?

Python
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")
C++
#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";
    }
}
Java
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");
        }
    }
}
JavaScript
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");
}
Output
collected 4 cases
Any
Chicken
Duck
Null

Time 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?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report