Cracking DSA with Kotlin: Mastering Arrays — Part 01-A
Mastering Linear Data Structures & Algorithms
Cracking DSA with Kotlin: Mastering Arrays — Part 01-A

If you’re an Android or Kotlin developer, chances are you’ve built beautiful apps with Jetpack Compose, Coroutines, and modern libraries. But when it comes to solving real problems — optimizing performance, managing large datasets, or cracking interviews — Data Structures and Algorithms (DSA) become your secret weapon.
In this article and the upcoming series, we’ll explore DSA with Kotlin in a simple, practical way. We’ll start from the basics, look at core data structures, and see how to implement algorithms that every developer should know.
Why DSA Matters for Kotlin Developers
- Efficient Code: A wrong data structure can slow down your app significantly.
- Interview Prep: Top tech companies evaluate problem-solving, not just framework knowledge.
- Problem Solving: Writing optimized solutions makes you a stronger engineer.
Kotlin as a DSA Language
Kotlin is concise, expressive, and has many modern features that make it great for implementing algorithms:
- Immutable collections (listOf, mapOf)
- Mutable collections (mutableListOf, hashMapOf)
- Extension functions (e.g., writing List<Int>.prefixSum())
- Null-safety and smart casting
This lets us write cleaner and safer implementations compared to Java.
Essential Data Structures in Kotlin
Now that we know why DSA matters, let’s look at the building blocks. Data structures are just different ways of organizing and storing data so we can work with it efficiently. Kotlin makes this easy with its expressive syntax and modern collections API.
Arrays
An array is a collection of elements stored in contiguous memory locations. Think of it like a row of boxes, each holding a value, and every box has a fixed index.

Defining Arrays in Kotlin
// 1. Using arrayOf()
val numbers = arrayOf(1, 2, 3, 4, 5)
// 2. Using arrayOfNulls()
val nullArray = arrayOfNulls<String>(3) // [null, null, null]
// 3. Using constructor
val squares = Array(5) { i -> i * i } // [0, 1, 4, 9, 16]
// 4. Primitive arrays (more memory-efficient)
val intArray = intArrayOf(10, 20, 30)
val doubleArray = doubleArrayOf(2.5, 3.5, 4.5)
Let’s take an example;
Given an array nums of size n, return the majority element.
The majority element is the element that appears more than ⌊n / 2⌋ times. You may assume that the majority element always exists in the array.
Let’s build a solution to solve this problem
private fun majorityElement(numbers: IntArray): Int {
var count = 0
var number = -1
var result = -1
val matcher = (numbers.size) / 2
numbers.sortedArray().forEachIndexed { index, item ->
if (number == item) {
count++
if (count > matcher) {
result = number
}
} else {
number = item
count = 1
}
}
return result
}
Now Let’s see its time complexity
- We are sorting it first means O(NLogN) — assuming Kotlin used best sorting algorithm
- We are iterating over sorted array means O(N)
- Total Time Complexity = O(NLogN) + N => O(NLogN)
- Total Space Complexity = O(1)
Now as we see it’s not optimized nor memory efficient. Let’s think about a different solution
Let’s try to solve it using HashMap( We’ll cover this in upcoming articles)
typealias Element = Int
typealias Count = Int
private fun majorityElement(numbers: IntArray): Int {
val itemsMap = hashMapOf<Element, Count>()
numbers.forEach { item ->
if (itemsMap.contains(item)) {
var count = itemsMap[item] ?: 0
count += 1
itemsMap[item] = count
} else {
itemsMap[item] = 1
}
}
return itemsMap.maxBy { it.value }.key
}
Now Let’s see its time complexity
- We are iterating over the numbers array means O(N)
- HashMap operations = O(1)
- Finding Max means 0(N)
- Total Time Complexity = O(N) + O(1) + O(N) => O(N)
- Total Space Complexity = O(1)
Can we make this even more efficient? Absolutely — that’s where the Boyer–Moore Voting Algorithm comes in.
What is the Boyer–Moore Voting Algorithm?
The Boyer–Moore Voting Algorithm is a clever algorithm used to find the majority element in an array — i.e., the element that appears more than [ n/2 ]times (where n is the size of the array).
It was proposed by Robert S. Boyer and J Strother Moore in 1981.
The beauty of this algorithm is that it works in O(n) time and uses only O(1) space, making it extremely efficient.
How it works?
Imagine you are “voting” for elements:
- Candidate selection
- Start with no candidate and count = 0.
- For each element:
- If count == 0, pick the current element as the new candidate.
- If the element equals the candidate, increment count.
- Otherwise, decrement count.
This way, non-majority elements cancel each other out, leaving the majority element as the last standing candidate.
2. Verification (optional)
- If the problem guarantees a majority element, the candidate at the end is the answer.
- If not guaranteed, you should count again to confirm the candidate really appears more than n/2 times.
Let’s implement this
private fun mooreMajorityElement(numbers: IntArray): Int {
var candidate: Int = -1
var count = 0
val matcher = (numbers.size / 2)
numbers.forEach { item ->
if (count == 0) {
candidate = item
count = 1
} else {
if (candidate != item) {
count--
} else {
count++
}
}
}
// Validate frequency of candidate
var frequency = 0
numbers.forEach { item ->
if (item == candidate) {
frequency++
}
}
return if (frequency > matcher) candidate else -1
}
- Total Time Complexity = O(N) i.e single pass + optional verification
- Total Space Complexity = O(1) i.e. only a couple of variables
Let’s move on to another example. Suppose we want to calculate the Largest Sum Contiguous Subarray. How can we approach this?
Consider the array: [-5, 4, 6, -3, 4, 1].
How do we solve this? Well, the simplest way is to begin with a brute force approach.
private fun sumOfContiguousSubArray(intArray: IntArray): Int {
var maxSum = -1
for (index in 0..<intArray.size) {
var sum = 0
for (subIndex in index..<intArray.size) {
sum += intArray[subIndex]
if (sum > maxSum) {
maxSum = sum
}
}
}
return maxSum
}
- Total Time Complexity = O(N²)
- Total Space Complexity = O(1)
Is there a smarter way? Absolutely — enter Kadane’s Algorithm
Kadane’s Algorithm — Overview
Kadane’s Algorithm is a fast way to find the largest sum of a contiguous subarray in an array. Instead of checking all possible subarrays (which takes a lot of time), it solves the problem in just one pass through the array by keeping track of the current running sum and the maximum sum found so far.
How it works?
- Start with two variables:
- currentSum → the sum of the subarray ending at the current element.
- maxSum → the largest sum found so far.
- For each element in the array:
- Decide whether to add the element to the existing sum (currentSum + item)
- or start fresh from this element (item).
- Update currentSum with the better choice.
- Compare currentSum with maxSum and update maxSum if needed.
- At the end of the loop, maxSum will hold the answer.
Let’s build it now
private fun kadaneMaxSum(
intArray: IntArray
): Int {
var maxSum = -1
var sum = 0
for (item in intArray) {
sum += item
if (sum > maxSum) {
maxSum = sum
}
if (sum < 0) {
maxSum = -1
sum = 0
}
}
return maxSum
}
- Total Time Complexity = O(N) i.e. single for loop
- Total Space Complexity = O(1) i.e. only a couple of variables
Let’s look at another classic problem: Buy and Sell Stocks.
You are given an array prices, where prices[i] represents the price of a stock on the i-th day.
Your goal is to maximize profit by choosing one day to buy the stock and a later day to sell it.
Return the maximum profit you can achieve from this transaction.
If no profit is possible, return 0.
📌 Note: The buy must always happen before the sell
Stop here and think about the brute force approach first.
fun maxProfitOfStocks(prices: IntArray): Int {
var maxProfit = 0
for (buy in prices.indices) {
for (sell in buy + 1 until prices.size) {
val profit = prices[sell] - prices[buy]
if (profit > maxProfit) {
maxProfit = profit
}
}
}
return maxProfit
}
- Total Time Complexity = O(N²)
- Total Space Complexity = O(1)
Is there a smarter way? Absolutely — Let’s think about getting min and max value.
Here it the optimized solution
fun maxProfitOfStock(prices: IntArray): Int {
var buyAt = 0
var maxProfit = 0
for (index in prices.indices) {
if (prices[index] < prices[buyAt]) {
buyAt = index
}
val profit = prices[index] - prices[buyAt]
if (profit > maxProfit) {
maxProfit = profit
}
}
return maxProfit
}
// With less code
fun maxProfitOfStock(prices: IntArray): Int {
var maxProfit = 0
var minimum = prices.first()
prices.forEach { item ->
minimum = minimum.coerceAtMost(item)
val currentProfit = item - minimum
if (maxProfit < currentProfit) {
maxProfit = currentProfit
}
}
return maxProfit
}
- Total Time Complexity = **O(N)** — For Both
- Total Space Complexity = **O(1)** — For Both
Next, let’s dive into our new topic: Array Preprocessing
🔎 Understanding Array Preprocessing
Array preprocessing means doing some calculations beforehand on an array so that answering queries later becomes faster and easier.
Instead of solving a problem from scratch every time, we prepare the array in a smart way once, and then reuse that information whenever we need it.
For example:
- If we want to quickly find the sum of elements between two indices, we can create a prefix sum array.
- If we want to find the maximum element in a range, we can preprocess using extra structures (like segment trees or sparse tables).
So, preprocessing is like “doing homework in advance” — it may take a little time initially, but it saves a lot of time later when multiple queries come in.
Let’s take an example:
Suppose we want to know the minimum price up to each day (like in the stock problem we discussed earlier).
Instead of checking again and again, we can preprocess it once.
// Preprocess minimum value up to each index
fun buildMinArray(arr: IntArray): IntArray {
val minArray = IntArray(arr.size)
minArray[0] = arr[0]
for (i in 1 until arr.size) {
minArray[i] = minOf(minArray[i - 1], arr[i])
}
return minArray
}
fun main() {
val arr = intArrayOf(7, 1, 5, 3, 6, 4)
val minArray = buildMinArray(arr)
println(minArray.joinToString()) // [7, 1, 1, 1, 1, 1]
}
Wrapping Up Part 01-A
In this first part of our “Cracking DSA with Kotlin” series, we explored how arrays form the foundation of problem-solving and performance optimization. From simple array definitions to real-world challenges, we learned how to turn brute-force solutions into efficient algorithms through clear reasoning and Kotlin’s expressive syntax.
We covered:
- Finding the Majority Element using Sorting, HashMap, and the Boyer–Moore Voting Algorithm
- Solving the Maximum Subarray Sum problem efficiently using Kadane’s Algorithm
- Optimizing Stock Buy and Sell to achieve O(N) performance
- Using Array Preprocessing to speed up repeated queries
Each problem showed a step-by-step evolution — from brute-force to optimal — helping you reason about time and space complexity while keeping Kotlin code clean and expressive.
Coming Up Next
In the next part of this series (Part 01-B), we’ll continue exploring arrays — diving deeper into how preprocessing, patterns, and clever logic can help us solve more complex problems with less code.
We’ll build on what we learned here and uncover new tricks that make Kotlin an excellent language for mastering DSA.
Stay tuned — things are about to get even more interesting!
💡 Final Takeaway
“Mastering DSA isn’t about memorizing algorithms — it’s about learning to think efficiently.”
Keep experimenting with these examples, write them from scratch, and tweak them in Kotlin Playground or Android Studio.
Once you feel comfortable, jump into Part 01-B, where we’ll push your Kotlin problem-solving skills to the next level.
💬 Follow me on LinkedIn and GitHub for more updates, tips, and upcoming parts of this Kotlin DSA series!
메타데이터
- post_id
- e31162df8c36
- slug
- cracking-dsa-with-kotlin-linear-data-structures-part-01-a-e31162df8c36
- url
- https://medium.com/@syedovaiss/cracking-dsa-with-kotlin-linear-data-structures-part-01-a-e31162df8c36
- canonical_url
- https://medium.com/@syedovaiss/cracking-dsa-with-kotlin-linear-data-structures-part-01-a-e31162df8c36
- author_url
- https://medium.com/@syedovaiss
- status
- ok
- fetched_at
- 2026-06-24 23:31:39