DSA_Chapter1
DSA_Chapter1
The code above initializes an array arr with values {1, 2, 3, 4, 5} and an integer variable index with the
value 2. It then retrieves the element at the specified index from the array and assigns it to the variable
element. Finally, it prints the index and the element using std::cout. The time complexity of this code is
O(1) because it performs a constant number of operations, regardless of the size of the array.
If you edit the code to increase the number of elements in the array arr, the time complexity of the
code will NOT change.
Even though the array size is now 100, the code still performs the same number of operations as
before and takes the same constant amount of time to access and print an element.
• O(log n) - Logarithmic time: The runtime of the algorithm increases logarithmically with the input size.
Example: binary search in a sorted array.
• O(n) - Linear time: The runtime of the algorithm increases linearly with the input size.
Example:
The code above prints the elements of an array. It defines a function called printArray that takes an
array arr and its size as parameters. The function uses a for loop to iterate over the elements of the
array and prints each element using the std::cout statement. The main function initializes an array arr
with values 1, 2, 3, 4, and 5, and then calls the printArray function to print the elements of the array.
Finally, the main function returns 0, indicating successful execution. The time complexity of this code is
O(n), where n is the size of the array, because the printArray function iterates over each element of the
array once. The time it takes to execute the code is directly proportional to the size of the
array.
• O(n log n) - Linearithmic time: The runtime of the algorithm increases linearly multiplied by the
logarithm of the input size. Example: sorting algorithms like merge sort and quicksort.
• O(n^2) - Quadratic time: The runtime of the algorithm increases quadratically with the input size.
Example:
Output:
The code above prints all the elements of an array in a specific pattern. The printArrayElements
function takes an array arr and its size as parameters. It uses nested loops to iterate over the array
elements and print them. The outer loop iterates over the array elements from index 0 to size-1,
and the inner loop also iterates over the array elements from index 0 to size-1 (size-1 because array
indices in C++ start from 0). As a result, each element of the array is printed size times. Therefore, the
time complexity of this code is O(n^2), where n is the size of the array.
• O(2^n) - Exponential time: The runtime of the algorithm grows exponentially with the input size.
Example: generating all subsets of a set.
• O(n!) - Factorial time: The running time of an algorithm increases factorially with the size of the input.
Example: Using a recursive function to generate all permutations of a given set of elements
2. Arrays
Arrays in C++ have a fixed size, which is determined at compile-time. They can store elements of any data
type, including built-in types (int, float, etc.) and user-defined types (structs, classes).
Syntax:
type arrayName[arraySize]{initializer list};
The above syntax is known as zero initialization and is available in C++11 and later. It ensures that all
elements of the array are initialized to their default values, which is 0 for int.
Another syntax:
In arr2, we are explicitly specifying the size of the array as 5. This means that the array will have exactly 5
elements, and each element will be initialized with the corresponding value in the initializer list.
In arr3, the size of the array is automatically determined based on the number of elements in the initializer
list. In this case, the array will have a size of 5 because there are 5 elements in the initializer list. This syntax
is known as "array size deduction" and was introduced in C++17.
It's important to note that in both cases, the array elements are initialized in the order they appear in
the initializer list. If the initializer list has fewer elements than the size of the array, the remaining elements
will be value-initialized (which means they will be set to their default values).
Example:
To access elements in an array, you can use the subscript operator [] along with the index of the element you
want to access. Note that the index of arrays starts with 0.
You can use a variable as the index of the element you want to access:
• Using std::size:
Note that std::size is only available in C++17 and later versions. If you're using an earlier version of C++, you
can use the sizeof operator to get the size of an array.
The code above prints the addresses of each element in the array. The & operator is used to get the address
of a variable. The addresses are shown in hexadecimal notation. Each address is 4 bytes (32 bits) apart,
which corresponds to the size of an int element. You can observe that the addresses are contiguous
because they increase by 4 for each element. This indicates that the elements of the array are stored in
consecutive memory locations.
3. Pointers
Pointers in C++ are variables that hold memory addresses. They are used to store the location of other
variables or objects in memory.
• Pointers are declared using the asterisk (*) symbol.
• Pointers are not initialized by default. It is important to initialize pointers to a known value to
avoid undefined behavior.
• Pointers are often used to hold the address of another variable. The address of a variable can be
obtained using the address-of operator (&).
• The dereference operator (*) is used to access the value at the address stored in a pointer. It
allows you to manipulate the value that the pointer points to.
• Pointers can be declared using the asterisk (*) symbol next to the type name. For example, int*
ptr; declares a pointer to an integer.
Example:
Null pointers are pointers that do not point to any valid memory address. They are often used to indicate the
absence of a valid object or memory location. The nullptr keyword should be used to represent a null
pointer.
Examples:
Pointer arithmetic
Pointer arithmetic refers to performing arithmetic operations on pointers in order to manipulate memory
addresses. It is a fundamental concept in C++ and is often used when working with arrays or dynamically
allocated memory.
• Incrementing a pointer: When you increment a pointer, it moves to the next memory location of
the same type. For example, if you have a pointer to an integer (int* ptr), incrementing it (++ptr)
will move it to the next integer in memory. Similarly, if you have a pointer to a character (char*
ptr), incrementing it (++ptr) will move it to the next character in memory.
• Decrementing a pointer: Decrementing a pointer works in the opposite way. It moves the pointer
to the previous memory location of the same type.
• Adding an offset: You can also add an offset to a pointer using the addition operator (+). This
allows you to move the pointer by a specific number of elements. For example, ptr + 2 will move
the pointer two elements ahead.
• Subtracting an offset: Similarly, you can subtract an offset from a pointer using the subtraction
operator (-). This allows you to move the pointer back by a specific number of elements.
• Pointer comparison: Pointers can be compared using relational operators (<, >, <=, >=, ==, !=).
The comparison is based on the memory addresses they point to.
Example showing pointer arithmetic:
Output:
Output:
In the above example, we have a function modifyArray that takes a pointer to an integer (int* arr) and the
size of the array. Inside the function, we use pointer arithmetic to access and modify each element of the
array. The arr[i] syntax is equivalent to *(arr + i), where arr is the base address of the array and i is the index.
In the main function, we declare an array arr and calculate the size of the array using the sizeof operator. We
then pass the array and its size to the modifyArray function. After calling the function, we iterate over the
modified array and print its elements.
When you pass an array to a function in C++, you are actually passing a pointer to the first element of the
array. This means any changes made to the array inside the function will affect the original array.
Array to pointer decay
When an array is passed as a function argument, it decays into a pointer to its first element. This means
that the function receives a pointer to the array, rather than a copy of the entire array. The pointer can
then be used to access and modify the elements of the array.
Output:
In the main function, we declare an array arr and calculate its size. Int is 4 bytes and there are 5 elements so
the size will be the number of elements multiplied by the type’s size, which results to 20. When we call the
printArraySize function and pass the arr array as an argument, the array decays into a pointer to its first
element. As a result, the sizeof(arr) expression inside the printArraySize function returns the size of the
pointer, not the size of the original array.
Array decay to pointer can also be seen in the following example. This can happen when you want to access
an element but forgot to use the [] operator and the index of the element
Output: