Learn DSA Using Python || Lesson 03 - Data Structures || Code Explorer

Welcome To DSA Complete Course Using Python By Code Explorer


Here We Will Learn About Data Structures


What Are Data Structures ??


Data structures refer to the fundamental ways of organizing and storing data in a computer's memory to facilitate efficient manipulation and retrieval of information. These structures provide a systematic arrangement of data elements, defining the relationships between them and the operations that can be performed on them. Common data structures include arrays, linked lists, stacks, queues, trees, graphs, and hash tables, each designed to suit specific data management and processing needs. Choosing the right data structure is crucial in optimizing algorithms and ensuring optimal performance in software development and problem-solving.


Two Types Of Data Structures


We can divide Data Structures into two parts . Like We have : - Liner Data Structures & Non-Liner Data Structures

Liner Data Structures

  1. 1) Array
  2. 2) Linked List
  3. 3) Stack
  4. 4) Queue
  5. 5) Matrix

Non-Liner Data Structures

  1. 1) Binary Tree
  2. 2) Heap
  3. 3) Hash Table
  4. 4) Graph

Python Specific Data Structures


Python has some by default specific Data Structures like :-

  1. 1) List
  2. 2) Tuple
  3. 3) Dictionary
  4. 4) Set

Python allows us to create and use various types of Data Structures and you can control all of them .


Lists In Python


In Python, a list data structure is an ordered collection of elements that can hold items of different data types, such as integers, strings, or even other lists. Lists are mutable, meaning their elements can be modified, added, or removed after creation. They are represented using square brackets [ ] and can be indexed and sliced to access specific elements or subsets of the list. Python lists offer versatile functionality, making them widely used for data storage, iteration, and manipulation in various programming tasks.

Implementing A Simple List In Python

                  
                    # Creating a list
                    my_list = [1, 2, 3, 4, 5]
                    
                    # Accessing elements in the list
                    print("Element at index 0:", my_list[0])   
                    # Output: Element at index 0: 1
                    print("Element at index 2:", my_list[2])   
                    # Output: Element at index 2: 3
                    
                    # Modifying elements in the list
                    my_list[3] = 10
                    print("Modified list:", my_list)          
                    # Output: Modified list: [1, 2, 3, 10, 5]
                    
                    # Appending elements to the list
                    my_list.append(6)
                    print("List after appending 6:", my_list) 
                    # Output: List after appending 6: [1, 2, 3, 10, 5, 6]
                    
                    # Removing elements from the list
                    my_list.remove(3)
                    print("List after removing 3:", my_list)  
                    # Output: List after removing 3: [1, 2, 10, 5, 6]
                    
                    # Slicing the list
                    sliced_list = my_list[1:4]
                    print("Sliced list:", sliced_list)        
                    # Output: Sliced list: [2, 10, 5]
                    
                    # Length of the list
                    length_of_list = len(my_list)
                    print("Length of the list:", length_of_list)  
                    # Output: Length of the list: 5
                    
                  
                

In this code, we create a list called my_list and perform various operations such as accessing elements using index, modifying elements, appending elements, removing elements, slicing the list, and finding the length of the list. Lists in Python are very versatile and can be used in a wide range of scenarios for data storage and manipulation.


Nested or Multi Dimensional List

A nested or multi-dimensional list is a list that contains other lists as its elements, creating a structure similar to a matrix or an array of arrays. Each element in the main list can be another list, and these sub-lists can have different lengths. Here's an example of a 2D nested list in Python:

                      
                        # Creating a nested list (2D list)
                        nested_list = [
                            [1, 2, 3],
                            [4, 5, 6],
                            [7, 8, 9]
                        ]
                        
                        # Accessing elements in the nested list
                        print("Element at row 0, column 1:", nested_list[0][1])   
                        # Output: Element at row 0, column 1: 2
                        print("Element at row 2, column 2:", nested_list[2][2])   
                        # Output: Element at row 2, column 2: 9
                        
                        # Modifying elements in the nested list
                        nested_list[1][0] = 10
                        print("Modified nested list:", nested_list)
                        # Output: Modified nested list: [[1, 2, 3], [10, 5, 6], [7, 8, 9]]
                        
                        # Appending a new row to the nested list
                        new_row = [11, 12, 13]
                        nested_list.append(new_row)
                        print("Nested list after appending a new row:", nested_list)
                        # Output: Nested list after appending a new row: 
                        # [[1, 2, 3], [10, 5, 6], [7, 8, 9], [11, 12, 13]]
                        
                        # Slicing the nested list
                        sliced_list = nested_list[1:3]
                        print("Sliced nested list:", sliced_list)
                        # Output: Sliced nested list: [[10, 5, 6], [7, 8, 9]]
                        
                      
                    

In this example, nested_list is a 2D list with three rows, and each row is a list itself. We can access elements using two indices, one for the row and another for the column. The code demonstrates accessing, modifying, appending rows, and slicing the nested list. Multi-dimensional lists are useful when dealing with data organized in a grid-like structure, such as matrices or tables.


How To Check The Size Of A List ??

                        
                            list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, "Bhed"]
                            print(len(list))

                            # Output : 15
                        
                      

Using len() function we can easily find the length / size of any Lists .


Concept Of Negative Indexing

In Python, negative indexing is a concept that allows you to access elements from the end of a list using negative numbers as indices. Instead of counting from the beginning (0) like positive indexing, negative indexing starts counting from the end (-1) of the list. This means that the last element of the list can be accessed using index -1, the second-to-last element using index -2, and so on.

Here's how negative indexing works with a list :-

                        
                            # Creating a list
                            my_list = [10, 20, 30, 40, 50]
                            
                            # Accessing elements using positive indexing
                            print("Element at index 0:", my_list[0])   
                            # Output: Element at index 0: 10
                            print("Element at index 3:", my_list[3])   
                            # Output: Element at index 3: 40
                            
                            # Accessing elements using negative indexing
                            print("Element at index -1:", my_list[-1])   
                            # Output: Element at index -1: 50
                            print("Element at index -3:", my_list[-3])   
                            # Output: Element at index -3: 30
                            
                        
                      

Using negative indexing can be especially useful when you want to access elements near the end of the list, or when the length of the list is not known, but you still need to access the last few elements. Negative indexing provides a convenient and concise way to achieve this without explicitly calculating the positive index from the length of the list.

So, negative indexing is a powerful feature in Python lists that allows you to access elements from the end of the list using negative numbers as indices, making it easier and more intuitive to work with lists, especially when dealing with elements near the end.


Tuples In Python


In Python, a tuple is an immutable, ordered collection of elements enclosed in parentheses ( ) or created without any brackets. Unlike lists, tuples cannot be modified after creation, making them "immutable" data structures. Tuples can hold elements of different data types, just like lists, but once a tuple is created, its elements and their order cannot be changed.


Definition Of Tuples

Tuples in Python are fixed-size, ordered collections of elements, and are defined by enclosing the elements within parentheses. They provide a way to group related data together and are commonly used to represent data that should remain constant throughout the program's execution. The elements in a tuple can be accessed using indexing, similar to lists. However, since tuples are immutable, you cannot add, remove, or modify elements once the tuple is created, which makes them suitable for situations where data integrity and protection against accidental changes are desired. Tuples are commonly used to return multiple values from functions, as dictionary keys (since they are immutable), and as elements of sets, where only immutable objects are allowed.


Implementations Of Tuple In Python

                  
                    # Creating a tuple
                    my_tuple = (1, 2, 3, 'apple', 'banana')
                    
                    # Accessing elements in the tuple
                    print("Element at index 0:", my_tuple[0])     
                    # Output: Element at index 0: 1
                    print("Element at index 3:", my_tuple[3])     
                    # Output: Element at index 3: apple
                    
                    # Length of the tuple
                    length_of_tuple = len(my_tuple)
                    print("Length of the tuple:", length_of_tuple)    
                    # Output: Length of the tuple: 5
                    
                    # Iterating over the tuple
                    print("All elements in the tuple:")
                    for item in my_tuple:
                        print(item)
                    # Output:
                    # 1
                    # 2
                    # 3
                    # apple
                    # banana
                    
                  
                

In this example, my_tuple is a simple tuple containing integer and string elements. We access elements using indexing, find the length of the tuple, and iterate through all the elements using a for loop. Once a tuple is created, its elements cannot be changed, added, or removed, ensuring its immutability. Tuples are useful for scenarios where you need to store a fixed set of related data that should not be altered during the program's execution.


Immutable Property Of Tuples

Let's demonstrate the immutable property of tuples by attempting to modify a tuple after it has been created. This will result in a TypeError since tuples cannot be changed once they are defined.

          
            # Creating a tuple
            my_tuple = (1, 2, 3)
            
            # Attempting to modify the tuple
            try:
                my_tuple[1] = 10
                # Trying to change the element at index 1 to 10
            except TypeError as e:
                print("TypeError:", e)
                # Output: TypeError: 'tuple' object does not support item assignment
            
          
        

As shown in the code, when we try to modify the element at index 1 of the tuple my_tuple, Python raises a TypeError with the message " 'tuple' object does not support item assignment." This error indicates that tuples are immutable and cannot be changed after creation.

This is in contrast to lists, where you can modify, add, or remove elements freely. The immutability of tuples makes them suitable for situations where you want to ensure data integrity and protect against unintended changes to the data.


Dictionary In Python


In Python, a dictionary is an unordered, mutable collection of key-value pairs, where each key must be unique. Dictionaries are represented using curly braces { } and allow for efficient data retrieval and modification based on their keys. Each key is associated with a value, and this association is known as a key-value pair. Keys in dictionaries must be immutable objects, such as strings, numbers, or tuples, while values can be of any data type. Dictionaries are widely used in Python for tasks that involve data lookup and storage, mapping relationships between different elements, and efficiently organizing and managing data.


Implementations Of Dictionary

          
            # Creating a dictionary
            my_dict = {
                'name': 'John',
                'age': 30,
                'city': 'New York',
                'email': 'john@example.com'
            }
            
            # Accessing values using keys
            print("Name:", my_dict['name'])       # Output: Name: John
            print("Age:", my_dict['age'])         # Output: Age: 30
            print("City:", my_dict['city'])       # Output: City: New York
            print("Email:", my_dict['email'])     # Output: Email: john@example.com
            
            # Modifying values in the dictionary
            my_dict['age'] = 32
            print("Modified dictionary:", my_dict)
            # Output: Modified dictionary: 
            # {'name': 'John', 'age': 32, 'city': 'New York', 'email': 'john@example.com'}
            
            # Adding new key-value pairs to the dictionary
            my_dict['occupation'] = 'Engineer'
            print("Dictionary with new pair:", my_dict)
            # Output: Dictionary with new pair: 
            # {'name': 'John', 'age': 32, 'city': 'New York', 
               'email': 'john@example.com', 'occupation': 'Engineer'}
            
            # Removing a key-value pair from the dictionary
            del my_dict['email']
            print("Dictionary after removing email:", my_dict)
            # Output: Dictionary after removing email: 
            # {'name': 'John', 'age': 32, 'city': 'New York', 'occupation': 'Engineer'}
            
            # Length of the dictionary
            length_of_dict = len(my_dict)
            print("Length of the dictionary:", length_of_dict)
            # Output: Length of the dictionary: 4
            
          
        

In this example, my_dict is a dictionary with key-value pairs representing various information about a person. We access values using their corresponding keys, modify values, add new key-value pairs, and remove key-value pairs using the del keyword. Dictionaries are versatile data structures that offer efficient lookup and manipulation based on their keys, making them valuable for organizing and managing data in Python programs.


Set In Python


In Python, a set is an unordered, mutable collection of unique elements. Sets are represented using curly braces { } or the built-in `set()` function. Unlike lists or tuples, sets do not allow duplicate elements. When you create a set with duplicate items, Python automatically removes the duplicates, leaving only the unique elements. Sets are useful for tasks that involve membership tests, removing duplicates from sequences, and performing mathematical set operations like union, intersection, and difference. Additionally, since sets only contain unique elements, they are commonly used to find distinct elements in data collections.


Implementations Of Set

              
                # Creating a set using curly braces
                my_set = {1, 2, 3, 4, 4, 5}   
                # Note the duplicate element 4, which will be automatically removed
                print("Set using curly braces:", my_set)  
                # Output: Set using curly braces: {1, 2, 3, 4, 5}
                
                # Creating a set using the set() function
                another_set = set([3, 4, 5, 6, 7])
                print("Set using set() function:", another_set)  
                # Output: Set using set() function: {3, 4, 5, 6, 7}
                
                # Adding elements to the set
                my_set.add(6)
                my_set.add(7)
                print("Set after adding elements:", my_set)  
                # Output: Set after adding elements: {1, 2, 3, 4, 5, 6, 7}
                
                # Removing elements from the set
                my_set.remove(5)
                print("Set after removing element 5:", my_set)  
                # Output: Set after removing element 5: {1, 2, 3, 4, 6, 7}
                
                # Checking membership in the set
                print("Is 3 in the set?", 3 in my_set)  
                # Output: Is 3 in the set? True
                print("Is 5 in the set?", 5 in my_set)  
                # Output: Is 5 in the set? False
                
                # Set operations
                set_a = {1, 2, 3, 4, 5}
                set_b = {4, 5, 6, 7, 8}
                
                # Union of two sets
                union_set = set_a.union(set_b)
                print("Union of sets:", union_set)  
                # Output: Union of sets: {1, 2, 3, 4, 5, 6, 7, 8}
                
                # Intersection of two sets
                intersection_set = set_a.intersection(set_b)
                print("Intersection of sets:", intersection_set)  
                # Output: Intersection of sets: {4, 5}
                
                # Difference between two sets
                difference_set = set_a.difference(set_b)
                print("Difference of sets:", difference_set)  
                # Output: Difference of sets: {1, 2, 3}
                
                # Removing all elements from the set
                my_set.clear()
                print("Set after clearing:", my_set)  
                # Output: Set after clearing: set()
                
              
            

In this code, we first create sets using curly braces and the set() function. We demonstrate adding and removing elements from the set, checking membership using the in keyword, and performing set operations like union, intersection, and difference. Sets are versatile data structures, especially useful when you need to handle unique elements and perform set operations efficiently.