Big-O for Collections - Java
- What is the worst-case time complexity of the code? Answer: , because each node is visited once and iterator removal unlinks it in constant time When analyzing iterator-based loops over linked lists, the key question is: what does each operation actually cost at the data structure level? Java's
LinkedListis a doubly-linked list, meaning every node holds a direct pointer to both its predecessor and successor. When you use anIterator, the iterator tracks its current position internally — it already holds a reference to the node it just visited. Callingit.remove()uses that stored reference to unlink the node directly: just rewire two pointers. No traversal is needed. This makes each removal , and since thewhileloop visits each of the nodes exactly once, the total complexity is , making A correct. B is wrong because linked lists have no "balanced structure" to maintain — that concept belongs to trees like AVL or Red-Black trees. There is no rebalancing step anywhere in aLinkedListoperation. C describes the cost oflist.remove(Object o)— the list's remove method — which does scan from the head to find the target element. But the code usesit.remove(), notlist.remove(). The iterator already knows where it is, so no scan occurs. This is a critical distinction. D actually describes the correct reasoning for whylist.remove(value)would be slow — it would scan from the head for each element, giving — but the code in the question doesn't call that method, so D does not apply here. Study tip: Always distinguishiterator.remove()(, uses stored position) fromcollection.remove(value)(, searches from the beginning). This difference is a frequent exam trap. - What is the worst-case time complexity of the code? Answer: , because each removal shifts the remaining elements toward index zero When analyzing time complexity for list operations, you need to consider not just how many times the loop runs, but how much work each individual operation costs. In Java,
ArrayListis backed by a dynamic array. When you callremove(0), you're removing the element at index zero — which sounds cheap, but it forces every remaining element to shift one position to the left to fill the gap. If the list has elements remaining, that removal costs time. So the total cost across all iterations is: This makes D the correct answer. A is wrong because implies a halving strategy, like binary search. The list does shrink each iteration, but that alone doesn't create logarithmic behavior — the shrinking actually reduces the shifting cost per step but doesn't eliminate the cumulative quadratic total. B is the most tempting trap. Yes, the loop runs exactly iterations, and if you only count iterations, seems right. But this ignores the internal cost of eachremove(0)call, which is not constant — it's where is the current size. C is a misapplication of reasoning, which applies to efficient sorting algorithms. There's no halving of a search space here. Key tip: Whenever you seeremove(0)oradd(0)on anArrayList, immediately think per call — and multiply by the number of calls to get your total complexity. - What is the worst-case time complexity, and which change would improve it asymptotically? Answer: ; traverse the list with an iterator or enhanced
forloop When analyzing time complexity in Java, always consider the underlying data structure — not just how the code looks on the surface. ALinkedListstores elements as nodes connected by pointers, meaning there is no direct index-based access. To retrieve the element at positioni, Java must traverse the list from the head, one node at a time, taking time perget(i)call. This makes the loop catastrophically expensive. For each of the iterations,list.get(i)takes up to time, giving a total of overall. The fix is straightforward: use an iterator or enhanced for loop (for (String s : list)), which advances a pointer sequentially without restarting from the head each time — reducing complexity to . This confirms C is correct. A is wrong on two counts: the complexity is not to begin with, and cachinglist.size()is a micro-optimization that doesn't change asymptotic behavior at all —size()on aLinkedListis already . B is wrong because binary search requires random access (like anArrayList) and a sorted structure. Applying it to aLinkedListwould actually make things worse, not better, and the complexity claim of is fabricated here. D is wrong because has no basis — copying a retrieved element is and doesn't add another nested loop. Study tip: Whenever you see index-basedget()on aLinkedList, immediately think . Prefer iterators for sequential traversal of linked structures. - What is the overall time complexity of processing the list and all queries? Answer: , because sorting and the collection of searches have that order When a question combines multiple algorithmic steps, you need to analyze each step's complexity separately and then determine which term dominates the total. Here, two operations happen: one sort, then binary searches.
Collections.sort()uses a modified merge sort, running in . EachCollections.binarySearch()call runs in , and since you run it times, the searches collectively cost . Adding both phases gives , making B correct. A is wrong because it misreads "performed once" as meaning cheap. Sorting is indeed called once, but "once" doesn't mean — sorting elements always costs at least in the comparison model. Additionally, the searches are not performed just once; there are of them. C confuses binary search with linear search. An unsorted list would require per query, giving total — but after sorting, each binary search only needs , not . D isolates a single binary search's cost and ignores everything else. describes one query; you still have the sort and remaining queries to account for. As a strategy, whenever you see a multi-step algorithm, list each phase and its cost, multiply repeated operations by how many times they run, then sum and drop lower-order terms. This "phase-by-phase" breakdown prevents you from accidentally analyzing only one piece. - What is the worst-case time complexity of the code? Answer: , because a linear search is repeated logarithmically many times When analyzing nested complexity, you need to evaluate how many times the outer loop runs and what each iteration costs — then multiply them together. The outer loop runs while
step < n, doubling each time: 1, 2, 4, 8, … This produces iterations. However,list.contains(target)on anArrayListperforms a full linear scan of all elements every single time it's called — it has no knowledge of prior searches. Sincetargetis guaranteed not to be in the list,breaknever fires, meaning every iteration completes its full scan. The total work is therefore , making C correct. A is tempting because it correctly identifies that the loop runs times — but it ignores the cost inside the loop. Doubling the step controls iteration count, not the cost per iteration. B incorrectly treatscontainsas if its work accumulates only once across the entire loop. In reality, each call tocontainsindependently scans the full list; there's no shared state between calls. D would apply if either the loop ran times orcontainsitself were called times. The loop is logarithmic, not linear, so overstates the complexity. Study tip: Whenever you see a method call inside a loop, always look up that method's time complexity separately and multiply — don't assume it's just because it looks like a single line. - What is the total worst-case time complexity of both phases combined? Answer: , because both insertion and lookup are logarithmic per value When analyzing time complexity for data structures, always break the problem into phases and add them together — then simplify using Big-O rules. A
TreeSetin Java is backed by a red-black tree, a self-balancing binary search tree. For a balanced tree with elements, both insertion and lookup (contains) run in per operation, because the height of the tree is bounded by . Since you're inserting values, the first phase costs . The second phase — callingcontainsonce per original value — also performs lookups at each, giving another . Combined: , making B correct. A is wrong because it ignores the logarithmic cost per operation. Saying operations "occur times" doesn't tell you how expensive each one is — operations at each is not . C is wrong because it confuses aTreeSetwith a linear scan. A balanced tree doesn't search through all elements on each lookup — that per call behavior describes an unsorted list, which would yield total. The tree structure eliminates that. D is wrong because describes the cost of a single operation, not the total cost across all calls. A useful habit: always multiply the per-operation cost by the number of operations before summing phases. Don't confuse one operation's complexity with the whole program's complexity. - Which statement best describes the time complexity of these append operations? Answer: Some appends are , but all appends together take amortized When analyzing the time complexity of repeated
ArrayListoperations, you need to distinguish between the cost of a single operation and the total cost across all operations — this is where amortized analysis comes in.ArrayListstarts with a small default capacity (10 in Java). Eachadd(value)is when space is available. But when the array is full, Java allocates a new array roughly 1.5× larger and copies all existing elements — an resize. Here's the key insight: resizes happen exponentially less often as the list grows. If you count all copy operations across all appends, the total work is approximately , forming a geometric series that sums to . Dividing by operations gives an amortized per append — making B the correct answer. A is wrong because not every append is worst-case — resize operations genuinely cost for that single call. A is confusing "amortized " with "guaranteed ." C is wrong becauseArrayListhas no behavior anywhere in its append logic — that's more characteristic of tree-based structures likeTreeMap. D is the most tempting trap: yes, some appends are , but it doesn't follow that the total is . That would require -cost operations happening times, but resizes occur only times. Study tip: Whenever a question mentions repeated operations with occasional expensive steps, think amortized analysis — the total cost spread across all operations is often much better than the worst-case single-operation cost suggests. - Which pair gives the expected and pathological worst-case time complexities of the entire loop? Answer: Expected and pathological worst case When analyzing loop complexity with hash maps, you need to think about two separate scenarios: the expected case (assuming good hash distribution) and the worst case (pathological collisions that degrade every operation). In this loop, you iterate over keys, and for each key you call
containsKeyand potentiallyput— both are hash-table operations. Under normal conditions, each of these runs in expected time. Since you're doing constant work per iteration across keys, the total expected complexity is . Under pathological collisions (e.g., all keys hash to the same bucket, forcing a linear scan of that bucket), each operation can degrade to — meaning the single operation at step scans up to elements. Summing across all iterations gives . This confirms A is correct: expected , pathological worst case . B is wrong because it claims expected — that's characteristic of tree-based structures likeTreeMap, notHashMap. There's no logarithmic factor in hash-table expected lookups. C is wrong because it claims the worst case is also , ignoring the quadratic blowup when collisions force linear probing or chaining across all operations. D is wrong on both counts — for the whole loop would mean sub-linear work, which is impossible when you must visit all keys. A useful rule of thumb: whenever you see a loop over elements calling hash-map operations, the expected total is , but worst-case degrades to due to the multiplicative effect of per-operation linear time. - What is the worst-case time complexity of the construction and removal phases combined? Answer: , because construction is and removals each cost When analyzing data structure operations, always separate the construction cost from the operational cost — they often differ in ways that surprise people. Java's
PriorityQueueis backed by a binary min-heap. When you construct it from an existing collection using the collection constructor, Java uses the heapify algorithm, which runs in time. This is a classic result: rather than inserting elements one by one (which would cost ), heapify works bottom-up and achieves linear time. Then, eachpoll()call removes the minimum element and restores the heap property via sift-down, costing . Doing this times gives for the removal phase. Combined: , making D the correct answer. A is tempting but wrong — while construction alone is , it ignores the cost of poll operations. You can't discount the removal phase. B arrives at the right total complexity but for the wrong reason. It claims construction is (as if inserting one-by-one), which misrepresents how the collection constructor works. The distinction matters conceptually even if the final big-O looks similar. C describes a naive linear scan approach, which is not how a heap works. The heap's structure guarantees removal, not . As a study tip: remember that **heap construction from a collection = , but heap sort (repeated removal) = **. These two facts together explain most PriorityQueue complexity questions. - What is the worst-case time complexity of this code? Answer: , because removing the last element requires no shifting of later elements When analyzing time complexity of loops that modify a data structure, you need to think about two things: how many iterations occur, and what each iteration costs. Here, the loop runs exactly times — once per element. The critical question is: what does each
list.remove(list.size() - 1)call cost? In anArrayList, removal works by nullifying the slot and decrementing the size counter. When you remove the last element specifically, there are no elements to the right that need to be shifted. This makes each removal . Multiply that by iterations, and your total complexity is , confirming that B is correct. A is wrong because there's no logarithmic behavior here. The valid index range shrinking doesn't require any binary search or divide-and-conquer logic —list.size() - 1is a direct calculation, always . C is wrong for the same reason as A. Finding the last index is not logarithmic; it's a single arithmetic operation on a stored size field. D describes what happens when you remove from the middle or front of anArrayList— every element after the removed one must shift left, giving per removal and overall. But this code always removes from the tail, so no shifting ever occurs. Study tip: Always distinguish where you're removing from in anArrayList. Removing from the end is ; removing from anywhere else is due to shifting. This distinction is a common trap on Java complexity questions.