Topics › Basics › Things to Know in C++/Java/Python or any language
While loops
Java source ↗ Edit this article ↗
Jump to code:
What this covers
A while loop repeats as long as a condition holds, and it is the right tool when you cannot say in advance how many passes are needed: peel digits off a number until none are left, run Euclid’s algorithm until the remainder is zero, move two pointers until they cross. This article covers while, do-while, the while (true) loop with an exit in the middle, and the habit that keeps all of them from running forever: every pass must make measurable progress toward making the condition false. Each form is shown on a small task that comes back repeatedly on the sheets, especially in the basic maths and two-pointer sections.
while (condition) body tests the condition first and runs the body only while it is true. do body while (condition); runs the body first and tests afterwards, so the body executes at least once.
Peeling digits
The canonical while loop processes a number one digit at a time. n % 10 is the last digit and n / 10 drops it; the loop ends when n reaches 0, and nobody needs to know how many digits there were.
static long reverseNumber(long n) {
long rev = 0;
while (n != 0) {
rev = rev * 10 + n % 10;
n /= 10;
}
return rev;
}
The condition is n != 0 rather than n > 0 so that negative input works: for -45, n % 10 is -5, then -4, and rev becomes -54 with no special handling. The return type is long because reversing 1534236469, a valid int, gives 9646324351, which is not.
do-while: run at least once
How many digits does 0 have? One. A while (n > 0) loop would say zero, because the body never runs. do-while performs the first pass unconditionally, which is exactly the semantics the problem needs.
static int countDigits(int n) {
long m = Math.abs((long) n);
int count = 0;
do {
count++;
m /= 10;
} while (m > 0);
return count;
}
The cast to long before Math.abs handles Integer.MIN_VALUE, whose absolute value does not fit in an int; without it, Math.abs returns the same negative number and the loop stops after one pass with a wrong count.
Euclid’s algorithm
The greatest common divisor satisfies gcd(a, b) = gcd(b, a mod b), and the second argument shrinks every step until it hits 0, at which point the first argument is the answer. The number of steps depends on the inputs in a way that only a while loop expresses naturally.
static int gcd(int a, int b) {
while (b != 0) {
int t = a % b;
a = b;
b = t;
}
return a;
}
gcd(0, 9) returns 9 after one pass, gcd(9, 0) returns 9 after zero passes. The loop runs O(log(min(a, b))) times, because the remainder at least halves every two iterations.
Base conversion
Dividing by 2 repeatedly yields the binary digits from least significant to most. Each new bit belongs in front of the ones found so far, so it is inserted at position 0. The special case for 0 exists because the loop would otherwise produce an empty string.
static String toBinary(int n) {
if (n == 0) return "0";
StringBuilder sb = new StringBuilder();
while (n > 0) {
sb.insert(0, n % 2);
n /= 2;
}
return sb.toString();
}
Integer.toBinaryString does the same job in one call, and the test compares the two; writing it by hand once is how you learn what the library does.
When nobody knows the trip count
The Collatz rule, halve if even, triple and add one if odd, reaches 1 for every starting value anyone has tried, but there is no formula for how many steps it takes. A for loop has nothing to count to; while simply keeps going until the condition is met.
static int collatzSteps(long n) {
int steps = 0;
while (n != 1) {
n = (n % 2 == 0) ? n / 2 : 3 * n + 1;
steps++;
}
return steps;
}
6 takes 8 steps, 27 takes 111. The parameter is a long because intermediate values for 27 climb to 9232, and for larger starts they can exceed int.
Searching upward with a guard against overflow
To find the integer square root, increase r while (r + 1)^2 still fits under n. The product is formed in long: for n near Integer.MAX_VALUE, 46341 * 46341 overflows an int and would become negative, making the condition true forever.
static int floorSqrt(int n) {
int r = 0;
while ((long) (r + 1) * (r + 1) <= n) r++;
return r;
}
This runs O(sqrt(n)) times, which is fine here and becomes binary search in the later “square root” problem.
while (true) with an exit in the middle
Sometimes the natural place to test for termination is after some work has been done, not before. Writing while (true) and returning (or breaking) from inside keeps the code honest about that shape instead of duplicating the work before the loop.
static int firstPowerOfTwoAbove(int n) {
int p = 1;
while (true) {
if (p > n) return p;
p *= 2;
}
}
No statement follows the loop, and the compiler accepts the missing return because it knows a while (true) without break can only be left through the return inside. Every such loop must contain a reachable exit, and you should be able to say in one sentence why it is eventually reached: here, p doubles each pass and will exceed any n.
Two indices moving at different speeds
Merging two sorted arrays is the template for every two-pointer algorithm. Which index advances depends on the data, so the loop cannot be a for over either array alone. The condition keeps both indices in range; two short loops afterwards copy whatever one side has left.
static int[] mergeSorted(int[] a, int[] b) {
int[] out = new int[a.length + b.length];
int i = 0, j = 0, k = 0;
while (i < a.length && j < b.length) {
if (a[i] <= b[j]) out[k++] = a[i++];
else out[k++] = b[j++];
}
while (i < a.length) out[k++] = a[i++];
while (j < b.length) out[k++] = b[j++];
return out;
}
Every pass increments exactly one of i and j, so the total work is O(n + m). out[k++] = a[i++] stores at the current k and i, then increments both; the post-increment form is idiomatic here but easy to misread, so expand it mentally once.
Edge cases and pitfalls
- Infinite loops. Every pass must bring the condition closer to false. If a branch of the body can run without changing anything the condition depends on, the loop may never end.
- Zero passes versus one pass.
whilecan run zero times;do-whilealways runs once. Pick the one that matches the empty or zero case (countDigits(0)is the standard example). - Overflow inside the condition.
(r + 1) * (r + 1) <= noverflows for larger; compute inlong. - Negative inputs.
n % 10is negative for negativen. Decide whether to take the absolute value first or, as inreverseNumber, let the signs flow through. Math.abs(Integer.MIN_VALUE). Still negative. Widen tolongfirst.- Forgetting the leftovers. In two-pointer merges, after the main loop one side may still have elements; copy them.
- Updating before using. In
collatzStepsthe step counter is incremented after the transformation; swapping the order changes the answer by one.
Related problems
- For loops is the loop form for a known number of passes.
- Count all Digits of a Number is
countDigitsas a full problem with alternative approaches. - Reverse a number extends
reverseNumberand discusses overflow in detail. - GCD of Two Numbers compares Euclid’s loop with brute force.
- Functions (Pass by Reference and Value) explains why
gcdcan overwrite its parameters without affecting the caller.