Javatpoint Logo
Javatpoint Logo

Data Structures and Algorithms in C | Set 1

Data Structures-Arrays, Dynamic Arrays, and Linked lists

DSA is a very important concept in any programming language. Suppose we have a lot of books, and we need to choose a bookshelf to organize all the books. We'll first check how many books we have, and then we'll assume how many more books we might buy in the future. Then, we'll think of a reasonable budget we can spend for the shelf. We'll check which one is strong and has a good life out of the filtered shelves. Finally, we'll buy the best one.

In programming, books refer to huge amounts of data we must deal with while writing code, the program we are writing is the room, and inside our program, we need to find a good storage spot to arrange all the data efficiently. A messy room is never any good.

In this series of tutorials, we'll cover all the data structures in C language and then about another topic-Algorithms.

Data Types and Data Structures:

These two words might have Data in common but are not the same. Simply, we can say that a data type is a characteristic of variables. It represents what kind of data/ value can be stored in a particular variable. C is a static language. Hence, we need to declare every variable we want to use in the program with its data type before using it.

On the other hand, a data structure is the collection and organization of data from different data types from which we can process and retrieve data. It deals with the arrangement of data in the computer's memory.

int, float, etc. are data types, and stacks, queues, etc. are examples of data structures.

There are different types of data structures available in C. We need to learn them to know which one to use when in need.

Here is a flowchart showing the classification in C based on the organization of the variables:

Data Structures and Algorithms in C | Set 1

Terminology:

  1. Linear DS: Linear: line. If the elements in a data structure are stored sequentially, it is a linear data structure.
  2. Static DS: If the memory or space in a data structure is fixed and can't be altered according to need at runtime, it is a static data structure.
  3. Dynamic DS: The space or the memory in the data structure can be altered according to the need at runtime.

Facts:

  1. A linear data structure is easy to traverse.
  2. Accessing elements of a static data structure is easy
  3. The space complexity of dynamic data structures is less, making them efficient.

Arrays:

An array is a collection of homogeneous elements at contiguous memory locations. The elements of an array can be accessed using indexes (0 to size-1). It can be multi-dimensional. It can store primitive and derived data typed elements, but all the elements should belong to the same data type. The aim of creating an array is to represent multiple same data-typed elements under one name.

Basic syntax:

Notable points:

  1. There is no IndexOutOfBoundException in C; if we try to access an element from out of indexes available in the array, the compiler won't raise any error but gives garbage values.
  2. Data type * name: An array of data-typed pointers. If we declare int * a, a is the name of an array that can consist of integer pointers.
  3. Pointers: The name of the array indicates the address of the first element, and when passed to functions, arrays are always passed as pointers, even if we use square braces.
  4. array[index] = *(array + index) = index[array] (commutative property)
  5. Matrices: We can use multi-dimensional arrays to represent matrices-array[rows][columns].

Here is a program with all the basic operations on arrays:

Output:

a[5] = 10 20 30 40 50
Out of index a[5]: 0
Array elements are stored in contiguous locations:
000000000062FDF0
000000000062FDF4
000000000062FDF8
000000000062FDFC
000000000062FE00
Pointer indication:
 a[0] = 10
 *a = 10
Traversing using pointer:
10
20
30
40
50
By commutative property a[i] = i[a] =
 a[2] = 30
 2[a] = 30
Multi-dimensional array(4 X 4):
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
Sum of elements of b[5] =  15
Sorting the array x: 1 2 4 7 9
--------------------------------
Process exited after 0.8778 seconds with return value 2
Press any key to continue . . .

Linked Lists:

A linked list is a linear data structure like an array, but the elements are not stored in contiguous memory locations. Every node/ element in the linked list has two sections-one with the data and the other with a pointer pointing to the next node's location. These pointers connect the nodes making a linked list linear. The address of the first node is stored in the head.

Notable Points:

  1. A linked list is also used to implement other user-defined data structures like stack and queue.
  2. The first node in a linked list is the head, and we must start all the operations on the linked list from it.
  3. The last node of the linked list refers to Null, showing that the linked list is complete.

In C, a linked list is created using structures, and it also is homogeneous like an array, meaning it can only store data of the same data type in its nodes.

Data Structures and Algorithms in C | Set 1

A simple linked list looks like this:

Data Structures and Algorithms in C | Set 1

As shown in the above figure, the head is the first node, and the next (reference) part of the last node holds None.

A double-linked list looks like this:

Data Structures and Algorithms in C | Set 1

In a double-linked list, every node will have three sections. Head holds the reference of the first node, the "previous" section of the first node holds None, and the next field of the last node refers to None. Each node will hold two references along with the data, one to its previous node and the next to the succeeding node.

Circular Linked List:

A circular linked list can be single or double:

Circular single linked list:

Data Structures and Algorithms in C | Set 1

It is a single linked list, but the last node in the list holds the reference of the first node like a circle.

Circular Double-linked List:

Data Structures and Algorithms in C | Set 1

It is a double-linked list, but the last node in the list holds the reference of the first node, and the 'previous' section of the first node holds the reference of the last node like a circle.

Here is a program showing some of the basic operations we can perform on a single linked list:

Output:

The created Linked List: HEAD -> 1 -> 2 -> 3 -> NULL

Insertions:

After inserting 5 at the beginning: HEAD -> 1 -> 2 -> 3 -> NULL -----> HEAD -> 5 -> 1 -> 2 -> 3 -> NULL

After inserting 10 after 2 node: HEAD -> 1 -> 2 -> 3 -> NULL ----> HEAD -> 1 -> 2 -> 10 -> 3 -> NULL

After inserting 17 at the end: HEAD -> 1 -> 2 -> 10 -> 3 -> NULL ----> HEAD -> 1 -> 2 -> 10 -> 3 -> 17 -> NULL

Deletions:

After deleting the 1st node: HEAD -> 1 -> 2 -> 10 -> 3 -> 17 -> NULL ----> HEAD -> 2 -> 10 -> 3 -> 17 -> NULL

After deleting the last node: HEAD -> 1 -> 2 -> 10 -> 3 -> 17 -> NULL ----> HEAD -> 1 -> 2 -> 10 -> 3 -> NULL

After deleting node from 3 position: HEAD -> 1 -> 2 -> 10 -> 3 -> NULL ----> HEAD -> 1 -> 2 -> 3 -> NULL

Dynamic Arrays:

Once we declare an array, its size will be fixed. It is a drawback because we might run into the need for more memory at runtime. Hence, dynamic arrays are introduced.

  • Unlike a normal array, dynamic arrays are expandable and shrinkable, providing random access to the elements.
  • Dynamic arrays are not the same as Variable length arrays. A variable length array is an array whose size is determined at runtime mostly by taking the size as input from the user.
  • Even in the case of variable length arrays, once the user gives the size, we can't modify it.
  • Regular arrays and variable-length arrays are allocated on the stack, whereas dynamic arrays are allocated on the heap.
  • C language doesn't support built-in dynamic arrays, but we can use dynamic memory allocations (DMA).

There are 4 built-in dynamic memory allocation functions available:

1. malloc():

  • malloc stands for memory allocation.
  • Syntax: (type*) malloc(size)
  • If we call the malloc function with n bytes reserves one large block of memory with n bytes. It returns a void pointer pointing to the first byte, which we can cast into any data type we want - (type*).
  • If the available space is insufficient, the allocation fails and returns a NULL pointer.
  • Each block is initialized with garbage values.

2. calloc():

  • calloc stands for contiguous allocation.
  • It is similar to malloc().
  • Syntax: (type*)calloc(count, size)
  • count is the number of elements, and size refers to the size of each element.
  • If we call the calloc function with m count and n bytes, it allocates m number of n-sized blocks.
  • Each block is initialized with 0.

In both malloc() and calloc(), the memory is allocated in one place contiguously. Hence, it is possible to create dynamic arrays.

Here is an example program to show the difference between malloc() and calloc() allocations:

Output:

Enter the number of elements in the first D-array: 5
Enter the number of elements in the second D-array: 5

Initial values:
By malloc(): 11901584 0 11862352 0 1936028257
By calloc(): 0 0 0 0 0

Memory allocations:
By malloc(): 11867904 11867908 11867912 11867916 11867920
By calloc(): 11867936 11867940 11867944 11867948 11867952

3. free():

  • It is used to explicitly de-allocate the memory of dynamic memory allocations. For regular variables, memory is released automatically at runtime. We need to use free() in the case of DMA for all the reserved memory to be released.
  • Syntax: free(ptr)
  • ptr refers to the pointer previously allocated using malloc or calloc.

4. realloc():

  • realloc stands for reallocation.
  • It is used to dynamically re-allocate the allocated dynamic memory.
  • Suppose we use malloc or calloc and reserve some memory, which is insufficient; we can reserve more memory using realloc.
  • Syntax: realloc(ptr, newsize)
  • ptr refers to the pointer previously allocated using malloc or calloc.

Suppose we created a regular array with size n, and we decided we didn't need the first element at runtime. We shift all the elements to the left by removing the first element. It will not change the array size, and the empty spot is never gone.

In the case of dynamic arrays, we can use realloc() to shrink the array. Here is an example to demonstrate the phenomenon:

Output:

Enter the size: 6

The memory allocations in a regular array: 6487264 6487268 6487272 6487276 6487280 6487284
The memory allocations in the dynamic array: 12130048 12130052 12130056 12130060 12130064 12130068

After removing the first element:
Regular array: 6487264 6487268 6487272 6487276 6487280 6487284
Dynamic array: 12130048 12130052 12130056 12130060 12130064
--------------------------------
Process exited after 2.795 seconds with return value 1
Press any key to continue . . .
  • We can't use sizeof() on a dynamic array. It only returns the size of the beginning pointer.
  • We need to use the free() function to explicitly de-allocate the memory.

Conclusion:

This is the first tutorial on Data structures and algorithms in C.

In this tutorial, we covered:

  1. Difference between Data type and Data structures
  2. Arrays
  3. Linked lists
  4. Dynamic arrays-DMA in C

Programs with all the basic operations and explanations are implemented for better understanding. In the next tutorial, we'll carry on with stacks and queues.







Youtube For Videos Join Our Youtube Channel: Join Now

Feedback


Help Others, Please Share

facebook twitter pinterest

Learn Latest Tutorials


Preparation


Trending Technologies


B.Tech / MCA