# Learning DSA With Numbers

*Counting Digits, Reversing Numbers, Palindromes, and Perfect Numbers*

Before arrays, before recursion, before graphs… most problems quietly begin with **numbers**.

Digits. Place values. Division. Remainders.

At first, these problems look simple. But once you solve them properly, you start thinking like an algorithm designer.

## 1\. Counting the Number of Digits in a Number

Let’s start with a basic question.

```java
Input: 5678  
Output: 4
```

### Approach 1: Repeated Division (Most Intuitive)

In base 10, dividing a number by 10 removes the last digit.

```java
5678 / 10 → 567
567  / 10 → 56
56   / 10 → 5
5    / 10 → 0
```

Each division removes one digit.  
So the number of divisions until the number becomes zero equals the digit count.

```java
public static int countDigitsDiv(int n) {
    if (n == 0) return 1;

    int count = 0;
    n = Math.abs(n);

    while (n != 0) {
        count++;
        n /= 10;
    }
    return count;
}
```

**Time Complexity:** O(n) where n is number of digits  
**Space Complexity:** O(1)

### Approach 2: Using Logarithms (Mathematical Insight)

Logarithms answer one question very well:

> How many times can I divide by 10 before reaching 1?

That’s exactly what digit count is.

```java
digits = ⌊log10(n)⌋ + 1
```

```java
public static int countDigitsLog(int n) {
    if (n == 0) return 1;
    return (int) Math.floor(Math.log10(Math.abs(n))) + 1;
}
```

**Time Complexity:** O(1)  
**Space Complexity:** O(1)

This method is fast and elegant, but it works only when you clearly understand its limitations (zero and negatives).

### Approach 3: Convert to String (Readable, Not Optimal)

```java
public static int countDigitsString(int n) {
    return String.valueOf(Math.abs(n)).length();
}
```

This is simple and readable, but it uses extra space.

## 2\. Reversing a Number

```java
Input: 5467  
Output: 7645
```

This problem teaches **place value manipulation**.

### Approach 1: Mathematical Reverse (Recommended)

Each step:

* Extract last digit using `% 10`
    
* Shift previous digits left using `* 10`
    
* Append the extracted digit
    

```java
public static int reverseMath(int n) {
    int sign = n < 0 ? -1 : 1;
    n = Math.abs(n);

    int reversed = 0;
    while (n != 0) {
        reversed = reversed * 10 + (n % 10);
        n /= 10;
    }
    return reversed * sign;
}
```

**Time Complexity:** O(n)  
**Space Complexity:** O(1)

### Approach 2: String Reversal

```java
public static int reverseString(int n) {
    String s = new StringBuilder(String.valueOf(Math.abs(n)))
                    .reverse()
                    .toString();
    return Integer.parseInt(s) * (n < 0 ? -1 : 1);
}
```

Readable, but uses extra space and conversions.

## 3\. Checking if a Number Is a Palindrome

A palindrome reads the same forward and backward.

```java
121  → true  
5467 → false
```

### Approach 1: Reverse and Compare

```java
public static boolean isPalindromeReverse(int n) {
    return n == reverseMath(n);
}
```

Simple, but reversing the whole number may overflow for very large inputs.

### Approach 2: Two-Pointer Method (Conceptually Clean)

The two-pointer technique compares symmetric elements from both ends and moves inward.

For numbers, we apply it by converting the number to a string.

```java
public static boolean isPalindromeTwoPointer(int n) {
    if (n < 0) return false;

    String s = String.valueOf(n);
    int left = 0;
    int right = s.length() - 1;

    while (left < right) {
        if (s.charAt(left) != s.charAt(right)) {
            return false;
        }
        left++;
        right--;
    }
    return true;
}
```

**Time Complexity:** O(n)  
**Space Complexity:** O(n)

### Approach 3: Reverse Half the Number (Optimized)

Instead of reversing the full number, reverse only half and compare.

```java
public static boolean isPalindromeHalf(int x) {
    if (x < 0 || (x % 10 == 0 && x != 0)) return false;

    int reversedHalf = 0;
    while (x > reversedHalf) {
        reversedHalf = reversedHalf * 10 + x % 10;
        x /= 10;
    }
    return x == reversedHalf || x == reversedHalf / 10;
}
```

This avoids overflow and is the most optimized numeric solution.

## 4\. Checking if a Number Is Perfect

A **perfect number** equals the sum of its positive divisors excluding itself.

```java
28 → 1 + 2 + 4 + 7 + 14 = 28
```

### Key Insight: Divisors Come in Pairs

If `i` divides `n`, then `n / i` also divides `n`.

> To check divisor of n we do not check all the numbers till n but we do till sqrt(n) as after this the divisors repeat itself.
> 
> example:

```java
36:
divisors:
1*36
2*18
3*12
4*9
6*6 <------- sqrt(36) ----> numbers start to repeat itself below this
9*4
12*3
18*2
36*1
```

### Efficient Solution

```java
public static boolean isPerfect(int num) {
    if (num <= 1) return false;

    int sum = 1;
    for (int i = 2; i * i <= num; i++) {
        if (num % i == 0) {
            sum += i;
            if (i * i != num) {
                sum += num / i;
            }
        }
    }
    return sum == num;
}
```

**Time Complexity:** O(√n)  
**Space Complexity:** O(1)

## What These Problems Taught Me

Across all these questions, a few patterns repeat:

* `%` and `/` are the backbone of numeric DSA.
    
* Logarithms help when division is involved.
    

Learning DSA is less about memorizing solutions and more about **seeing patterns early**.

Numbers are usually the first place where that mindset begins.
