Understanding Arrays in Java: Types & Usage
Understanding Arrays in Java: Types & Usage
Introduction
An array also referred to as a collection of elements, is the simplest and most widely used Data
Structure. Most of the Data Structures ([Link] and Queue) were derived using the Array
structure, which is why it is known as one of the central building blocks of Data Structures. These
Data Structures will be discussed later in the coming chapters. The purpose of an Array is to group
similar kinds of data for fast access.
Look at the figure below; we have made a simple array with four elements. Each item in the
collection is called a Data Element, and the number of data elements stored in an Array is known as
its size. You can see that each data element has a maximum of two neighbors, except the first and
last one.
Array Indexing
Each data element is assigned a numerical value called the index, which corresponds to the position
of that item in the array. It is important to note that the value of the index is non-negative and always
starts from zero. So the first element of an array will be stored at index 0 and the last one at index
size-1.
An index makes it possible to access the contents of the array directly. Otherwise, we would have to
traverse through the whole array to access a single element. That is the key feature that
differentiates Arrays from Linked lists (we will cover them in the next chapter).
Types Of Arrays
Arrays can store primitive data-type values (e.g., int, char, floats, boolean, byte, short, long, etc.),
non-primitive data-type values (e.g., Java Objects, etc.) or it can even hold references of other
arrays. That divides the arrays into two categories:
One-Dimensional Array
The basic syntax for declaring and initializing the one-dimensional array is given below:
Array Declaration
In the array declaration, reference of an array is created. To declare an array, you have to specify
the data type and name of the array.
class OneDArray{
public static void main(String args[]){
//Declaration Syntax
int myArray1[];
int[] myArray2;
}
}
The above declarations will tell the compiler that reference variables myArray1and myArray2 will
hold an array of type int. For now, no actual array exists. To link these reference variables with the
actual physical array, we have to create one using the new operator.
Array Initialization
Array initialization actually gives memory to the elements of an array. The basic syntax for initializing
an array is given below: arrayName = new type [size];
datatype[] arrayName = new datatype [size]; or datatype arrayName[] = new datatype [size];
Practice Problem
arrayName[index] = value;
The only drawback of using arrays is that we have to specify the size of the array during the time of
instantiation. That means the size remains fixed and can not be extended. If we want to add more
elements, we will have to create a new array, copy all the items from the old array to the new one,
and then insert the new element.
sidebar_position: 7
2D ARRAY
Introduction
The Java 2D arrays are arranged as arrays of arrays, i.e., each element of a 2D array is another
array. These are generally used if we want to store the data items in a table or matrix-like
structure. The representation of the elements is in rows and columns. Thus, we can get a total
number of elements in a multidimensional array by multiplying row size with column size.
Like 1D arrays, 2D arrays must have values of the same data type.
Declaration
Syntax -
Initialization
There are various ways to initialize a 2D array with values.
The traditional method is to assign values to each element. int [][] arr =new int [3]
[4]; //assigning 10 at Row 2 and Column 1 arr[2][1]=10;
We can also initialize it with the declaration itself. int[][] arr = { { 1, 2, 3, 4 }, { 5,
6, 7, 8 }, {9, 10, 11, 12} };
We can also initialize or assign the values using a loop. for (int i = 0; i < 3; i++)
{ for (int j = 0; j < 4; j++) { arr[i][j]=i+j; } }
Printing 2D array in tabular format
public class Main {
public static void main(String args[]) {
int[][] arr = { { 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{9, 10, 11, 12} };
Practice Problems
Sum of Elements in a matrix.
Diagonal Sum.
Multiply Matrices.
Transpose of Matrix.
Rotate by 90 degree.
In this problem, you have to implement the int [] removeEven(int[] arr) method, which removes
all the even elements from the array and returns back updated array.
Method Prototype
Input
An array with integers.
Output
An array with only odd integers.
Sample Input
arr = {1, 2, 4, 5, 10, 6, 3}
Sample Output
arr = {1, 5, 3}
SOLUTION
CODE:
class CheckRemoveEven {
//Create result array with the size equal to the number of odd
elements in arr
int[] result = new int[oddElements];
int result_index = 0;
return result;
} //end of removeEven
Next, we initialize an array with a size oddElements, and store all the odd numbers in it.
Time Complexity
Since the entire array has to be iterated over, this solution is in O(n).
Method Prototype
int[] mergeArrays(int[] arr1, int[] arr2) Here arr1 and arr2 are sorted already.
Sample Input
arr1 = {1, 3, 4, 5}
arr2 = {2, 6, 7, 8}
Sample Output
arr = {1, 2, 3, 4, 5, 6, 7, 8}
SOLUTION
CODE:
class checkMergeArray {
return resultantArray;
}
Explanation
In the solution above, we start by creating a new empty array of the size equal to the sum of sizes of
input arrays. Starting off from the index 0 individually compare the elements at corresponding
indexes of both arrays. Place the element with smaller value in the resultant array, and increment
the index of the array where you find the smallest element. Keep repeating this till you hit the end of
one array. Move the elements of the other array into the resultantArray as it is.
Time Complexity
The time complexity for this algorithm is O(n+m), where nn and mm are the sizes of arr1 and arr2,
respectively. This is because both arrays are iterated over once.
Input
The input is an array, a value, and the size of the array
Output
An array with two integers that add up to the value given
Sample input
arr = {1,21,3,14,5,60,7,6}; value = 81;
Sample output
arr = {21,60};
For example, in this illustration we are given 81 as the number value. When we traverse the whole
array, we find that 21 and 60 are the integers that add up to 81.
Code:
[Link]
import [Link];
class Helper {
static void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
public static int choosePivot(int left, int right) {
Random rand = new Random();
// Pick 3 random numbers within the range of the array
int i1 = left + ([Link](right - left + 1));
int i2 = left + ([Link](right - left + 1));
int i3 = left + ([Link](right - left + 1));
public static int[] findSum(int[] arr, int n) //Returns 2 elements of arr that sum to
the given value
{
//Helper sort function that uses the Quicksort Algorithm
[Link](arr, 0, [Link] - 1); //Sort the array in Ascending Order
if (sum < n)
Pointer1++; //if sum is less than given value => Move Pointer 1 to Right
else if (sum > n)
Pointer2--;
else {
result[0] = arr[Pointer1];
result[1] = arr[Pointer2];
return result; // containing 2 number
}
}
return arr;
}
int n = 9;
int[] arr1 = {2, 4, 5, 7, 8};
int[] arr2 = findSum(arr1, n);
int num1 = arr2[0];
int num2 = arr2[1];
if ((num1 + num2) != n)
[Link]("Results not found!");
else
[Link]("Sum of " + n + " found: " + num1 + " and " + num2);
}
}
While solution #1 is very intuitive, it is not very time efficient. A better way to solve this challenge is
by first sorting the array, then using two variables: one starting from the first index of the array and
the second starting from the last index of the array, (size-1size−1).
If the sum of the elements at these indices of the array is smaller than the given value, then we
increment Pointer1. If the sum is smaller, then we decrement Pointer2 from the end. If none of those
conditions are true, this means that the sum is equal to the given value. That being said, we will
append the elements at those indices in the array and return the result.
Time complexity
Since the sorting function we use in this code takes O(nlog(n)) and the algorithm itself takes O(n)
time, the overall time complexity of this algorithm is in O(nlog(n)).
int n = 9;
int[] arr1 = {2, 4, 5, 7, 8};
int[] arr2 = findSum(arr1, n);
int num1 = arr2[0];
int num2 = arr2[1];
if ((num1 + num2) != n)
[Link]("Results not found!");
else
[Link]("Sum of " + n + " found: " + num1 + " and " + num2);
}
}
Explanation
We solve this problem by using a HashMap called hmap.
We will run a for loop on the whole array. If the element arr[i] doesn’t exist in the hmap, we add n
- arr[i] to the hmap as shown in the line 14.
If any element of arr exists in the hmap, that means the difference of n and the number found (n -
arr[i]) are also present.
Therefore, an array of size 2 called result is created to store the pair that sums up to n.
If hmap contains any array element, result[] is updated, or else it is returned containing the default
value.
Time complexity
This code works in O(n), as the whole array is iterated over once.
Method Prototype
int findMinimum(int[] arr) Here arr1 and arr2 are sorted already.
Sample Input
arr = {9, 2, 3, 6}
Sample Output
2
SOLUTION
CODE:
public static int findMinimum(int[] arr) {
int minimum = arr[0];
//At every Index compare its value with minimum and if its less
//then make that index value new minimum value
for (int i = 1; i < [Link]; i++) {
Explanation
Start with the first element, which is 9 in this example, and save it in minimum as the smallest value.
Then, iterate over the rest of the array and compare the minimum to each element. If any element is
smaller than the minimum, then set the minimum to that element. By the end of the array, the
number stored in the minimum will be the smallest integer in the whole array.
Time Complexity
Since the entire list is iterated over once, this algorithm is in linear time, O(n).
Function Prototype:
int findFirstUnique(int[] arr)
Output:
The first unique element in the array.
Sample Input
arr = {9, 2, 3, 2, 6, 6}
Sample Output
9
Solution: Using a HashMap
class CheckFirstUnique {
public static int findFirstUnique(int[] arr) {
}
}
Explanation
Firstly, we store all the elements from the array into a HashMap. The element is stored as key and the
count of multiple occurrences is stored as value in the HashMap. Initially, the count is 0. But if the
same element is encountered again, the count is increased by 1 each time.
Afterward, we traverse the array again from the beginning and return the first element which has
count equal to 0 in the HashMap.
Time Complexity
The array is iterated multiple times but the complexity is still linear. The time complexity of this code
is O(n).
}
}
Time Complexity
The time complexity of this program is O(n).
Method Prototype
Sample Output
6
SOLUTION
CODE:
public static int findSecondMaximum(int[] arr) {
// Keep track of Maximum value, whenever the value at an array index is greater
// than current Maximum value then make that max value 2nd max value and
// make that index value maximum value
for (int i = 0; i < [Link]; i++) {
if (arr[i] > max) {
secondmax = max;
max = arr[i];
}
else if (arr[i] > secondmax && arr[i] != max) {
secondmax = arr[i];
}
}//end of for-loop
return secondmax;
}
Explanation
We initialize two variables max and secondmax to Integer. MIN_VALUE having value -2147483648,
which is the maximum integer negative value range. We then traverse the array, and if the current
element in the array is greater than the maximum value, then set secondmax to max and max to the
current element. If the current element is greater than the secondmax but less than max, then
update secondmax to store the value of the current element. Finally, return the value stored
in secondmax.
Time Complexity
This solution is in O(n) since the list is traversed once only.
Method Prototype
Sample Input
Sample Output
11
Kadane's Algorithm
Code:
Explanation
The basic idea of Kadane’s algorithm is to scan the entire array and at each position find the
maximum sum of the subarray ending there. This is achieved by keeping a currMax for the current
array index and a globalMax. The algorithm is as follows:
currMax = A[0]
globalMax = A[0]
for i = 1 -> size of A
if currMax is less than 0
then currMax = A[i]
otherwise
currMax = currMax + A[i]
if globalMax is less than currMax
then globalMax = currMax
Time Complexity
Example
Given the following array, if the search key is 47, binary search will return 3.
Explanation
Binary search is used to find the index of an element in a sorted array. If the element doesn’t exist,
that can be determined efficiently as well. The algorithm divides the input array by half at every step.
After every step, either we have found the index that we are looking for or half of the array can be
discarded. Hence, the solution can be calculated in O(log n) time.
Here’s how the algorithm works:
At every step, consider the array between low and high indices
Calculate the mid index.
If the element at the mid index is the key, return mid.
If the element at mid is greater than the key, then change the index high to mid - 1. The
index at low remains the same.
If the element at mid is less than the key, then change low to mid + 1. The index
at high remains the same.
When low is greater than high, the key doesn’t exist and -1 is returned.
Code
static int binSearch(int[] A, int key) {
int low = 0;
int high = [Link] -1;
while (low <= high) {
if (A[mid] == key) {
return mid;
}
Let’s try to find all maximums for a window size equal to 33 in the array given below:
-4 2 -5 3
Step1: For the first 3 elements in the window, max is 2.
-4 2 -5
Step2: Slide window one position to the right and max for window becomes 3.
2 -5 3
Solution Approach
The algorithm uses the deque data structure to find the maximum in a window. A deque is a double-
ended queue in which push and pop operations work in O(1) at both ends. It will act as our window.
At the start of the algorithm, we search for the maximum value in the first window. The first element’s
index is pushed to the front of the deque. If an element is smaller than the one at the back of the
queue, then the index of this element is pushed in and becomes the new back. If the current element
is larger, the back of the queue is popped repeatedly until we can find a higher value, and then we’ll
push the index of the current element in as the new back. As we can see, the deque stores elements
in decreasing order. The front of the deque contains the index for the maximum value in that
particular window. We will repeat the following steps each time our window moves to the right:
Remove the indices of all elements from the back of the deque, which are smaller than or
equal to the current element.
If the element no longer falls in the current window, remove the index of the element from the
front.
Push the current element index at the back of the window.
The index of the current maximum element is at the front.
Code:
public static ArrayDeque<Integer> findMaxSlidingWindow(int[] arr, int windowSize) {
if([Link] > 0) {
// Removing all the elements indexes which are not in the current window
while((![Link]()) && [Link]() <= i-windowSize)
[Link]();
0 0 0 1 10 20 59 63
Remember: We need to maintain the order of non-zero elements.
Solution Approach
We will keep two markers: read_index and write_index and point them to the end of the array.
Let’s take a look at an overview of the algorithm: While moving read_index towards the start of the
array:
If read_index points to 0, skip.
If read_index points to a non-zero value, write the value at read_index to write_index and
decrement write_index.
Assign zeros to all the values before the write_index and to the current position
of write_index as well.
Code:
static void moveZerosToLeft(int[] A) {
if ([Link] < 1) {
return;
}
while(readIndex >= 0) {
if(A[readIndex] != 0) {
A[writeIndex] = A[readIndex];
writeIndex--;
}
readIndex--;
}
while(writeIndex >= 0) {
A[writeIndex] = 0;
writeIndex--;
}
}
Runtime complexity
The runtime complexity if this solution is linear, O(n).
Memory complexity
The memory complexity of this solution is constant, O(1).
We need to maximize the single buy/sell profit. If we can’t make any profit, we’ll try to minimize the
loss. For the below examples, buy and sell prices for making a maximum profit are highlighted.
8 5 12 9 19
Solution Approach
The values in the array represent the cost of a stock each day. As we can buy and sell the stock
only once, we need to find the best buy and sell prices for which our profit is maximized (or loss is
minimized) over a given span of time.
A naive solution, with runtime complexity of O(n^2), is to find the maximum gain between each
element and its succeeding elements. There is a tricky linear solution to this problem that requires
maintaining current_buy_price (which is the smallest number seen so far), current_profit,
and global_profit as we iterate through the entire array of stock prices. At each iteration, we will
compare the current_profit with the global_profit and update the global_profit accordingly.
The basic algorithm is as follows:
current profit = INT_MIN
current buy = stock_prices[0]
global sell = stock_prices[1]
global profit = global sell - current buy
for i = 1 to stock_prices.length:
current profit = stock_prices[i] - current buy
if current profit is greater than global profit
then update global profit to current profit and update global sell to stock_prices[i]
if stock_prices[i] is less than current buy
then update current buy to stock_prices[i]
Code:
class Tuple<X, Y> {
public X x;
public Y y;
public Tuple(X x, Y y) {
this.x = x;
this.y = y;
}
}
return result;
}
Runtime complexity
The runtime complexity if this solution is linear, O(n).
Memory complexity
The memory complexity of this solution is constant, O(1).
Original Array
55 23 26 2
Sorted Array
2 23 25 26
Solution Approach
Here is an overview of how the quicksort algorithm works:
Select a pivot element from the array to divide the array into two parts based on the pivot.
We pick the first element as the pivot.
Reorder the array by comparing with the pivot element such that smaller values end up at
the left side, and larger values end up at the right side of the pivot.
Now, the pivot element is in its correct sorted position. Applying the above steps, we can
recursively sort the sublists on the right and left sides of the pivot.
Code:
static int partition(int[] arr, int low, int high) {
int pivotValue = arr[low];
int i = low;
int j = high;
while (i < j) {
while (i <= high && arr[i] <= pivotValue) i++;
while (arr[j] > pivotValue) j--;
if (i < j) {
// swap arr[i] and arr[j]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
arr[low] = arr[j];
arr[j] = pivotValue;
Runtime complexity
The runtime complexity of this solution is linearithmic, O(nlogn).
Memory complexity
The memory complexity of this solution is logarithmic, O(logn).
Maximum Sum Subarray of Size K
Description
Given an array of positive numbers and a positive number ‘k,’ find the maximum sum of any
contiguous subarray of size ‘k’.
Example 1
Input: [2, 1, 5, 1, 3, 2], k=3 Output: 9 Explanation: Subarray with maximum sum is [5, 1, 3].
Example 2
Input: [2, 3, 4, 1, 5], k=2 Output: 7 Explanation: Subarray with maximum sum is [3, 4].
Solution Approach
A basic brute force solution will be to calculate the sum of all ‘k’ sized subarrays of the given array to
find the subarray with the highest sum. We can start from every index of the given array and add the
next ‘k’ elements to find the subarray’s sum.
Code:
public static int findMaxSumSubArray(int k, int[] arr) {
int maxSum = 0, windowSum;
for (int i = 0; i <= [Link] - k; i++) {
windowSum = 0;
for (int j = i; j < i + k; j++) {
windowSum += arr[j];
}
maxSum = [Link](maxSum, windowSum);
}
return maxSum;
}
The above algorithm’s time complexity will be O(N*K)O(N∗K), where ‘N’ is the total number of
elements in the given array. Is it possible to find a better algorithm than this?
A better approach
If you observe closely, you will realize that to calculate the sum of a contiguous subarray, we can
utilize the sum of the previous subarray. For this, consider each subarray as a Sliding Window of
size ‘k.’ To calculate the sum of the next subarray, we need to slide the window ahead by one
element. So to slide the window forward and calculate the sum of the new position of the sliding
window, we need to do two things:
Subtract the element going out of the sliding window, i.e., subtract the first element of the
window.
Add the new element getting included in the sliding window, i.e., the element coming right
after the end of the window.
This approach will save us from re-calculating the sum of the overlapping part of the sliding
window. Here is what our algorithm will look like:
Code
public static int findMaxSumSubArray(int k, int[] arr) {
int windowSum = 0, maxSum = 0;
int windowStart = 0;
for (int windowEnd = 0; windowEnd < [Link]; windowEnd++) {
windowSum += arr[windowEnd]; // add the next element
// slide the window, we don't need to slide if we've not hit the required window
size of 'k'
if (windowEnd >= k - 1) {
maxSum = [Link](maxSum, windowSum);
windowSum -= arr[windowStart]; // subtract the element going out
windowStart++; // slide the window ahead
}
}
return maxSum;
}
Runtime complexity
The time complexity of the above algorithm will be O(N).
Memory complexity
The algorithm runs in constant space O(1).