Skip to content
geeksforgeeks
  • Courses
    • DSA to Development
    • Get IBM Certification
    • Newly Launched!
      • Master Django Framework
      • Become AWS Certified
    • For Working Professionals
      • Interview 101: DSA & System Design
      • Data Science Training Program
      • JAVA Backend Development (Live)
      • DevOps Engineering (LIVE)
      • Data Structures & Algorithms in Python
    • For Students
      • Placement Preparation Course
      • Data Science (Live)
      • Data Structure & Algorithm-Self Paced (C++/JAVA)
      • Master Competitive Programming (Live)
      • Full Stack Development with React & Node JS (Live)
    • Full Stack Development
    • Data Science Program
    • All Courses
  • Tutorials
    • Data Structures & Algorithms
    • ML & Data Science
    • Interview Corner
    • Programming Languages
    • Web Development
    • CS Subjects
    • DevOps And Linux
    • School Learning
  • Practice
    • Build your AI Agent
    • GfG 160
    • Problem of the Day
    • Practice Coding Problems
    • GfG SDE Sheet
  • Contests
    • Accenture Hackathon (Ending Soon!)
    • GfG Weekly [Rated Contest]
    • Job-A-Thon Hiring Challenge
    • All Contests and Events
  • Python Tutorial
  • Interview Questions
  • Python Quiz
  • Python Glossary
  • Python Projects
  • Practice Python
  • Data Science With Python
  • Python Web Dev
  • DSA with Python
  • Python OOPs
Open In App
Next Article:
Learn DSA with Python | Python Data Structures and Algorithms
Next article icon

Last Minute Notes (LMNs) – Data Structures with Python

Last Updated : 24 Jan, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

Data Structures and Algorithms (DSA) are fundamental for effective problem-solving and software development. Python, with its simplicity and flexibility, provides a wide range of libraries and packages that make it easier to implement various DSA concepts. This "Last Minute Notes" article offers a quick and concise revision of the essential topics in Data Structures using Python.

Table of Content

  • Python Collections
  • Stack
  • Queues
  • Linked List
  • Trees
  • Hash Tables

Python Collections

In Python, collections are specialized container data types used to store and manage multiple items. They provide alternatives to built-in data types like lists, tuples, and dictionaries, offering enhanced functionality and efficiency for specific use cases.

1. List

In Python, a list is an ordered, mutable collection that can hold elements of different types. Lists are one of the most commonly used data types in Python because of their flexibility and ease of use.

  • Ordered: The items in a list have a specific order, and this order is maintained.
  • Mutable: You can modify, add, or remove items after the list is created.
  • Heterogeneous: A list can store elements of different data types (e.g., integers, strings, objects).

Syntax:

my_list = [1, 2, 3, "Python", 4.5]

Features:

  • Lists are indexed, meaning each item in the list can be accessed using an index (starting from 0).
  • You can use various methods to manipulate lists such as append(), remove(), insert(), and pop().

2. Tuples

In Python, a tuple is an ordered, immutable collection of items. Once a tuple is created, its elements cannot be modified, added, or removed. Tuples can store items of different data types, just like lists, but they offer a more efficient and safer alternative when you don't want the data to change.

Syntax:

my_tuple = (1, 2, 3, "Python", 4.5)

Features:

  • Ordered: The items in a tuple have a defined order and can be accessed using indexing.
  • Immutable: Once created, the values inside a tuple cannot be changed, making them more secure and hashable.
  • Heterogeneous: Tuples can hold elements of different types (e.g., integers, strings, floats).

3. Sets

In Python, a set is an unordered collection of unique elements. Sets do not allow duplicate values, making them useful for storing distinct items. Since they are unordered, the elements do not have a specific index and cannot be accessed by position.

Syntax:

my_set = {1, 2, 3, 4}

Features:

  • Unordered: The items in a set do not have a specific order.
  • Unique: A set automatically removes duplicate elements, ensuring all items are distinct.
  • Mutable: You can add or remove elements from a set after it is created.

4. Dictionary

A dictionary is an unordered collection of key-value pairs. Each key is unique, and it maps to a specific value. Dictionaries are useful for storing and retrieving data quickly based on a unique key.

Syntax:

my_dict = {"key1": "value1", "key2": "value2"}

Features:

  • Unordered: The items in a dictionary are not stored in any specific order.
  • Key-Value Pairs: Each element in a dictionary consists of a key and its associated value.
  • Mutable: You can change, add, or remove items from a dictionary.

5. List Comprehension

List comprehension is a concise way to create lists in Python. It provides a more readable and efficient alternative to traditional for loops for generating lists. You can use it to apply expressions or conditions while building a new list from an existing iterable.

Syntax:

new_list = [expression for item in iterable if condition]
  • expression: The value or operation that will be added to the new list.
  • iterable: The collection (like a list, tuple, or range) you’re looping through.
  • condition (optional): A condition to filter which items to include in the new list.

Features:

  • List comprehension makes code more concise and readable, allowing you to generate new lists in a single line.
  • It is often used to filter data or apply transformations efficiently.

6. NamedTuple

A NamedTuple is a subclass of the built-in tuple that allows you to define a tuple with named fields. It is useful for creating simple classes that hold data and allow you to access values using field names instead of relying on index positions.

Syntax:

from collections import namedtuple# Define a named tuple with fields 'name' and 'age'Person = namedtuple('Person', ['name', 'age'])

Features:

  • Immutable: Like tuples, namedtuples cannot be modified after creation.
  • Named Fields: Each element in the tuple can be accessed by name, improving code readability.
  • Memory Efficient: Namedtuples use less memory than regular classes because they are optimized for storing data.

7. Deque (Double-Ended Queue)

A deque (short for Double-Ended Queue) is a collection type from the collections module that allows fast appends and pops from both ends (front and rear) of the container. It is highly efficient for tasks that require adding or removing elements from both ends.

Syntax:

from collections import dequemy_deque = deque([1, 2, 3])

Features:

  • Efficient Operations: Deques allow O(1) time complexity for append and pop operations from both ends, unlike lists, which have O(n) complexity for operations at the beginning.
  • Mutable: You can add, remove, or modify elements after the deque is created.
  • Supports Queues and Stacks: Deques can be used to implement both queues and stacks due to their flexibility.

8. Counter

A Counter is a subclass of the dict class from the collections module, designed to count the frequency of elements in an iterable. It helps to quickly tally and store counts of items, making it especially useful for tasks like frequency analysis.

Syntax:

from collections import Countermy_counter = Counter([1, 2, 2, 3, 3, 3])

Features:

  • Counts Elements: Automatically counts the occurrences of elements in an iterable (e.g., a list or string).
  • Dictionary-like: Like a dictionary, it maps keys to values, where the key is the item, and the value is its count.
  • Supports Operations: You can perform arithmetic and set operations with Counters, such as adding, subtracting, and finding common elements.

9. OrderedDict

An OrderedDict is a specialized dictionary from the collections module that maintains the order of key-value pairs as they are added. Unlike a regular dictionary (which does not guarantee order before Python 3.7), an OrderedDict remembers the insertion order of items.

Syntax:

from collections import OrderedDictmy_ordered_dict = OrderedDict([('key1', 'value1'), ('key2', 'value2')])

Features:

  • Maintains Order: Unlike regular dictionaries, OrderedDict remembers the order in which items were inserted.
  • Mutable: You can modify, add, or remove items after creation.
  • Supports Dictionary Methods: All typical dictionary methods, such as get(), keys(), and items(), are supported.

Stack

A stack is a linear data structure that follows the Last-In/First-Out (LIFO) principle. Elements are added and removed from the same end, with the operations called push (insert) and pop (delete).

stackDiagram1

The functions associated with stack are:

  • empty() – Returns whether the stack is empty – Time Complexity: O(1)
  • size() – Returns the size of the stack – Time Complexity: O(1)
  • top() / peek() – Returns a reference to the topmost element of the stack – Time Complexity: O(1)
  • push(a) – Inserts the element ‘a’ at the top of the stack – Time Complexity: O(1)
  • pop() – Deletes the topmost element of the stack – Time Complexity: O(1)

Implementation

There are several ways to implement a stack in Python. A stack in Python can be implemented using the following approaches:

1. Implementation Using List

Python's built-in list can be used as a stack, where append() adds elements and pop() removes them in LIFO order. However, lists can face performance issues as they grow, as they require memory reallocation when the stack exceeds the current memory block, causing some append() operations to take longer.

Python
# Create an empty list to represent the stack stack = []  # Push items onto the stack using append() stack.append(1)  # Push 1 stack.append(2)  # Push 2 stack.append(3)  # Push 3  # Check the stack size print("Stack size:", len(stack))  # Output: 3  # Pop items from the stack using pop() print(stack.pop())  # Output: 3 (pop) print(stack.pop())  # Output: 2 (pop)  # Check if the stack is empty print("Is stack empty?", len(stack) == 0)  # Output: False  # Pop the last item print(stack.pop())  # Output: 1 (pop)  # Attempt to pop from an empty stack # Uncommenting the next line will raise an error: # print(stack.pop())  # IndexError: pop from empty list 

Output
Stack size: 3 3 2 Is stack empty? False 1 

2. Implementation Using collections.deque

Python’s stack can be implemented using the deque class from the collections module. Deque is preferred over lists for faster append() and pop() operations, offering O(1) time complexity compared to O(n) for lists. It uses the same methods: append() and pop().

Python
from collections import deque  # Create an empty deque to represent the stack stack = deque()  # Push items onto the stack using append() stack.append(1)  # Push 1 stack.append(2)  # Push 2 stack.append(3)  # Push 3  # Check the stack size print("Stack size:", len(stack))  # Output: 3  # Pop items from the stack using pop() print(stack.pop())  # Output: 3 (pop) print(stack.pop())  # Output: 2 (pop)  # Check if the stack is empty print("Is stack empty?", len(stack) == 0)  # Output: False  # Pop the last item print(stack.pop())  # Output: 1 (pop)  # Attempt to pop from an empty stack # Uncommenting the next line will raise an error: # print(stack.pop())  # IndexError: pop from an empty deque 

Output
Stack size: 3 3 2 Is stack empty? False 1 

3. Implementation Using Queue Module

The queue module in Python includes a LIFO Queue, which functions as a stack. Data is added using put() and removed using get().

Key functions:

  • maxsize: Sets the maximum number of items in the queue.
  • empty(): Returns True if the queue is empty.
  • full(): Returns True if the queue is full.
  • get(): Removes and returns an item, blocking if the queue is empty.
  • get_nowait(): Returns an item without blocking; raises QueueEmpty if empty.
  • put(item): Adds an item, blocking if the queue is full.
  • put_nowait(item): Adds an item without blocking; raises QueueFull if full.
  • qsize(): Returns the number of items in the queue.
Python
import queue  # Create a LifoQueue (stack) with maxsize=3 stack = queue.LifoQueue(maxsize=3)  # Push items onto the stack using put() stack.put(1)  # Push 1 stack.put(2)  # Push 2 stack.put(3)  # Push 3  # Check the stack size print("Stack size:", stack.qsize())  # Output: 3  # Pop items from the stack using get() print(stack.get())  # Output: 3 (pop) print(stack.get())  # Output: 2 (pop)  # Check if the stack is empty print("Is stack empty?", stack.empty())  # Output: False  # Pop the last item print(stack.get())  # Output: 1 (pop)  # Attempt to pop from an empty stack # Uncommenting the next line will raise an error: # print(stack.get())  # queue.Empty exception 

Output
Stack size: 3 3 2 Is stack empty? False 1 

4. Implementation Using a Singly Linked List

A linked list can implement a stack with methods like addHead(item) and removeHead(), both running in constant time.

Key methods:

  • getSize(): Returns the number of items in the stack.
  • isEmpty(): Returns True if the stack is empty.
  • peek(): Returns the top item; raises an exception if empty.
  • push(value): Adds a value to the head of the stack.
  • pop(): Removes and returns the top item; raises an exception if empty.
Python
# Create a Node class to create a node class Node:     def __init__(self, data):         self.data = data         self.next = None  # Create a LinkedList class class LinkedList:     def __init__(self):         self.head = None      # Method to add a node at the beginning of the LL     def insertAtBegin(self, data):         new_node = Node(data)         new_node.next = self.head         self.head = new_node      # Method to add a node at any index     # Indexing starts from 0.     def insertAtIndex(self, data, index):         if index == 0:             self.insertAtBegin(data)             return          position = 0         current_node = self.head         while current_node is not None and position + 1 != index:             position += 1             current_node = current_node.next          if current_node is not None:             new_node = Node(data)             new_node.next = current_node.next             current_node.next = new_node         else:             print("Index not present")      # Method to add a node at the end of LL     def insertAtEnd(self, data):         new_node = Node(data)         if self.head is None:             self.head = new_node             return          current_node = self.head         while current_node.next:             current_node = current_node.next          current_node.next = new_node      # Update node at a given position     def updateNode(self, val, index):         current_node = self.head         position = 0         while current_node is not None and position != index:             position += 1             current_node = current_node.next          if current_node is not None:             current_node.data = val         else:             print("Index not present")      # Method to remove first node of linked list     def remove_first_node(self):         if self.head is None:             return          self.head = self.head.next      # Method to remove last node of linked list     def remove_last_node(self):         if self.head is None:             return          # If there's only one node         if self.head.next is None:             self.head = None             return          # Traverse to the second last node         current_node = self.head         while current_node.next and current_node.next.next:             current_node = current_node.next          current_node.next = None      # Method to remove a node at a given index     def remove_at_index(self, index):         if self.head is None:             return          if index == 0:             self.remove_first_node()             return          current_node = self.head         position = 0         while current_node is not None and current_node.next is not None and position + 1 != index:             position += 1             current_node = current_node.next          if current_node is not None and current_node.next is not None:             current_node.next = current_node.next.next         else:             print("Index not present")      # Method to remove a node from the linked list by its data     def remove_node(self, data):         current_node = self.head          # If the node to be removed is the head node         if current_node is not None and current_node.data == data:             self.remove_first_node()             return          # Traverse and find the node with the matching data         while current_node is not None and current_node.next is not None:             if current_node.next.data == data:                 current_node.next = current_node.next.next                 return             current_node = current_node.next          # If the data was not found         print("Node with the given data not found")      # Print the size of the linked list     def sizeOfLL(self):         size = 0         current_node = self.head         while current_node:             size += 1             current_node = current_node.next         return size      # Print the linked list     def printLL(self):         current_node = self.head         while current_node:             print(current_node.data)             current_node = current_node.next   # create a new linked list llist = LinkedList()  # add nodes to the linked list llist.insertAtEnd('a') llist.insertAtEnd('b') llist.insertAtBegin('c') llist.insertAtEnd('d') llist.insertAtIndex('g', 2)  # print the linked list print("Node Data:") llist.printLL()  # remove nodes from the linked list print("\nRemove First Node:") llist.remove_first_node() llist.printLL()  print("\nRemove Last Node:") llist.remove_last_node() llist.printLL()  print("\nRemove Node at Index 1:") llist.remove_at_index(1) llist.printLL()  # print the linked list after all removals print("\nLinked list after removing a node:") llist.printLL()  print("\nUpdate node Value at Index 0:") llist.updateNode('z', 0) llist.printLL()  print("\nSize of linked list:", llist.sizeOfLL()) 

Output
Node Data: c a g b d  Remove First Node: a g b d  Remove Last Node: a g b  Remove Node at Index 1: a b  Linked list after removing a node: a b  Update node Value at Index 0: z b  Size of linked list: ...

Queues

A queue is a linear data structure that follows the First In First Out (FIFO) principle, where the first item added is the first to be removed. It’s commonly used in scenarios like customer service, where the first customer to arrive is the first to be served.

QueueDiagram

Operations associated with queue are: 

  • Enqueue: Adds an item to the queue. If the queue is full, then it is said to be an Overflow condition – Time Complexity : O(1)
  • Dequeue: Removes an item from the queue. The items are popped in the same order in which they are pushed. If the queue is empty, then it is said to be an Underflow condition – Time Complexity : O(1)
  • Front: Get the front item from queue – Time Complexity : O(1)
  • Rear: Get the last item from queue – Time Complexity : O(1)

Implementation

There are several ways to implement a queue in Python. This article explores different methods using Python's built-in data structures and modules. A queue can be implemented in the following ways:

1. Implementation Using List

Python’s built-in list can be used as a queue, using append() to enqueue and pop() to dequeue. However, lists are inefficient for queues, as removing elements from the beginning requires shifting all other elements, which takes O(n) time. The code simulates a queue by adding elements ('a', 'b', 'c') and then dequeuing them, resulting in an empty queue.

Python
# Create an empty list to represent the queue queue = []  # Enqueue items using append() queue.append(1)  # Enqueue 1 queue.append(2)  # Enqueue 2 queue.append(3)  # Enqueue 3  # Check the queue size print("Queue size:", len(queue))  # Output: 3  # Dequeue items using pop(0) (removes from front) print(queue.pop(0))  # Output: 1 (dequeue) print(queue.pop(0))  # Output: 2 (dequeue)  # Check if the queue is empty print("Is queue empty?", len(queue) == 0)  # Output: False  # Remove the last item print(queue.pop(0))  # Output: 3 (dequeue)  # Attempt to dequeue from an empty queue # Uncommenting the next line will raise an error: # print(queue.pop(0))  # IndexError: pop from empty list 

Output
Queue size: 3 1 2 Is queue empty? False 3 

2. Implementation Using collections.deque

In Python, a queue can be implemented using the deque class from the collections module, offering O(1) time complexity for append() and popleft() operations, unlike lists which have O(n) time complexity. The code demonstrates enqueueing ('a', 'b', 'c') using append() and dequeuing with popleft(), resulting in an empty queue. An attempt to dequeue from an empty queue raises an IndexError.

Python
from collections import deque  # Create a deque (queue) q = deque()  # Add items to the queue (enqueue) q.append(1)  # Enqueue 1 q.append(2)  # Enqueue 2 q.append(3)  # Enqueue 3  # Check the size of the queue print("Queue size:", len(q))  # Output: 3  # Remove items from the queue (dequeue) print(q.popleft())  # Output: 1 print(q.popleft())  # Output: 2  # Check if the queue is empty print("Is queue empty?", len(q) == 0)  # Output: False  # Remove the last item print(q.popleft())  # Output: 3  # Attempt to dequeue from an empty deque (will raise error) # Uncommenting the next line will raise an error: # print(q.popleft())  # IndexError: pop from an empty deque 

Output
Queue size: 3 1 2 Is queue empty? False 3 

3. Implementation Using queue.Queue

Python's queue module implements a FIFO queue with the Queue(maxsize) class, where maxsize limits the queue size (0 means infinite). Key functions include:

  • maxsize: Maximum items allowed in the queue.
  • empty(): Returns True if the queue is empty.
  • full(): Returns True if the queue is full.
  • get(): Removes and returns an item, blocking if empty.
  • get_nowait(): Returns an item if available, else raises QueueEmpty.
  • put(item): Adds an item, blocking if full.
  • put_nowait(item): Adds an item without blocking; raises QueueFull if full.
  • qsize(): Returns the current queue size.
Python
import queue  # Create a queue with maxsize of 3 q = queue.Queue(maxsize=3)  # Add items to the queue q.put(1)  # Enqueue 1 q.put(2)  # Enqueue 2 q.put(3)  # Enqueue 3  # Check queue size and if it's full print("Queue size:", q.qsize())  # Output: 3 print("Is queue full?", q.full())  # Output: True  # Remove items from the queue print(q.get())  # Output: 1 (dequeue) print(q.get())  # Output: 2 (dequeue)  # Check if the queue is empty print("Is queue empty?", q.empty())  # Output: False  # Remove the last item print(q.get())  # Output: 3 (dequeue)  # Attempt to dequeue from an empty queue # Uncommenting the next line will raise an error: # print(q.get_nowait())  # QueueEmpty exception 

Output
Queue size: 3 Is queue full? True 1 2 Is queue empty? False 3 

Linked List

A linked list is a linear data structure consisting of nodes, where each node contains data and a link to the next node. The first node, pointed to by the head, allows access to all elements in the list. In Python, linked lists are implemented using classes.

LLdrawio

Creating a linked list in Python

In the LinkedList class, we use the Node class to create a linked list. The class includes methods to:

  • insertAtBegin(): Insert a node at the beginning.
  • insertAtIndex(): Insert a node at a specific index.
  • insertAtEnd(): Insert a node at the end.
  • remove_node(): Delete a node with specific data.
  • sizeOfLL(): Get the current size of the linked list.
  • printLL(): Traverse and print the data of each node.

Creating a Node Class

The Node class has an __init__ function that initializes the node with the provided data and sets its reference to None, as there’s no next node if it’s the only node.

Python
class Node:     def __init__(self, data):         self.data = data         self.next = None 

Insertion in Linked List

1. Insertion at Beginning in Linked List

This method inserts a node at the beginning of the linked list. It creates a new node with the given data and, if the head is empty, sets the new node as the head. Otherwise, the current head becomes the next node, and the new node is set as the head.

Python
def insertAtBegin(self, data):     new_node = Node(data)     if self.head is None:         self.head = new_node         return     else:         new_node.next = self.head         self.head = new_node 

2. Insert a Node at a Specific Position in a Linked List

This method inserts a node at a specified index. It creates a new node with the given data and initializes a counter. If the index is 0, it calls the insertAtBegin() method. Otherwise, it traverses the list until reaching the desired position. If the index exists, the new node is inserted; if not, it prints "Index not present".

Python
# Method to add a node at any index # Indexing starts from 0. def insertAtIndex(self, data, index):     if (index == 0):         self.insertAtBegin(data)         return      position = 0     current_node = self.head     while (current_node != None and position+1 != index):         position = position+1         current_node = current_node.next      if current_node != None:         new_node = Node(data)         new_node.next = current_node.next         current_node.next = new_node     else:         print("Index not present") 

3. Insertion in Linked List at End

This method inserts a node at the end of the linked list. It creates a new node with the given data and checks if the head is empty. If it is, the new node becomes the head. Otherwise, it traverses the list to the last node and inserts the new node after it.

Python
def inserAtEnd(self, data):     new_node = Node(data)     if self.head is None:         self.head = new_node         return      current_node = self.head     while(current_node.next):         current_node = current_node.next      current_node.next = new_node 

Updating the Node of a Linked List

This code defines a method called updateNode in a linked list class. It is used to update the value of a node at a given position in the linked list.

Python
# Update node of a linked list # at given position def updateNode(self, val, index):     current_node = self.head     position = 0     if position == index:         current_node.data = val     else:         while(current_node != None and position != index):             position = position+1             current_node = current_node.next          if current_node != None:             current_node.data = val         else:             print("Index not present") 

Deleting a Node in Linked List

1. Removing First Node from Linked List

This method removes the first node of the linked list by setting the second node as the new head.

Python
def remove_first_node(self):     if(self.head == None):         return          self.head = self.head.next 

2. Removing Last Node from Linked List

This method deletes the last node by first traversing to the second-last node, then setting its next reference to None, effectively removing the last node.

Python
def remove_last_node(self):      if self.head is None:         return      curr_node = self.head     while (curr_node.next != None and curr_node.next.next != None):         curr_node = curr_node.next      curr_node.next = None 

3. Deleting a Linked List Node at a given Position

This method removes a node at a given index, similar to the insert_at_index() method. If the head is None, it returns immediately. Otherwise, it initializes current_node with the head and position with 0. If the position matches the index, it calls remove_first_node(). Otherwise, it traverses the list using a while loop until current_node is None or the position reaches the target index minus 1. After the loop, if current_node or current_node.next is None, the index is out of range. If not, it bypasses the node to remove it.

Python
# Method to remove at given index def remove_at_index(self, index):     if self.head is None:         return      current_node = self.head     position = 0          if index == 0:         self.remove_first_node()     else:         while current_node is not None and position < index - 1:             position += 1             current_node = current_node.next                  if current_node is None or current_node.next is None:             print("Index not present")         else:             current_node.next = current_node.next.next 

3. Delete a Linked List Node of a given Data

This method removes the node with the specified data from the linked list. It starts by setting current_node to the head and traverses the list using a while loop. The loop breaks when current_node is None or the data of the next node matches the given data. After exiting the loop, if current_node is None, the node is not found, and the method returns. If the next node’s data matches, the node is removed by linking the current node's next to the next node’s next.

Python
def remove_node(self, data):     current_node = self.head      # Check if the head node contains the specified data     if current_node.data == data:         self.remove_first_node()         return      while current_node is not None and current_node.next.data != data:         current_node = current_node.next      if current_node is None:         return     else:         current_node.next = current_node.next.next 

Linked List Traversal in Python

This method traverses the linked list, printing the data of each node. It starts with current_node as the head and iterates through the list, printing the data and moving to the next node until current_node is None.

Python
def printLL(self):     current_node = self.head     while(current_node):         print(current_node.data)         current_node = current_node.next 

Get Length of a Linked List in Python

This method returns the size of the linked list. It initializes a counter size to 0, then traverses the list, incrementing the size with each node until current_node becomes None. If the list is empty, it returns 0.

Python
def sizeOfLL(self):     size = 0     if(self.head):         current_node = self.head         while(current_node):             size = size+1             current_node = current_node.next         return size     else:         return 0 

Example of the Linked list in Python

In this example, we define the Node and LinkedList classes and create a linked list named "llist." We insert four nodes with character data ('a', 'b', 'c', 'd', 'g') and print the list using the printLL() method. After removing some nodes, we print the list again to verify successful deletion, followed by printing the size of the linked list.

Python
# Create a Node class to create a node class Node:     def __init__(self, data):         self.data = data         self.next = None  # Create a LinkedList class class LinkedList:     def __init__(self):         self.head = None      # Method to add a node at the beginning of the LL     def insertAtBegin(self, data):         new_node = Node(data)         new_node.next = self.head         self.head = new_node      # Method to add a node at any index     # Indexing starts from 0.     def insertAtIndex(self, data, index):         if index == 0:             self.insertAtBegin(data)             return          position = 0         current_node = self.head         while current_node is not None and position + 1 != index:             position += 1             current_node = current_node.next          if current_node is not None:             new_node = Node(data)             new_node.next = current_node.next             current_node.next = new_node         else:             print("Index not present")      # Method to add a node at the end of LL     def insertAtEnd(self, data):         new_node = Node(data)         if self.head is None:             self.head = new_node             return          current_node = self.head         while current_node.next:             current_node = current_node.next          current_node.next = new_node      # Update node at a given position     def updateNode(self, val, index):         current_node = self.head         position = 0         while current_node is not None and position != index:             position += 1             current_node = current_node.next          if current_node is not None:             current_node.data = val         else:             print("Index not present")      # Method to remove first node of linked list     def remove_first_node(self):         if self.head is None:             return          self.head = self.head.next      # Method to remove last node of linked list     def remove_last_node(self):         if self.head is None:             return          # If there's only one node         if self.head.next is None:             self.head = None             return          # Traverse to the second last node         current_node = self.head         while current_node.next and current_node.next.next:             current_node = current_node.next          current_node.next = None      # Method to remove a node at a given index     def remove_at_index(self, index):         if self.head is None:             return          if index == 0:             self.remove_first_node()             return          current_node = self.head         position = 0         while current_node is not None and current_node.next is not None and position + 1 != index:             position += 1             current_node = current_node.next          if current_node is not None and current_node.next is not None:             current_node.next = current_node.next.next         else:             print("Index not present")      # Method to remove a node from the linked list by its data     def remove_node(self, data):         current_node = self.head          # If the node to be removed is the head node         if current_node is not None and current_node.data == data:             self.remove_first_node()             return          # Traverse and find the node with the matching data         while current_node is not None and current_node.next is not None:             if current_node.next.data == data:                 current_node.next = current_node.next.next                 return             current_node = current_node.next          # If the data was not found         print("Node with the given data not found")      # Print the size of the linked list     def sizeOfLL(self):         size = 0         current_node = self.head         while current_node:             size += 1             current_node = current_node.next         return size      # Print the linked list     def printLL(self):         current_node = self.head         while current_node:             print(current_node.data)             current_node = current_node.next   # create a new linked list llist = LinkedList()  # add nodes to the linked list llist.insertAtEnd('a') llist.insertAtEnd('b') llist.insertAtBegin('c') llist.insertAtEnd('d') llist.insertAtIndex('g', 2)  # print the linked list print("Node Data:") llist.printLL()  # remove nodes from the linked list print("\nRemove First Node:") llist.remove_first_node() llist.printLL()  print("\nRemove Last Node:") llist.remove_last_node() llist.printLL()  print("\nRemove Node at Index 1:") llist.remove_at_index(1) llist.printLL()  # print the linked list after all removals print("\nLinked list after removing a node:") llist.printLL()  print("\nUpdate node Value at Index 0:") llist.updateNode('z', 0) llist.printLL()  print("\nSize of linked list:", llist.sizeOfLL()) 

Output
Node Data: c a g b d  Remove First Node: a g b d  Remove Last Node: a g b  Remove Node at Index 1: a b  Linked list after removing a node: a b  Update node Value at Index 0: z b  Size of linked list: ...

Trees

A tree is a hierarchical data structure consisting of nodes connected by edges. It is used to represent relationships, such as family trees, organizational structures, and file systems. The topmost node is called the root, and each node contains a value and references (or pointers) to its child nodes.

Key terms in Tree:

  • Node: Contains data and links to its child nodes.
  • Root: The topmost node in a tree.
  • Leaf: A node with no children.
  • Parent: A node that has one or more child nodes.
  • Child: A node that is a descendant of another node.
  • Height: The number of edges from the node to the deepest leaf.
  • Depth: The number of edges from the root to the node.
  • Subtree: A tree consisting of a node and all its descendants.

Types of Trees

  • Binary Tree:
    • Each node has at most two children.
    • The children are typically referred to as the left and right child.
    • Commonly used in searching algorithms like binary search trees.
  • Binary Search Tree (BST):
    • A special type of binary tree where the left child contains smaller values and the right child contains larger values than the parent node.
    • It allows efficient searching, insertion, and deletion operations.
  • AVL Tree:
    • A self-balancing binary search tree where the difference in height between the left and right subtrees of any node is at most one.
    • Ensures logarithmic time complexity for search, insert, and delete operations.
  • Heap Tree:
    • A complete binary tree that satisfies the heap property.
      • Max-Heap: The value of the parent is greater than or equal to the values of its children.
      • Min-Heap: The value of the parent is smaller than or equal to the values of its children.
    • Used in priority queues and heap sort algorithms.
  • Trie (Prefix Tree):
    • A type of search tree used for storing a dynamic set of strings, often used for word search applications.
    • Each node represents a common prefix of the stored strings.
  • N-ary Tree:
    • A tree where each node can have at most N children.
    • Used to represent structures like multi-way decision trees and file systems.
  • Red-Black Tree:
    • A self-balancing binary search tree where each node has an extra bit for color (red or black).
    • Used for balanced search trees in applications where performance is crucial.

Common Operations on Trees:

  • Insertion: Add a new node to the tree.
  • Deletion: Remove a node from the tree.
  • Traversal: Visit all the nodes in a specific order:
    • In-order: Left, Root, Right (Used in BST to get sorted order).
    • Pre-order: Root, Left, Right.
    • Post-order: Left, Right, Root.
    • Level-order: Visit nodes level by level from top to bottom.

Implementation in Python

Python
class Node:     def __init__(self, data):         self.data = data  # Store node data         self.left = None  # Left child         self.right = None  # Right child  class BinaryTree:     def __init__(self):         self.root = None  # Initialize with empty tree      def insert(self, data):         if not self.root:             self.root = Node(data)         else:             self._insert(self.root, data)      def _insert(self, node, data):         if data < node.data:             if node.left is None:                 node.left = Node(data)             else:                 self._insert(node.left, data)         else:             if node.right is None:                 node.right = Node(data)             else:                 self._insert(node.right, data)      def inorder(self, node):         if node:             self.inorder(node.left)             print(node.data, end=" ")             self.inorder(node.right)  # Example usage bt = BinaryTree() bt.insert(50) bt.insert(30) bt.insert(70) bt.insert(20) bt.insert(40) bt.insert(60) bt.insert(80)  print("In-order traversal of the tree:") bt.inorder(bt.root) 

Output
In-order traversal of the tree: 20 30 40 50 60 70 80 

Hash Tables

A Hash Table (also known as a Hash Map) is a data structure that stores data in key-value pairs, making it efficient for lookup, insertion, and deletion operations. It uses a hash function to compute an index into an array of buckets or slots, from which the desired value can be found.

In Python, dictionaries (dict) are implemented as hash tables, providing an easy way to store and retrieve data using keys.

Major concepts of Hash Tables:

  • Hash Function: A function that converts the key into an index in the hash table. It ensures that the keys are uniformly distributed across the table, minimizing collisions.
  • Key-Value Pair: Data in a hash table is stored as pairs. The key is hashed, and the value is stored at the computed index.
  • Collision Handling: When two keys hash to the same index, a collision occurs. Common strategies for handling collisions include:
    • Chaining: Storing multiple key-value pairs at the same index using a linked list or another data structure.
    • Open Addressing: Finding the next available slot in the table (e.g., linear probing, quadratic probing).

Operations in Hash Tables

  • Insertion: Adding a key-value pair to the hash table.
  • Deletion: Removing a key-value pair based on the key.
  • Search: Retrieving a value by its key.
  • Update: Modifying the value associated with a specific key.

Python’s Dictionary as a Hash Table:

In Python, dictionaries are implemented using hash tables. Here's how they work:

  • Keys in dictionaries are hashed, and their associated values are stored at the computed index.
  • Python handles collisions automatically, and the hash table grows dynamically as needed.

Operations on Python Dictionaries (Hash Maps)

1. Creating a Dictionary:

my_dict = {'apple': 1, 'banana': 2, 'cherry': 3}

2. Adding Items:

my_dict['orange'] = 4 # Adds a new key-value pair

3. Accessing Values:

print(my_dict['apple']) # Output: 1

4. Updating Values:

my_dict['banana'] = 5 # Updates the value associated with the key 'banana'

5. Deleting Items:

del my_dict['cherry'] # Deletes the key 'cherry' and its value

6. Checking for a Key:

print('apple' in my_dict) # Output: True

7. Iterating Through a Dictionary:

for key, value in my_dict.items():

print(key, value)

8. Getting the Size:

print(len(my_dict)) # Output: 3 (number of items)

9. Clearing the Dictionary:

my_dict.clear() # Removes all items

Collisions and How Python Handles Them

  • Python uses a technique called open addressing for collision resolution.
  • Chaining isn’t directly used in Python’s dictionaries, but the internal structure handles multiple items efficiently at the same index.

Time Complexity

  • Average Case:
    • Search, Insert, Delete: O(1) for average cases.
  • Worst Case: O(n), when the hash function leads to many collisions (e.g., a poor hash function or a small hash table).

Example Program

Python
# Creating a dictionary (Hash Table) my_dict = {'name': 'Alice', 'age': 25, 'city': 'New York'}  # Insertion my_dict['job'] = 'Engineer'  # Accessing a value print(my_dict['name'])  # Output: Alice  # Updating a value my_dict['age'] = 26  # Deletion del my_dict['city']  # Checking if a key exists print('job' in my_dict)  # Output: True  # Iterating through the dictionary for key, value in my_dict.items():     print(f'{key}: {value}')  # Size of dictionary print(len(my_dict))  # Output: 3 

Output
Alice True name: Alice age: 26 job: Engineer 3 



Next Article
Learn DSA with Python | Python Data Structures and Algorithms

R

realravipal27
Improve
Article Tags :
  • Python
  • Computer Subject
  • Data Structures
  • GATE
  • Data Structures
  • python
  • GATE
  • GATE DA
Practice Tags :
  • Data Structures
  • Data Structures
  • python
  • python

Similar Reads

  • Inbuilt Data Structures in Python
    Python has four non-primitive inbuilt data structures namely Lists, Dictionary, Tuple and Set. These almost cover 80% of the our real world data structures. This article will cover the above mentioned topics. Above mentioned topics are divided into four sections below. Lists: Lists in Python are one
    3 min read
  • Internal implementation of Data Structures in Python
    Python provides a variety of built-in data structures, each with its own characteristics and internal implementations optimized for specific use cases. In this article we are going to discuss about the most commonly used Data structures in Python and a brief overview of their internal implementation
    3 min read
  • User Defined Data Structures in Python
    In computer science, a data structure is a logical way of organizing data in computer memory so that it can be used effectively. A data structure allows data to be added, removed, stored and maintained in a structured manner. Python supports two types of data structures: Non-primitive data types: Py
    4 min read
  • Learn DSA with Python | Python Data Structures and Algorithms
    This tutorial is a beginner-friendly guide for learning data structures and algorithms using Python. In this article, we will discuss the in-built data structures such as lists, tuples, dictionaries, etc. and some user-defined data structures such as linked lists, trees, graphs, etc. 1. ListList is
    8 min read
  • Last Minute Notes (LMNs) - Python Programming
    Python is a widely-used programming language, celebrated for its simplicity, comprehensive features, and extensive library support. This "Last Minute Notes" article aims to offer a quick, concise overview of essential Python topics, including data types, operators, control flow statements, functions
    15+ min read
  • Introduction to Data Structures
    What is Data Structure?A data structure is a particular way of organising data in a computer so that it can be used effectively. The idea is to reduce the space and time complexities of different tasks. The choice of a good data structure makes it possible to perform a variety of critical operations
    7 min read
  • Python Data Structures
    Data Structures are a way of organizing data so that it can be accessed more efficiently depending upon the situation. Data Structures are fundamentals of any programming language around which a program is built. Python helps to learn the fundamental of these data structures in a simpler way as comp
    15+ min read
  • Introduction to Graph Data Structure
    Graph Data Structure is a non-linear data structure consisting of vertices and edges. It is useful in fields such as social network analysis, recommendation systems, and computer networks. In the field of sports data science, graph data structure can be used to analyze and understand the dynamics of
    15+ min read
  • What is Python? Its Uses and Applications
    Python is a programming language that is interpreted, object-oriented, and considered to be high-level. What is Python? Python is one of the easiest yet most useful programming languages and is widely used in the software industry. People use Python for Competitive Programming, Web Development, and
    8 min read
  • Linked List Data Structure
    A linked list is a fundamental data structure in computer science. It mainly allows efficient insertion and deletion operations compared to arrays. Like arrays, it is also used to implement other data structures like stack, queue and deque. Here’s the comparison of Linked List vs Arrays Linked List:
    3 min read
geeksforgeeks-footer-logo
Corporate & Communications Address:
A-143, 7th Floor, Sovereign Corporate Tower, Sector- 136, Noida, Uttar Pradesh (201305)
Registered Address:
K 061, Tower K, Gulshan Vivante Apartment, Sector 137, Noida, Gautam Buddh Nagar, Uttar Pradesh, 201305
GFG App on Play Store GFG App on App Store
Advertise with us
  • Company
  • About Us
  • Legal
  • Privacy Policy
  • In Media
  • Contact Us
  • Advertise with us
  • GFG Corporate Solution
  • Placement Training Program
  • Languages
  • Python
  • Java
  • C++
  • PHP
  • GoLang
  • SQL
  • R Language
  • Android Tutorial
  • Tutorials Archive
  • DSA
  • Data Structures
  • Algorithms
  • DSA for Beginners
  • Basic DSA Problems
  • DSA Roadmap
  • Top 100 DSA Interview Problems
  • DSA Roadmap by Sandeep Jain
  • All Cheat Sheets
  • Data Science & ML
  • Data Science With Python
  • Data Science For Beginner
  • Machine Learning
  • ML Maths
  • Data Visualisation
  • Pandas
  • NumPy
  • NLP
  • Deep Learning
  • Web Technologies
  • HTML
  • CSS
  • JavaScript
  • TypeScript
  • ReactJS
  • NextJS
  • Bootstrap
  • Web Design
  • Python Tutorial
  • Python Programming Examples
  • Python Projects
  • Python Tkinter
  • Python Web Scraping
  • OpenCV Tutorial
  • Python Interview Question
  • Django
  • Computer Science
  • Operating Systems
  • Computer Network
  • Database Management System
  • Software Engineering
  • Digital Logic Design
  • Engineering Maths
  • Software Development
  • Software Testing
  • DevOps
  • Git
  • Linux
  • AWS
  • Docker
  • Kubernetes
  • Azure
  • GCP
  • DevOps Roadmap
  • System Design
  • High Level Design
  • Low Level Design
  • UML Diagrams
  • Interview Guide
  • Design Patterns
  • OOAD
  • System Design Bootcamp
  • Interview Questions
  • Inteview Preparation
  • Competitive Programming
  • Top DS or Algo for CP
  • Company-Wise Recruitment Process
  • Company-Wise Preparation
  • Aptitude Preparation
  • Puzzles
  • School Subjects
  • Mathematics
  • Physics
  • Chemistry
  • Biology
  • Social Science
  • English Grammar
  • Commerce
  • World GK
  • GeeksforGeeks Videos
  • DSA
  • Python
  • Java
  • C++
  • Web Development
  • Data Science
  • CS Subjects
@GeeksforGeeks, Sanchhaya Education Private Limited, All rights reserved
We use cookies to ensure you have the best browsing experience on our website. By using our site, you acknowledge that you have read and understood our Cookie Policy & Privacy Policy
Lightbox
Improvement
Suggest Changes
Help us improve. Share your suggestions to enhance the article. Contribute your expertise and make a difference in the GeeksforGeeks portal.
geeksforgeeks-suggest-icon
Create Improvement
Enhance the article with your expertise. Contribute to the GeeksforGeeks community and help create better learning resources for all.
geeksforgeeks-improvement-icon
Suggest Changes
min 4 words, max Words Limit:1000

Thank You!

Your suggestions are valuable to us.

What kind of Experience do you want to share?

Interview Experiences
Admission Experiences
Career Journeys
Work Experiences
Campus Experiences
Competitive Exam Experiences